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) {
const mappedGraphFiles = createProjectFileMappings(
ctExecutorContext.projectGraph
ctExecutorContext.projectGraph.nodes
);
const ctProjectConfig = ctExecutorContext.projectGraph.nodes[
ctExecutorContext.projectName

View File

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

View File

@ -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

View File

@ -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')}`;

View File

@ -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))

View File

@ -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 = (

View File

@ -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';

View File

@ -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'],
},
},
};

View File

@ -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<string, ProjectGraphProjectNode>
): Record<string, string> {
const result: Record<string, string> = {};
Object.entries(
projectGraph.nodes as Record<string, ProjectGraphProjectNode>
).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<projectRoot, projectName>