feat(core): use createProjectFileMappings in TargetProjectLocator (#13268)

This commit is contained in:
Miroslav Jonaš 2022-11-21 14:43:40 +01:00 committed by GitHub
parent 41c320cb07
commit bd4f4a23c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 90 additions and 64 deletions

View File

@ -280,7 +280,7 @@ function withSchemaDefaults(options: any): BrowserBuilderSchema {
*/ */
function getTempStylesForTailwind(ctExecutorContext: ExecutorContext) { function getTempStylesForTailwind(ctExecutorContext: ExecutorContext) {
const mappedGraphFiles = createProjectFileMappings( const mappedGraphFiles = createProjectFileMappings(
ctExecutorContext.projectGraph ctExecutorContext.projectGraph.nodes
); );
const ctProjectConfig = ctExecutorContext.projectGraph.nodes[ const ctProjectConfig = ctExecutorContext.projectGraph.nodes[
ctExecutorContext.projectName ctExecutorContext.projectName

View File

@ -90,7 +90,7 @@ export function getProjectConfigByPath(
: configFileFromWorkspaceRoot : configFileFromWorkspaceRoot
); );
const mappedGraphFiles = createProjectFileMappings(graph); const mappedGraphFiles = createProjectFileMappings(graph.nodes);
const componentTestingProjectName = const componentTestingProjectName =
mappedGraphFiles[normalizedPathFromWorkspaceRoot]; mappedGraphFiles[normalizedPathFromWorkspaceRoot];
if ( if (

View File

@ -1015,7 +1015,7 @@ Violation detected in:
tags: [], tags: [],
implicitDependencies: [], implicitDependencies: [],
architect: {}, architect: {},
files: [createFile(`libs/other/index.ts`)], files: [createFile(`libs/other/src/index.ts`)],
}, },
}, },
}, },
@ -1033,6 +1033,7 @@ Violation detected in:
if (importKind === 'type') { if (importKind === 'type') {
expect(failures.length).toEqual(0); expect(failures.length).toEqual(0);
} else { } else {
expect(failures.length).toEqual(1);
expect(failures[0].message).toEqual( expect(failures[0].message).toEqual(
'Imports of lazy-loaded libraries are forbidden' 'Imports of lazy-loaded libraries are forbidden'
); );
@ -1144,7 +1145,10 @@ Violation detected in:
tags: [], tags: [],
implicitDependencies: [], implicitDependencies: [],
architect: {}, architect: {},
files: [createFile(`libs/mylib/src/main.ts`)], files: [
createFile(`libs/mylib/src/main.ts`),
createFile(`libs/mylib/src/index.ts`),
],
}, },
}, },
anotherlibName: { anotherlibName: {
@ -1268,7 +1272,10 @@ Violation detected in:
tags: [], tags: [],
implicitDependencies: [], implicitDependencies: [],
architect: {}, architect: {},
files: [createFile(`libs/mylib/src/main.ts`, ['anotherlibName'])], files: [
createFile(`libs/mylib/src/main.ts`, ['anotherlibName']),
createFile(`libs/mylib/src/index.ts`),
],
}, },
}, },
anotherlibName: { anotherlibName: {
@ -1363,6 +1370,7 @@ Circular file chain:
architect: {}, architect: {},
files: [ files: [
createFile(`libs/badcirclelib/src/main.ts`, ['anotherlibName']), createFile(`libs/badcirclelib/src/main.ts`, ['anotherlibName']),
createFile(`libs/badcirclelib/src/index.ts`),
], ],
}, },
}, },
@ -1886,8 +1894,9 @@ function runRule(
): TSESLint.Linter.LintMessage[] { ): TSESLint.Linter.LintMessage[] {
(global as any).projectPath = `${process.cwd()}/proj`; (global as any).projectPath = `${process.cwd()}/proj`;
(global as any).projectGraph = projectGraph; (global as any).projectGraph = projectGraph;
(global as any).projectGraphFileMappings = (global as any).projectGraphFileMappings = createProjectFileMappings(
createProjectFileMappings(projectGraph); projectGraph.nodes
);
(global as any).targetProjectLocator = new TargetProjectLocator( (global as any).targetProjectLocator = new TargetProjectLocator(
projectGraph.nodes, projectGraph.nodes,
projectGraph.externalNodes projectGraph.externalNodes

View File

@ -23,7 +23,7 @@ export function ensureGlobalProjectGraph(ruleName: string) {
try { try {
(global as any).projectGraph = readCachedProjectGraph(); (global as any).projectGraph = readCachedProjectGraph();
(global as any).projectGraphFileMappings = createProjectFileMappings( (global as any).projectGraphFileMappings = createProjectFileMappings(
(global as any).projectGraph (global as any).projectGraph.nodes
); );
} catch { } catch {
const WARNING_PREFIX = `${chalk.reset.keyword('orange')('warning')}`; const WARNING_PREFIX = `${chalk.reset.keyword('orange')('warning')}`;

View File

@ -22,9 +22,9 @@ describe('getTouchedProjects', () => {
it('should return a list of projects for the given changes', () => { it('should return a list of projects for the given changes', () => {
const fileChanges = getFileChanges(['libs/a/index.ts', 'libs/b/index.ts']); const fileChanges = getFileChanges(['libs/a/index.ts', 'libs/b/index.ts']);
const projects = { const projects = {
a: { root: 'libs/a' }, a: { root: 'libs/a', files: [{ file: 'libs/a/index.ts' }] },
b: { root: 'libs/b' }, b: { root: 'libs/b', files: [{ file: 'libs/b/index.ts' }] },
c: { root: 'libs/c' }, c: { root: 'libs/c', files: [{ file: 'libs/c/index.ts' }] },
}; };
expect( expect(
getTouchedProjects(fileChanges, buildProjectGraphNodes(projects)) 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', () => { 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 fileChanges = getFileChanges(['libs/a-b/index.ts']);
const projects = { const projects = {
a: { root: 'libs/a' }, a: { root: 'libs/a', files: [{ file: 'libs/a/index.ts' }] },
abc: { root: 'libs/a-b-c' }, abc: { root: 'libs/a-b-c', files: [{ file: 'libs/a-b-c/index.ts' }] },
ab: { root: 'libs/a-b' }, ab: { root: 'libs/a-b', files: [{ file: 'libs/a-b/index.ts' }] },
}; };
expect( expect(
getTouchedProjects(fileChanges, buildProjectGraphNodes(projects)) 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', () => { 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 fileChanges = getFileChanges(['libs/a-b/index.ts']);
const projects = { const projects = {
aaaaa: { root: 'libs/a' }, aaaaa: { root: 'libs/a', files: [{ file: 'libs/a/index.ts' }] },
abc: { root: 'libs/a-b-c' }, abc: { root: 'libs/a-b-c', files: [{ file: 'libs/a-b-c/index.ts' }] },
ab: { root: 'libs/a-b' }, ab: { root: 'libs/a-b', files: [{ file: 'libs/a-b/index.ts' }] },
}; };
expect( expect(
getTouchedProjects(fileChanges, buildProjectGraphNodes(projects)) getTouchedProjects(fileChanges, buildProjectGraphNodes(projects))
@ -58,8 +58,8 @@ describe('getTouchedProjects', () => {
it('should return the most qualifying match with the file path', () => { it('should return the most qualifying match with the file path', () => {
const fileChanges = getFileChanges(['libs/a/b/index.ts']); const fileChanges = getFileChanges(['libs/a/b/index.ts']);
const projects = { const projects = {
aaaaa: { root: 'libs/a' }, aaaaa: { root: 'libs/a', files: [{ file: 'libs/a/index.ts' }] },
ab: { root: 'libs/a/b' }, ab: { root: 'libs/a/b', files: [{ file: 'libs/a/b/index.ts' }] },
}; };
expect( expect(
getTouchedProjects(fileChanges, buildProjectGraphNodes(projects)) getTouchedProjects(fileChanges, buildProjectGraphNodes(projects))
@ -69,8 +69,8 @@ describe('getTouchedProjects', () => {
it('should not return parent project if nested project is touched', () => { it('should not return parent project if nested project is touched', () => {
const fileChanges = getFileChanges(['libs/a/b/index.ts']); const fileChanges = getFileChanges(['libs/a/b/index.ts']);
const projects = { const projects = {
a: { root: 'libs/a' }, a: { root: 'libs/a', files: [{ file: 'libs/a/index.ts' }] },
b: { root: 'libs/a/b' }, b: { root: 'libs/a/b', files: [{ file: 'libs/a/b/index.ts' }] },
}; };
expect( expect(
getTouchedProjects(fileChanges, buildProjectGraphNodes(projects)) getTouchedProjects(fileChanges, buildProjectGraphNodes(projects))

View File

@ -2,24 +2,23 @@ import * as minimatch from 'minimatch';
import { TouchedProjectLocator } from '../affected-project-graph-models'; import { TouchedProjectLocator } from '../affected-project-graph-models';
import { NxJsonConfiguration } from '../../../config/nx-json'; import { NxJsonConfiguration } from '../../../config/nx-json';
import { ProjectGraphProjectNode } from '../../../config/project-graph'; import { ProjectGraphProjectNode } from '../../../config/project-graph';
import { import { createProjectFileMappings } from '../../../utils/target-project-locator';
createProjectRootMappings,
findMatchingProjectForPath,
} from '../../../utils/target-project-locator';
export const getTouchedProjects: TouchedProjectLocator = ( export const getTouchedProjects: TouchedProjectLocator = (
touchedFiles, touchedFiles,
projectGraphNodes projectGraphNodes
): string[] => { ): string[] => {
const projectRootMap = createProjectRootMappings(projectGraphNodes); const allProjectFiles = createProjectFileMappings(projectGraphNodes);
const affected = [];
return touchedFiles.reduce((affected, f) => { touchedFiles.forEach((f) => {
const matchingProject = findMatchingProjectForPath(f.file, projectRootMap); const matchingProject = allProjectFiles[f.file];
if (matchingProject) { if (matchingProject) {
affected.push(matchingProject); affected.push(matchingProject);
} }
});
return affected; return affected;
}, []);
}; };
export const getImplicitlyTouchedProjects: TouchedProjectLocator = ( export const getImplicitlyTouchedProjects: TouchedProjectLocator = (

View File

@ -8,7 +8,6 @@ import { workspaceRoot } from './workspace-root';
import { readJsonFile } from '../utils/fileutils'; import { readJsonFile } from '../utils/fileutils';
import { import {
PackageJson, PackageJson,
readModulePackageJson,
readModulePackageJsonWithoutFallbacks, readModulePackageJsonWithoutFallbacks,
} from './package-json'; } from './package-json';
import { registerTsProject } from './register'; import { registerTsProject } from './register';

View File

@ -80,6 +80,10 @@ describe('findTargetProjectWithImport', () => {
file: 'libs/proj/index.ts', file: 'libs/proj/index.ts',
hash: 'some-hash', hash: 'some-hash',
}, },
{
file: 'libs/proj/class.ts',
hash: 'some-hash',
},
], ],
proj2: [ proj2: [
{ {
@ -152,7 +156,7 @@ describe('findTargetProjectWithImport', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj3a', root: 'libs/proj3a',
files: [], files: ctx.fileMap['proj3a'],
}, },
}, },
proj2: { proj2: {
@ -160,7 +164,7 @@ describe('findTargetProjectWithImport', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj2', root: 'libs/proj2',
files: [], files: ctx.fileMap['proj2'],
}, },
}, },
proj: { proj: {
@ -168,7 +172,7 @@ describe('findTargetProjectWithImport', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj', root: 'libs/proj',
files: [], files: ctx.fileMap['proj'],
}, },
}, },
proj1234: { proj1234: {
@ -176,7 +180,7 @@ describe('findTargetProjectWithImport', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj1234', root: 'libs/proj1234',
files: [], files: ctx.fileMap['proj1234'],
}, },
}, },
proj123: { proj123: {
@ -184,7 +188,7 @@ describe('findTargetProjectWithImport', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj123', root: 'libs/proj123',
files: [], files: ctx.fileMap['proj123'],
}, },
}, },
proj4ab: { proj4ab: {
@ -192,7 +196,7 @@ describe('findTargetProjectWithImport', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj4ab', root: 'libs/proj4ab',
files: [], files: ctx.fileMap['proj4ab'],
}, },
}, },
proj5: { proj5: {
@ -200,7 +204,7 @@ describe('findTargetProjectWithImport', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj5', root: 'libs/proj5',
files: [], files: ctx.fileMap['proj5'],
}, },
}, },
proj6: { proj6: {
@ -208,7 +212,7 @@ describe('findTargetProjectWithImport', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj6', root: 'libs/proj6',
files: [], files: ctx.fileMap['proj6'],
}, },
}, },
proj7: { proj7: {
@ -216,7 +220,7 @@ describe('findTargetProjectWithImport', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj7', root: 'libs/proj7',
files: [], files: ctx.fileMap['proj7'],
}, },
}, },
'proj1234-child': { 'proj1234-child': {
@ -224,7 +228,7 @@ describe('findTargetProjectWithImport', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj1234-child', root: 'libs/proj1234-child',
files: [], files: ctx.fileMap['proj1234-child'],
}, },
}, },
}; };
@ -508,6 +512,10 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
file: 'libs/proj/index.ts', file: 'libs/proj/index.ts',
hash: 'some-hash', hash: 'some-hash',
}, },
{
file: 'libs/proj/class.ts',
hash: 'some-hash',
},
], ],
proj2: [ proj2: [
{ {
@ -580,7 +588,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj3a', root: 'libs/proj3a',
files: [], files: ctx.fileMap.proj3a,
}, },
}, },
proj2: { proj2: {
@ -588,7 +596,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj2', root: 'libs/proj2',
files: [], files: ctx.fileMap.proj2,
}, },
}, },
proj: { proj: {
@ -596,7 +604,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj', root: 'libs/proj',
files: [], files: ctx.fileMap.proj,
}, },
}, },
proj1234: { proj1234: {
@ -604,7 +612,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj1234', root: 'libs/proj1234',
files: [], files: ctx.fileMap.proj1234,
}, },
}, },
proj123: { proj123: {
@ -612,7 +620,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj123', root: 'libs/proj123',
files: [], files: ctx.fileMap.proj123,
}, },
}, },
proj4ab: { proj4ab: {
@ -620,7 +628,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj4ab', root: 'libs/proj4ab',
files: [], files: ctx.fileMap.proj4ab,
}, },
}, },
proj5: { proj5: {
@ -628,7 +636,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj5', root: 'libs/proj5',
files: [], files: ctx.fileMap.proj5,
}, },
}, },
proj6: { proj6: {
@ -636,7 +644,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj6', root: 'libs/proj6',
files: [], files: ctx.fileMap.proj6,
}, },
}, },
proj7: { proj7: {
@ -644,7 +652,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj7', root: 'libs/proj7',
files: [], files: ctx.fileMap.proj7,
}, },
}, },
'proj1234-child': { 'proj1234-child': {
@ -652,7 +660,7 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
type: 'lib', type: 'lib',
data: { data: {
root: 'libs/proj1234-child', root: 'libs/proj1234-child',
files: [], files: ctx.fileMap['proj1234-child'],
}, },
}, },
}; };

View File

@ -3,13 +3,12 @@ import { isRelativePath, readJsonFile } from './fileutils';
import { dirname, join, posix } from 'path'; import { dirname, join, posix } from 'path';
import { workspaceRoot } from './workspace-root'; import { workspaceRoot } from './workspace-root';
import { import {
ProjectGraph,
ProjectGraphExternalNode, ProjectGraphExternalNode,
ProjectGraphProjectNode, ProjectGraphProjectNode,
} from '../config/project-graph'; } from '../config/project-graph';
export class TargetProjectLocator { export class TargetProjectLocator {
private projectRootMappings = createProjectRootMappings(this.nodes); private allProjectsFiles = createProjectFileMappings(this.nodes);
private npmProjects = filterRootExternalDependencies(this.externalNodes); private npmProjects = filterRootExternalDependencies(this.externalNodes);
private tsConfig = this.getRootTsConfig(); private tsConfig = this.getRootTsConfig();
private paths = this.tsConfig.config?.compilerOptions?.paths; private paths = this.tsConfig.config?.compilerOptions?.paths;
@ -146,10 +145,19 @@ export class TargetProjectLocator {
const normalizedResolvedModule = resolvedModule.startsWith('./') const normalizedResolvedModule = resolvedModule.startsWith('./')
? resolvedModule.substring(2) ? resolvedModule.substring(2)
: resolvedModule; : 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) { private getAbsolutePath(path: string) {
@ -175,8 +183,8 @@ export class TargetProjectLocator {
} }
private findMatchingProjectFiles(file: string) { private findMatchingProjectFiles(file: string) {
const project = findMatchingProjectForPath(file, this.projectRootMappings); const project = this.allProjectsFiles[file];
return this.nodes[project]; 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 * Mapps the project root paths to the project name
* @param nodes * @param nodes
* @returns * @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 * Maps the file paths to the project name, both with and without the file extension
* based on the file path. * apps/myapp/src/main.ts -> { 'apps/myapp/src/main': 'myapp', 'apps/myapp/src/main.ts': 'myapp' }
* @param projectGraph * @param projectGraph
* @returns * @returns
*/ */
export function createProjectFileMappings( export function createProjectFileMappings(
projectGraph: ProjectGraph nodes: Record<string, ProjectGraphProjectNode>
): Record<string, string> { ): Record<string, string> {
const result: Record<string, string> = {}; const result: Record<string, string> = {};
Object.entries( Object.entries(nodes).forEach(([name, node]) => {
projectGraph.nodes as Record<string, ProjectGraphProjectNode>
).forEach(([name, node]) => {
node.data.files.forEach(({ file }) => { node.data.files.forEach(({ file }) => {
const fileName = removeExt(file); const fileName = removeExt(file);
result[fileName] = name; 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 * Locates a project in projectRootMap based on a file within it
* @param filePath path that is inside of projectName * @param filePath path that is inside of projectName
* @param projectRootMap Map<projectRoot, projectName> * @param projectRootMap Map<projectRoot, projectName>