From bd4f4a23c20c05b25e2bf8bd2ef6de80cae3fba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20Jona=C5=A1?= Date: Mon, 21 Nov 2022 14:43:40 +0100 Subject: [PATCH] feat(core): use createProjectFileMappings in TargetProjectLocator (#13268) --- packages/angular/plugins/component-testing.ts | 2 +- packages/cypress/plugins/cypress-preset.ts | 2 +- .../rules/enforce-module-boundaries.spec.ts | 19 ++++++-- .../src/utils/project-graph-utils.ts | 2 +- .../locators/workspace-projects.spec.ts | 26 +++++----- .../affected/locators/workspace-projects.ts | 17 ++++--- packages/nx/src/utils/nx-plugin.ts | 1 - .../src/utils/target-project-locator.spec.ts | 48 +++++++++++-------- .../nx/src/utils/target-project-locator.ts | 37 +++++++++----- 9 files changed, 90 insertions(+), 64 deletions(-) diff --git a/packages/angular/plugins/component-testing.ts b/packages/angular/plugins/component-testing.ts index 977ebe2c3d..d949c1a90c 100644 --- a/packages/angular/plugins/component-testing.ts +++ b/packages/angular/plugins/component-testing.ts @@ -280,7 +280,7 @@ function withSchemaDefaults(options: any): BrowserBuilderSchema { */ function getTempStylesForTailwind(ctExecutorContext: ExecutorContext) { const mappedGraphFiles = createProjectFileMappings( - ctExecutorContext.projectGraph + ctExecutorContext.projectGraph.nodes ); const ctProjectConfig = ctExecutorContext.projectGraph.nodes[ ctExecutorContext.projectName diff --git a/packages/cypress/plugins/cypress-preset.ts b/packages/cypress/plugins/cypress-preset.ts index d5a44a031e..578e688c5e 100644 --- a/packages/cypress/plugins/cypress-preset.ts +++ b/packages/cypress/plugins/cypress-preset.ts @@ -90,7 +90,7 @@ export function getProjectConfigByPath( : configFileFromWorkspaceRoot ); - const mappedGraphFiles = createProjectFileMappings(graph); + const mappedGraphFiles = createProjectFileMappings(graph.nodes); const componentTestingProjectName = mappedGraphFiles[normalizedPathFromWorkspaceRoot]; if ( diff --git a/packages/eslint-plugin-nx/src/rules/enforce-module-boundaries.spec.ts b/packages/eslint-plugin-nx/src/rules/enforce-module-boundaries.spec.ts index 190009b98d..56ee3ea47c 100644 --- a/packages/eslint-plugin-nx/src/rules/enforce-module-boundaries.spec.ts +++ b/packages/eslint-plugin-nx/src/rules/enforce-module-boundaries.spec.ts @@ -1015,7 +1015,7 @@ Violation detected in: tags: [], implicitDependencies: [], architect: {}, - files: [createFile(`libs/other/index.ts`)], + files: [createFile(`libs/other/src/index.ts`)], }, }, }, @@ -1033,6 +1033,7 @@ Violation detected in: if (importKind === 'type') { expect(failures.length).toEqual(0); } else { + expect(failures.length).toEqual(1); expect(failures[0].message).toEqual( 'Imports of lazy-loaded libraries are forbidden' ); @@ -1144,7 +1145,10 @@ Violation detected in: tags: [], implicitDependencies: [], architect: {}, - files: [createFile(`libs/mylib/src/main.ts`)], + files: [ + createFile(`libs/mylib/src/main.ts`), + createFile(`libs/mylib/src/index.ts`), + ], }, }, anotherlibName: { @@ -1268,7 +1272,10 @@ Violation detected in: tags: [], implicitDependencies: [], architect: {}, - files: [createFile(`libs/mylib/src/main.ts`, ['anotherlibName'])], + files: [ + createFile(`libs/mylib/src/main.ts`, ['anotherlibName']), + createFile(`libs/mylib/src/index.ts`), + ], }, }, anotherlibName: { @@ -1363,6 +1370,7 @@ Circular file chain: architect: {}, files: [ createFile(`libs/badcirclelib/src/main.ts`, ['anotherlibName']), + createFile(`libs/badcirclelib/src/index.ts`), ], }, }, @@ -1886,8 +1894,9 @@ function runRule( ): TSESLint.Linter.LintMessage[] { (global as any).projectPath = `${process.cwd()}/proj`; (global as any).projectGraph = projectGraph; - (global as any).projectGraphFileMappings = - createProjectFileMappings(projectGraph); + (global as any).projectGraphFileMappings = createProjectFileMappings( + projectGraph.nodes + ); (global as any).targetProjectLocator = new TargetProjectLocator( projectGraph.nodes, projectGraph.externalNodes diff --git a/packages/eslint-plugin-nx/src/utils/project-graph-utils.ts b/packages/eslint-plugin-nx/src/utils/project-graph-utils.ts index 37bd244bc9..1a5956435d 100644 --- a/packages/eslint-plugin-nx/src/utils/project-graph-utils.ts +++ b/packages/eslint-plugin-nx/src/utils/project-graph-utils.ts @@ -23,7 +23,7 @@ export function ensureGlobalProjectGraph(ruleName: string) { try { (global as any).projectGraph = readCachedProjectGraph(); (global as any).projectGraphFileMappings = createProjectFileMappings( - (global as any).projectGraph + (global as any).projectGraph.nodes ); } catch { const WARNING_PREFIX = `${chalk.reset.keyword('orange')('warning')}`; diff --git a/packages/nx/src/project-graph/affected/locators/workspace-projects.spec.ts b/packages/nx/src/project-graph/affected/locators/workspace-projects.spec.ts index d110d06870..05c417b74f 100644 --- a/packages/nx/src/project-graph/affected/locators/workspace-projects.spec.ts +++ b/packages/nx/src/project-graph/affected/locators/workspace-projects.spec.ts @@ -22,9 +22,9 @@ describe('getTouchedProjects', () => { it('should return a list of projects for the given changes', () => { const fileChanges = getFileChanges(['libs/a/index.ts', 'libs/b/index.ts']); const projects = { - a: { root: 'libs/a' }, - b: { root: 'libs/b' }, - c: { root: 'libs/c' }, + a: { root: 'libs/a', files: [{ file: 'libs/a/index.ts' }] }, + b: { root: 'libs/b', files: [{ file: 'libs/b/index.ts' }] }, + c: { root: 'libs/c', files: [{ file: 'libs/c/index.ts' }] }, }; expect( getTouchedProjects(fileChanges, buildProjectGraphNodes(projects)) @@ -34,9 +34,9 @@ describe('getTouchedProjects', () => { it('should return projects with the root matching a whole directory name in the file path', () => { const fileChanges = getFileChanges(['libs/a-b/index.ts']); const projects = { - a: { root: 'libs/a' }, - abc: { root: 'libs/a-b-c' }, - ab: { root: 'libs/a-b' }, + a: { root: 'libs/a', files: [{ file: 'libs/a/index.ts' }] }, + abc: { root: 'libs/a-b-c', files: [{ file: 'libs/a-b-c/index.ts' }] }, + ab: { root: 'libs/a-b', files: [{ file: 'libs/a-b/index.ts' }] }, }; expect( getTouchedProjects(fileChanges, buildProjectGraphNodes(projects)) @@ -46,9 +46,9 @@ describe('getTouchedProjects', () => { it('should return projects with the root matching a whole directory name in the file path', () => { const fileChanges = getFileChanges(['libs/a-b/index.ts']); const projects = { - aaaaa: { root: 'libs/a' }, - abc: { root: 'libs/a-b-c' }, - ab: { root: 'libs/a-b' }, + aaaaa: { root: 'libs/a', files: [{ file: 'libs/a/index.ts' }] }, + abc: { root: 'libs/a-b-c', files: [{ file: 'libs/a-b-c/index.ts' }] }, + ab: { root: 'libs/a-b', files: [{ file: 'libs/a-b/index.ts' }] }, }; expect( getTouchedProjects(fileChanges, buildProjectGraphNodes(projects)) @@ -58,8 +58,8 @@ describe('getTouchedProjects', () => { it('should return the most qualifying match with the file path', () => { const fileChanges = getFileChanges(['libs/a/b/index.ts']); const projects = { - aaaaa: { root: 'libs/a' }, - ab: { root: 'libs/a/b' }, + aaaaa: { root: 'libs/a', files: [{ file: 'libs/a/index.ts' }] }, + ab: { root: 'libs/a/b', files: [{ file: 'libs/a/b/index.ts' }] }, }; expect( getTouchedProjects(fileChanges, buildProjectGraphNodes(projects)) @@ -69,8 +69,8 @@ describe('getTouchedProjects', () => { it('should not return parent project if nested project is touched', () => { const fileChanges = getFileChanges(['libs/a/b/index.ts']); const projects = { - a: { root: 'libs/a' }, - b: { root: 'libs/a/b' }, + a: { root: 'libs/a', files: [{ file: 'libs/a/index.ts' }] }, + b: { root: 'libs/a/b', files: [{ file: 'libs/a/b/index.ts' }] }, }; expect( getTouchedProjects(fileChanges, buildProjectGraphNodes(projects)) diff --git a/packages/nx/src/project-graph/affected/locators/workspace-projects.ts b/packages/nx/src/project-graph/affected/locators/workspace-projects.ts index 27ea2762fa..0b4dd5de46 100644 --- a/packages/nx/src/project-graph/affected/locators/workspace-projects.ts +++ b/packages/nx/src/project-graph/affected/locators/workspace-projects.ts @@ -2,24 +2,23 @@ import * as minimatch from 'minimatch'; import { TouchedProjectLocator } from '../affected-project-graph-models'; import { NxJsonConfiguration } from '../../../config/nx-json'; import { ProjectGraphProjectNode } from '../../../config/project-graph'; -import { - createProjectRootMappings, - findMatchingProjectForPath, -} from '../../../utils/target-project-locator'; +import { createProjectFileMappings } from '../../../utils/target-project-locator'; export const getTouchedProjects: TouchedProjectLocator = ( touchedFiles, projectGraphNodes ): string[] => { - const projectRootMap = createProjectRootMappings(projectGraphNodes); + const allProjectFiles = createProjectFileMappings(projectGraphNodes); + const affected = []; - return touchedFiles.reduce((affected, f) => { - const matchingProject = findMatchingProjectForPath(f.file, projectRootMap); + touchedFiles.forEach((f) => { + const matchingProject = allProjectFiles[f.file]; if (matchingProject) { affected.push(matchingProject); } - return affected; - }, []); + }); + + return affected; }; export const getImplicitlyTouchedProjects: TouchedProjectLocator = ( diff --git a/packages/nx/src/utils/nx-plugin.ts b/packages/nx/src/utils/nx-plugin.ts index dcd8b66ce1..fd054e38d0 100644 --- a/packages/nx/src/utils/nx-plugin.ts +++ b/packages/nx/src/utils/nx-plugin.ts @@ -8,7 +8,6 @@ import { workspaceRoot } from './workspace-root'; import { readJsonFile } from '../utils/fileutils'; import { PackageJson, - readModulePackageJson, readModulePackageJsonWithoutFallbacks, } from './package-json'; import { registerTsProject } from './register'; diff --git a/packages/nx/src/utils/target-project-locator.spec.ts b/packages/nx/src/utils/target-project-locator.spec.ts index cadf8622bd..e660f442bd 100644 --- a/packages/nx/src/utils/target-project-locator.spec.ts +++ b/packages/nx/src/utils/target-project-locator.spec.ts @@ -80,6 +80,10 @@ describe('findTargetProjectWithImport', () => { file: 'libs/proj/index.ts', hash: 'some-hash', }, + { + file: 'libs/proj/class.ts', + hash: 'some-hash', + }, ], proj2: [ { @@ -152,7 +156,7 @@ describe('findTargetProjectWithImport', () => { type: 'lib', data: { root: 'libs/proj3a', - files: [], + files: ctx.fileMap['proj3a'], }, }, proj2: { @@ -160,7 +164,7 @@ describe('findTargetProjectWithImport', () => { type: 'lib', data: { root: 'libs/proj2', - files: [], + files: ctx.fileMap['proj2'], }, }, proj: { @@ -168,7 +172,7 @@ describe('findTargetProjectWithImport', () => { type: 'lib', data: { root: 'libs/proj', - files: [], + files: ctx.fileMap['proj'], }, }, proj1234: { @@ -176,7 +180,7 @@ describe('findTargetProjectWithImport', () => { type: 'lib', data: { root: 'libs/proj1234', - files: [], + files: ctx.fileMap['proj1234'], }, }, proj123: { @@ -184,7 +188,7 @@ describe('findTargetProjectWithImport', () => { type: 'lib', data: { root: 'libs/proj123', - files: [], + files: ctx.fileMap['proj123'], }, }, proj4ab: { @@ -192,7 +196,7 @@ describe('findTargetProjectWithImport', () => { type: 'lib', data: { root: 'libs/proj4ab', - files: [], + files: ctx.fileMap['proj4ab'], }, }, proj5: { @@ -200,7 +204,7 @@ describe('findTargetProjectWithImport', () => { type: 'lib', data: { root: 'libs/proj5', - files: [], + files: ctx.fileMap['proj5'], }, }, proj6: { @@ -208,7 +212,7 @@ describe('findTargetProjectWithImport', () => { type: 'lib', data: { root: 'libs/proj6', - files: [], + files: ctx.fileMap['proj6'], }, }, proj7: { @@ -216,7 +220,7 @@ describe('findTargetProjectWithImport', () => { type: 'lib', data: { root: 'libs/proj7', - files: [], + files: ctx.fileMap['proj7'], }, }, 'proj1234-child': { @@ -224,7 +228,7 @@ describe('findTargetProjectWithImport', () => { type: 'lib', data: { root: 'libs/proj1234-child', - files: [], + files: ctx.fileMap['proj1234-child'], }, }, }; @@ -508,6 +512,10 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => { file: 'libs/proj/index.ts', hash: 'some-hash', }, + { + file: 'libs/proj/class.ts', + hash: 'some-hash', + }, ], proj2: [ { @@ -580,7 +588,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => { type: 'lib', data: { root: 'libs/proj3a', - files: [], + files: ctx.fileMap.proj3a, }, }, proj2: { @@ -588,7 +596,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => { type: 'lib', data: { root: 'libs/proj2', - files: [], + files: ctx.fileMap.proj2, }, }, proj: { @@ -596,7 +604,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => { type: 'lib', data: { root: 'libs/proj', - files: [], + files: ctx.fileMap.proj, }, }, proj1234: { @@ -604,7 +612,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => { type: 'lib', data: { root: 'libs/proj1234', - files: [], + files: ctx.fileMap.proj1234, }, }, proj123: { @@ -612,7 +620,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => { type: 'lib', data: { root: 'libs/proj123', - files: [], + files: ctx.fileMap.proj123, }, }, proj4ab: { @@ -620,7 +628,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => { type: 'lib', data: { root: 'libs/proj4ab', - files: [], + files: ctx.fileMap.proj4ab, }, }, proj5: { @@ -628,7 +636,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => { type: 'lib', data: { root: 'libs/proj5', - files: [], + files: ctx.fileMap.proj5, }, }, proj6: { @@ -636,7 +644,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => { type: 'lib', data: { root: 'libs/proj6', - files: [], + files: ctx.fileMap.proj6, }, }, proj7: { @@ -644,7 +652,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => { type: 'lib', data: { root: 'libs/proj7', - files: [], + files: ctx.fileMap.proj7, }, }, 'proj1234-child': { @@ -652,7 +660,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => { type: 'lib', data: { root: 'libs/proj1234-child', - files: [], + files: ctx.fileMap['proj1234-child'], }, }, }; diff --git a/packages/nx/src/utils/target-project-locator.ts b/packages/nx/src/utils/target-project-locator.ts index fb365c8572..784aeeabb9 100644 --- a/packages/nx/src/utils/target-project-locator.ts +++ b/packages/nx/src/utils/target-project-locator.ts @@ -3,13 +3,12 @@ import { isRelativePath, readJsonFile } from './fileutils'; import { dirname, join, posix } from 'path'; import { workspaceRoot } from './workspace-root'; import { - ProjectGraph, ProjectGraphExternalNode, ProjectGraphProjectNode, } from '../config/project-graph'; export class TargetProjectLocator { - private projectRootMappings = createProjectRootMappings(this.nodes); + private allProjectsFiles = createProjectFileMappings(this.nodes); private npmProjects = filterRootExternalDependencies(this.externalNodes); private tsConfig = this.getRootTsConfig(); private paths = this.tsConfig.config?.compilerOptions?.paths; @@ -146,10 +145,19 @@ export class TargetProjectLocator { const normalizedResolvedModule = resolvedModule.startsWith('./') ? resolvedModule.substring(2) : resolvedModule; - const importedProject = this.findMatchingProjectFiles( - normalizedResolvedModule + + // for wildcard paths, we need to find file that starts with resolvedModule + if (normalizedResolvedModule.endsWith('*')) { + const matchingFile = Object.keys(this.allProjectsFiles).find((f) => + f.startsWith(normalizedResolvedModule.slice(0, -1)) + ); + return matchingFile && this.allProjectsFiles[matchingFile]; + } + + return ( + this.allProjectsFiles[normalizedResolvedModule] || + this.allProjectsFiles[`${normalizedResolvedModule}/index`] ); - return importedProject ? importedProject.name : void 0; } private getAbsolutePath(path: string) { @@ -175,8 +183,8 @@ export class TargetProjectLocator { } private findMatchingProjectFiles(file: string) { - const project = findMatchingProjectForPath(file, this.projectRootMappings); - return this.nodes[project]; + const project = this.allProjectsFiles[file]; + return project && this.nodes[project]; } } @@ -200,6 +208,8 @@ function filterRootExternalDependencies( } /** + * @deprecated This function will be removed in v16. Use {@link createProjectFileMappings} instead. + * * Mapps the project root paths to the project name * @param nodes * @returns @@ -228,21 +238,20 @@ export function removeExt(file: string): string { } /** - * Maps the project graph to a format that makes it easier to find the project - * based on the file path. + * Maps the file paths to the project name, both with and without the file extension + * apps/myapp/src/main.ts -> { 'apps/myapp/src/main': 'myapp', 'apps/myapp/src/main.ts': 'myapp' } * @param projectGraph * @returns */ export function createProjectFileMappings( - projectGraph: ProjectGraph + nodes: Record ): Record { const result: Record = {}; - Object.entries( - projectGraph.nodes as Record - ).forEach(([name, node]) => { + Object.entries(nodes).forEach(([name, node]) => { node.data.files.forEach(({ file }) => { const fileName = removeExt(file); result[fileName] = name; + result[file] = name; }); }); @@ -250,6 +259,8 @@ export function createProjectFileMappings( } /** + * @deprecated This function will be removed in v16. Use {@link createProjectFileMappings} instead. + * * Locates a project in projectRootMap based on a file within it * @param filePath path that is inside of projectName * @param projectRootMap Map