Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 46454aa

Browse files
committedSep 11, 2020
feat(core): optimize project locator perf
1 parent 4ea6ef8 commit 46454aa

File tree

5 files changed

+95
-42
lines changed

5 files changed

+95
-42
lines changed
 

Diff for: ‎packages/workspace/src/core/project-graph/build-dependencies/explicit-project-dependencies.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export function buildExplicitTypeScriptDependencies(
1414
fileRead: (s: string) => string
1515
) {
1616
const importLocator = new TypeScriptImportLocator(fileRead);
17-
const targetProjectLocator = new TargetProjectLocator(nodes);
17+
const targetProjectLocator = new TargetProjectLocator(nodes, fileRead);
1818
Object.keys(ctx.fileMap).forEach((source) => {
1919
Object.values(ctx.fileMap[source]).forEach((f) => {
2020
importLocator.fromFile(

Diff for: ‎packages/workspace/src/core/target-project-locator.spec.ts

+28-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { fs, vol } from 'memfs';
2-
import { join } from 'path';
32
import {
43
ProjectGraphContext,
54
ProjectGraphNode,
@@ -197,6 +196,34 @@ describe('findTargetProjectWithImport', () => {
197196
targetProjectLocator = new TargetProjectLocator(projects);
198197
});
199198

199+
it('should be able to resolve a module by using relative paths', () => {
200+
const res1 = targetProjectLocator.findProjectWithImport(
201+
'./class.ts',
202+
'libs/proj/index.ts',
203+
ctx.nxJson.npmScope
204+
);
205+
const res2 = targetProjectLocator.findProjectWithImport(
206+
'../index.ts',
207+
'libs/proj/src/index.ts',
208+
ctx.nxJson.npmScope
209+
);
210+
const res3 = targetProjectLocator.findProjectWithImport(
211+
'../proj/../proj2/index.ts',
212+
'libs/proj/index.ts',
213+
ctx.nxJson.npmScope
214+
);
215+
const res4 = targetProjectLocator.findProjectWithImport(
216+
'../proj/../index.ts',
217+
'libs/proj/src/index.ts',
218+
ctx.nxJson.npmScope
219+
);
220+
221+
expect(res1).toEqual('proj');
222+
expect(res2).toEqual('proj');
223+
expect(res3).toEqual('proj2');
224+
expect(res4).toEqual('proj');
225+
});
226+
200227
it('should be able to resolve a module by using tsConfig paths', () => {
201228
const proj2 = targetProjectLocator.findProjectWithImport(
202229
'@proj/my-second-proj',
@@ -243,19 +270,4 @@ describe('findTargetProjectWithImport', () => {
243270
);
244271
expect(parentProj).toEqual('proj1234');
245272
});
246-
247-
it('should be able to sort graph nodes', () => {
248-
expect(targetProjectLocator._sortedNodeNames).toEqual([
249-
'proj1234-child',
250-
'proj1234',
251-
'proj123',
252-
'proj4ab',
253-
'proj3a',
254-
'proj2',
255-
'proj',
256-
'@ng/core',
257-
'@ng/common',
258-
'npm-package',
259-
]);
260-
});
261273
});
+54-23
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,29 @@
11
import { resolveModuleByImport } from '../utils/typescript';
2-
import { normalizedProjectRoot } from './file-utils';
2+
import { defaultFileRead, normalizedProjectRoot } from './file-utils';
33
import { ProjectGraphNodeRecords } from './project-graph/project-graph-models';
44
import { getSortedProjectNodes, isWorkspaceProject } from './project-graph';
5+
import { isRelativePath, parseJsonWithComments } from '../utils/fileutils';
6+
import { dirname, join } from 'path';
57

68
export class TargetProjectLocator {
7-
_sortedNodeNames = [];
9+
private sortedWorkspaceProjects = [];
10+
private paths = parseJsonWithComments(this.fileRead(`./tsconfig.base.json`))
11+
?.compilerOptions?.paths;
12+
private cache = new Map<string, string>();
813

9-
constructor(private nodes: ProjectGraphNodeRecords) {
10-
this._sortedNodeNames = getSortedProjectNodes(nodes).map(
11-
({ name }) => name
12-
);
14+
constructor(
15+
private nodes: ProjectGraphNodeRecords,
16+
private fileRead: (path: string) => string = defaultFileRead
17+
) {
18+
this.sortedWorkspaceProjects = getSortedProjectNodes(nodes)
19+
.filter((node) => isWorkspaceProject(node))
20+
.map((node) => ({
21+
...node,
22+
data: {
23+
...node.data,
24+
normalizedRoot: normalizedProjectRoot(node),
25+
},
26+
}));
1327
}
1428

1529
/**
@@ -27,28 +41,45 @@ export class TargetProjectLocator {
2741
importExpr: string,
2842
filePath: string,
2943
npmScope: string
30-
) {
44+
): string {
3145
const normalizedImportExpr = importExpr.split('#')[0];
3246

33-
const resolvedModule = resolveModuleByImport(
34-
normalizedImportExpr,
35-
filePath
36-
);
37-
38-
return this._sortedNodeNames.find((projectName) => {
39-
const p = this.nodes[projectName];
47+
if (isRelativePath(normalizedImportExpr)) {
48+
const resolvedModule = join(dirname(filePath), normalizedImportExpr);
49+
return this.findProjectOfResolvedModule(resolvedModule);
50+
}
4051

41-
if (!isWorkspaceProject(p)) {
42-
return false;
52+
if (this.paths && this.paths[normalizedImportExpr]) {
53+
for (let p of this.paths[normalizedImportExpr]) {
54+
const maybeResolvedProject = this.findProjectOfResolvedModule(p);
55+
if (maybeResolvedProject) {
56+
return maybeResolvedProject;
57+
}
4358
}
59+
}
4460

45-
if (resolvedModule && resolvedModule.startsWith(p.data.root)) {
46-
return true;
47-
} else {
48-
const normalizedRoot = normalizedProjectRoot(p);
49-
const projectImport = `@${npmScope}/${normalizedRoot}`;
50-
return normalizedImportExpr.startsWith(projectImport);
51-
}
61+
const resolvedModule = this.cache.has(normalizedImportExpr)
62+
? this.cache.get(normalizedImportExpr)
63+
: resolveModuleByImport(normalizedImportExpr, filePath);
64+
65+
this.cache.set(normalizedImportExpr, resolvedModule);
66+
if (resolvedModule) {
67+
return this.findProjectOfResolvedModule(resolvedModule);
68+
}
69+
70+
const importedProject = this.sortedWorkspaceProjects.find((p) => {
71+
const projectImport = `@${npmScope}/${p.data.normalizedRoot}`;
72+
return normalizedImportExpr.startsWith(projectImport);
5273
});
74+
75+
return importedProject?.name;
76+
}
77+
78+
private findProjectOfResolvedModule(resolvedModule: string) {
79+
const importedProject = this.sortedWorkspaceProjects.find((p) => {
80+
return resolvedModule.startsWith(p.data.root);
81+
});
82+
83+
return importedProject?.name;
5384
}
5485
}

Diff for: ‎packages/workspace/src/utils/ast-utils.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,13 @@ export function getFullProjectGraphFromHost(host: Tree): ProjectGraph {
402402
const workspaceJson = readJsonInTree(host, getWorkspacePath(host));
403403
const nxJson = readJsonInTree<NxJson>(host, '/nx.json');
404404

405-
const fileRead = (f: string) => host.read(f).toString();
405+
const fileRead = (f: string) => {
406+
try {
407+
return host.read(f).toString();
408+
} catch (e) {
409+
throw new Error(`${f} does not exist`);
410+
}
411+
};
406412

407413
const workspaceFiles: FileData[] = [];
408414

Diff for: ‎packages/workspace/src/utils/fileutils.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,11 @@ export function serializeJson(json: any): string {
3535
* @param path Path of the JSON file on the filesystem
3636
*/
3737
export function readJsonFile<T = any>(path: string): T {
38-
return JSON.parse(stripJsonComments(fs.readFileSync(path, 'utf-8')));
38+
return parseJsonWithComments<T>(fs.readFileSync(path, 'utf-8'));
39+
}
40+
41+
export function parseJsonWithComments<T = any>(content: string): T {
42+
return JSON.parse(stripJsonComments(content));
3943
}
4044

4145
export function writeJsonFile(path: string, json: any) {

0 commit comments

Comments
 (0)
Please sign in to comment.