Skip to content

Commit

Permalink
feat(devkit): add method for tree-aware glob search (#19128)
Browse files Browse the repository at this point in the history
  • Loading branch information
AgentEnder authored Sep 13, 2023
1 parent 188dc8f commit de2824a
Show file tree
Hide file tree
Showing 44 changed files with 196 additions and 61 deletions.
1 change: 1 addition & 0 deletions docs/generated/devkit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ It only uses language primitives and immutable objects
- [getProjects](../../devkit/documents/getProjects)
- [getWorkspaceLayout](../../devkit/documents/getWorkspaceLayout)
- [getWorkspacePath](../../devkit/documents/getWorkspacePath)
- [glob](../../devkit/documents/glob)
- [hashArray](../../devkit/documents/hashArray)
- [installPackagesTask](../../devkit/documents/installPackagesTask)
- [isStandaloneProject](../../devkit/documents/isStandaloneProject)
Expand Down
20 changes: 20 additions & 0 deletions docs/generated/devkit/glob.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Function: glob

**glob**(`tree`, `patterns`): `string`[]

Performs a tree-aware glob search on the files in a workspace. Able to find newly
created files and hides deleted files before the updates are committed to disk.
Paths should be unix-style with forward slashes.

#### Parameters

| Name | Type | Description |
| :--------- | :------------------------------------ | :---------------------- |
| `tree` | [`Tree`](../../devkit/documents/Tree) | The file system tree |
| `patterns` | `string`[] | A list of glob patterns |

#### Returns

`string`[]

Normalized paths in the workspace that match the provided glob patterns.
1 change: 1 addition & 0 deletions docs/generated/packages/devkit/documents/nx_devkit.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ It only uses language primitives and immutable objects
- [getProjects](../../devkit/documents/getProjects)
- [getWorkspaceLayout](../../devkit/documents/getWorkspaceLayout)
- [getWorkspacePath](../../devkit/documents/getWorkspacePath)
- [glob](../../devkit/documents/glob)
- [hashArray](../../devkit/documents/hashArray)
- [installPackagesTask](../../devkit/documents/installPackagesTask)
- [isStandaloneProject](../../devkit/documents/isStandaloneProject)
Expand Down
7 changes: 4 additions & 3 deletions packages/devkit/internal-testing-utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* eslint-disable @typescript-eslint/no-restricted-imports */
export * from 'nx/internal-testing-utils/assert-valid-migrations';
export * from 'nx/internal-testing-utils/run-migration-against-this-workspace';
export * from 'nx/internal-testing-utils/with-environment';
export * from 'nx/src/internal-testing-utils/assert-valid-migrations';
export * from 'nx/src/internal-testing-utils/run-migration-against-this-workspace';
export * from 'nx/src/internal-testing-utils/with-environment';
export * from 'nx/src/internal-testing-utils/temp-fs';
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { assertRunsAgainstNxRepo } from 'nx/internal-testing-utils/run-migration-against-this-workspace';
import { assertRunsAgainstNxRepo } from 'nx/src/internal-testing-utils/run-migration-against-this-workspace';
import { createTreeWithEmptyWorkspace } from 'nx/src/generators/testing-utils/create-tree-with-empty-workspace';
import { Tree } from 'nx/src/generators/tree';
import { readJson, updateJson } from 'nx/src/generators/utils/json';
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin/src/rules/dependency-checks.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'nx/src/utils/testing/mock-fs';
import 'nx/src/internal-testing-utils/mock-fs';

import dependencyChecks, {
Options,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'nx/src/utils/testing/mock-fs';
import 'nx/src/internal-testing-utils/mock-fs';

import type { FileData, ProjectFileMap, ProjectGraph } from '@nx/devkit';
import { DependencyType } from '@nx/devkit';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'nx/src/utils/testing/mock-fs';
import 'nx/src/internal-testing-utils/mock-fs';

import {
ProjectGraph,
Expand Down
2 changes: 1 addition & 1 deletion packages/js/src/utils/find-npm-dependencies.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'nx/src/utils/testing/mock-fs';
import 'nx/src/internal-testing-utils/mock-fs';
import { vol } from 'memfs';
import { findNpmDependencies } from './find-npm-dependencies';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'nx/src/utils/testing/mock-fs';
import 'nx/src/internal-testing-utils/mock-fs';

import {
getUpdatedPackageJsonContent,
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/migrations.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import json = require('./migrations.json');

import { assertValidMigrationPaths } from './internal-testing-utils/assert-valid-migrations';
import { assertValidMigrationPaths } from './src/internal-testing-utils/assert-valid-migrations';
import { MigrationsJson } from './src/config/misc-interfaces';

describe('nx migrations', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/nx/plugins/package-json-workspaces.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as memfs from 'memfs';

import '../src/utils/testing/mock-fs';
import '../src/internal-testing-utils/mock-fs';
import { createNodeFromPackageJson } from './package-json-workspaces';

describe('nx package.json workspaces plugin', () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/nx/src/config/workspaces.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { toProjectName, Workspaces } from './workspaces';
import { TempFs } from '../utils/testing/temp-fs';
import { withEnvironmentVariables } from '../../internal-testing-utils/with-environment';
import { TempFs } from '../internal-testing-utils/temp-fs';
import { withEnvironmentVariables } from '../internal-testing-utils/with-environment';
import { retrieveProjectConfigurations } from '../project-graph/utils/retrieve-workspace-files';
import { readNxJson } from './configuration';

Expand Down
10 changes: 9 additions & 1 deletion packages/nx/src/devkit-exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ export {
getProjects,
} from './generators/utils/project-configuration';

/**
* @category Generators
*/
export { glob } from './generators/utils/glob';

/**
* @category Generators
*/
Expand All @@ -127,6 +132,9 @@ export {
getWorkspacePath,
} from './generators/utils/deprecated';

/**
* @category Generators
*/
export {
readNxJson,
updateNxJson,
Expand Down Expand Up @@ -164,7 +172,7 @@ export {
} from './project-graph/project-graph-builder';

/**
* @category Utils
* @category Generators
*/
export { readJson, writeJson, updateJson } from './generators/utils/json';

Expand Down
68 changes: 68 additions & 0 deletions packages/nx/src/generators/utils/glob.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { TempFs } from '../../internal-testing-utils/temp-fs';
import { FsTree, Tree } from '../tree';
import { glob } from './glob';

describe('glob', () => {
let fs: TempFs;
let tree: Tree;

beforeEach(() => {
fs = new TempFs('glob', true);
tree = new FsTree(fs.tempDir, false);
});

afterEach(() => {
fs.cleanup();
});

it('should find files on disk', async () => {
fs.writeFile('1.txt', '1');
fs.writeFile('2.txt', '2');
fs.writeFile('3.txt', '3');
fs.writeFile('4.md', '4');

const results = glob(tree, ['*.txt']).sort();

expect(results).toMatchInlineSnapshot(`
[
"1.txt",
"2.txt",
"3.txt",
]
`);
});

it('should add files from tree', async () => {
fs.writeFile('1.txt', '1');
fs.writeFile('2.txt', '2');
tree.write('3.txt', '3');
fs.writeFile('4.md', '4');

const withTree = glob(tree, ['*.txt']).sort();

expect(withTree).toMatchInlineSnapshot(`
[
"1.txt",
"2.txt",
"3.txt",
]
`);
});

it('should hide files deleted on tree', async () => {
fs.writeFile('1.txt', '1');
fs.writeFile('2.txt', '2');
fs.writeFile('3.txt', '3');
tree.delete('3.txt');
fs.writeFile('4.md', '4');

const withTree = glob(tree, ['*.txt']).sort();

expect(withTree).toMatchInlineSnapshot(`
[
"1.txt",
"2.txt",
]
`);
});
});
38 changes: 38 additions & 0 deletions packages/nx/src/generators/utils/glob.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Tree } from '../tree';
import { combineGlobPatterns } from '../../utils/globs';
import { workspaceRoot } from '../../utils/workspace-root';
import { globWithWorkspaceContext } from '../../utils/workspace-context';

import minimatch = require('minimatch');

/**
* Performs a tree-aware glob search on the files in a workspace. Able to find newly
* created files and hides deleted files before the updates are committed to disk.
* Paths should be unix-style with forward slashes.
*
* @param tree The file system tree
* @param patterns A list of glob patterns
* @returns Normalized paths in the workspace that match the provided glob patterns.
*/
export function glob(tree: Tree, patterns: string[]): string[] {
const matches = new Set(globWithWorkspaceContext(tree.root, patterns));

const combinedGlob = combineGlobPatterns(patterns);
const matcher = minimatch.makeRe(combinedGlob);

if (!matcher) {
throw new Error('Invalid glob pattern: ' + combinedGlob);
}

for (const change of tree.listChanges()) {
if (change.type !== 'UPDATE' && matcher.test(change.path)) {
if (change.type === 'CREATE') {
matches.add(change.path);
} else if (change.type === 'DELETE') {
matches.delete(change.path);
}
}
}

return Array.from(matches);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ProjectConfiguration } from '../../config/workspace-json-project-json';

import { createTree } from '../testing-utils/create-tree';
import { createTreeWithEmptyWorkspace } from '../testing-utils/create-tree-with-empty-workspace';
import { readJson, updateJson, writeJson } from '../utils/json';
import { readJson, writeJson } from '../utils/json';
import {
addProjectConfiguration,
getProjects,
Expand Down
16 changes: 7 additions & 9 deletions packages/nx/src/hasher/task-hasher.spec.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
// This must come before the Hasher import
import { TempFs } from '../utils/testing/temp-fs';
import { TempFs } from '../internal-testing-utils/temp-fs';
let tempFs = new TempFs('TaskHasher');

import { DependencyType } from '../config/project-graph';
import {
expandNamedInput,
filterUsingGlobPatterns,
Hash,
InProcessTaskHasher,
} from './task-hasher';
import { withEnvironmentVariables } from '../../internal-testing-utils/with-environment';

jest.mock('../utils/workspace-root', () => {
return {
workspaceRoot: tempFs.tempDir,
};
});
import { withEnvironmentVariables } from '../internal-testing-utils/with-environment';

describe('TaskHasher', () => {
const packageJson = {
Expand Down Expand Up @@ -53,6 +47,10 @@ describe('TaskHasher', () => {
tempFs.reset();
});

afterAll(() => {
tempFs.cleanup();
});

it('should create task hash', () =>
withEnvironmentVariables({ TESTENV: 'env123' }, async () => {
const hasher = new InProcessTaskHasher(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import {
MigrationsJson,
MigrationsJsonEntry,
} from '../src/config/misc-interfaces';
import { MigrationsJson, MigrationsJsonEntry } from '../config/misc-interfaces';
import * as path from 'path';

export function assertValidMigrationPaths(json: MigrationsJson, root: string) {
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FsTree, Tree } from '../src/generators/tree';
import { FsTree, Tree } from '../generators/tree';
import { join } from 'path';

export function assertRunsAgainstNxRepo(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,25 @@ import {
rmSync,
unlinkSync,
} from 'fs-extra';
import { joinPathFragments } from '../path';
import { joinPathFragments } from '../utils/path';
import { appendFileSync, existsSync, renameSync, writeFileSync } from 'fs';
import { setWorkspaceRoot, workspaceRoot } from '../utils/workspace-root';

type NestedFiles = {
[fileName: string]: string;
};

export class TempFs {
readonly tempDir: string;

private previousWorkspaceRoot: string;

constructor(private dirname: string, overrideWorkspaceRoot = true) {
this.tempDir = realpathSync(mkdtempSync(join(tmpdir(), this.dirname)));
this.previousWorkspaceRoot = workspaceRoot;

if (overrideWorkspaceRoot) {
process.env.NX_WORKSPACE_ROOT_PATH = this.tempDir;
setWorkspaceRoot(this.tempDir);
}
}

Expand Down Expand Up @@ -76,6 +82,7 @@ export class TempFs {

cleanup() {
rmSync(this.tempDir, { recursive: true });
setWorkspaceRoot(this.previousWorkspaceRoot);
}

reset() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { createTreeWithEmptyWorkspace } from '../../generators/testing-utils/cre
import { addProjectConfiguration } from '../../generators/utils/project-configuration';
import { readJson, updateJson, writeJson } from '../../generators/utils/json';
import removeRoots from './remove-roots';
import { assertRunsAgainstNxRepo } from '../../../internal-testing-utils/run-migration-against-this-workspace';
import { assertRunsAgainstNxRepo } from '../../internal-testing-utils/run-migration-against-this-workspace';

describe('remove-roots >', () => {
let tree: Tree;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { assertRunsAgainstNxRepo } from '../../../internal-testing-utils/run-migration-against-this-workspace';
import { assertRunsAgainstNxRepo } from '../../internal-testing-utils/run-migration-against-this-workspace';
import { createTreeWithEmptyWorkspace } from '../../generators/testing-utils/create-tree-with-empty-workspace';
import type { Tree } from '../../generators/tree';
import { readJson, writeJson } from '../../generators/utils/json';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
readNxJson,
updateNxJson,
} from '../../generators/utils/project-configuration';
import { assertRunsAgainstNxRepo } from '../../../internal-testing-utils/run-migration-against-this-workspace';
import { assertRunsAgainstNxRepo } from '../../internal-testing-utils/run-migration-against-this-workspace';
import removeDefaultCollection from './remove-default-collection';

describe('remove-default-collection', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { assertRunsAgainstNxRepo } from '../../../internal-testing-utils/run-migration-against-this-workspace';
import { assertRunsAgainstNxRepo } from '../../internal-testing-utils/run-migration-against-this-workspace';
import { createTreeWithEmptyWorkspace } from '../../generators/testing-utils/create-tree-with-empty-workspace';
import type { Tree } from '../../generators/tree';
import {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { assertRunsAgainstNxRepo } from '../../../internal-testing-utils/run-migration-against-this-workspace';
import { assertRunsAgainstNxRepo } from '../../internal-testing-utils/run-migration-against-this-workspace';
import { createTreeWithEmptyWorkspace } from '../../generators/testing-utils/create-tree-with-empty-workspace';
import type { Tree } from '../../generators/tree';
import {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
import update from './update-depends-on-to-tokens';
import { updateJson, writeJson } from '../../generators/utils/json';
import { createTreeWithEmptyWorkspace } from '../../generators/testing-utils/create-tree-with-empty-workspace';
import { assertRunsAgainstNxRepo } from '../../../internal-testing-utils/run-migration-against-this-workspace';
import { assertRunsAgainstNxRepo } from '../../internal-testing-utils/run-migration-against-this-workspace';

describe('update-depends-on-to-tokens', () => {
it('should update nx.json', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
addProjectConfiguration,
readProjectConfiguration,
} from '../../generators/utils/project-configuration';
import { assertRunsAgainstNxRepo } from '../../../internal-testing-utils/run-migration-against-this-workspace';
import { assertRunsAgainstNxRepo } from '../../internal-testing-utils/run-migration-against-this-workspace';
import removeRunCommandsOutputPath from './remove-run-commands-output-path';

describe('removeRunCommandsOutputPath', () => {
Expand Down
Loading

1 comment on commit de2824a

@vercel
Copy link

@vercel vercel bot commented on de2824a Sep 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nx-dev – ./

nx-dev-git-master-nrwl.vercel.app
nx-five.vercel.app
nx-dev-nrwl.vercel.app
nx.dev

Please sign in to comment.