feat(core): improve project graph creation not to invalidate nxdeps when global config change

This commit is contained in:
vsavkin 2021-07-14 16:22:03 -04:00 committed by Victor Savkin
parent f26eb1cffc
commit f86a07367a
7 changed files with 545 additions and 164 deletions

View File

@ -163,11 +163,6 @@ describe('project graph', () => {
type: 'app', type: 'app',
data: expect.anything(), data: expect.anything(),
}, },
'demo-e2e': {
name: 'demo-e2e',
type: 'e2e',
data: expect.anything(),
},
ui: { ui: {
name: 'ui', name: 'ui',
type: 'lib', type: 'lib',
@ -175,13 +170,6 @@ describe('project graph', () => {
}, },
}, },
dependencies: { dependencies: {
'demo-e2e': [
{
type: 'implicit',
source: 'demo-e2e',
target: 'demo',
},
],
demo: [ demo: [
{ {
type: 'static', type: 'static',
@ -240,21 +228,9 @@ describe('project graph', () => {
type: 'app', type: 'app',
data: expect.anything(), data: expect.anything(),
}, },
'demo-e2e': {
name: 'demo-e2e',
type: 'e2e',
data: expect.anything(),
},
}, },
dependencies: { dependencies: {
'npm:happy-nrwl': [], 'npm:happy-nrwl': [],
'demo-e2e': [
{
type: 'implicit',
source: 'demo-e2e',
target: 'demo',
},
],
demo: [ demo: [
{ {
type: 'static', type: 'static',
@ -286,7 +262,7 @@ describe('project graph', () => {
}, },
]); ]);
expect(Object.keys(affected.nodes)).toEqual(['demo', 'demo-e2e', 'api']); expect(Object.keys(affected.nodes)).toEqual(['demo', 'api']);
}); });
it('should support implicit JSON file dependencies (all projects)', async () => { it('should support implicit JSON file dependencies (all projects)', async () => {

View File

@ -0,0 +1,358 @@
import { NxJsonConfiguration, WorkspaceJsonConfiguration } from '@nrwl/devkit';
import {
extractCachedPartOfProjectGraph,
ProjectGraphCache,
shouldRecomputeWholeGraph,
} from './nx-deps-cache';
describe('nx deps utils', () => {
describe('shouldRecomputeWholeGraph', () => {
it('should be false when nothing changes', () => {
expect(
shouldRecomputeWholeGraph(
createCache({}),
createPackageJsonDeps({}),
createWorkspaceJson({}),
createNxJson({}),
createTsConfigJson()
)
).toEqual(false);
});
it('should be true when version of nrwl/workspace changes', () => {
expect(
shouldRecomputeWholeGraph(
createCache({
deps: {
'@nrwl/workspace': '12.0.1',
plugin: '1.0.0',
},
}),
createPackageJsonDeps({}),
createWorkspaceJson({}),
createNxJson({}),
createTsConfigJson()
)
).toEqual(true);
});
it('should be true when a cached project is missing', () => {
expect(
shouldRecomputeWholeGraph(
createCache({
nodes: {
'renamed-mylib': {} as any,
},
}),
createPackageJsonDeps({}),
createWorkspaceJson({}),
createNxJson({}),
createTsConfigJson()
)
).toEqual(true);
});
it('should be true when a path mapping changes', () => {
expect(
shouldRecomputeWholeGraph(
createCache({}),
createPackageJsonDeps({}),
createWorkspaceJson({}),
createNxJson({}),
createTsConfigJson({ mylib: ['libs/mylib/changed.ts'] })
)
).toEqual(true);
});
it('should be true when number of plugins changed', () => {
expect(
shouldRecomputeWholeGraph(
createCache({}),
createPackageJsonDeps({}),
createWorkspaceJson({}),
createNxJson({
plugins: ['plugin', 'plugin2'],
}),
createTsConfigJson()
)
).toEqual(true);
});
it('should be true when plugin version changed', () => {
expect(
shouldRecomputeWholeGraph(
createCache({}),
createPackageJsonDeps({ plugin: '2.0.0' }),
createWorkspaceJson({}),
createNxJson({}),
createTsConfigJson()
)
).toEqual(true);
});
});
describe('extractCachedPartOfProjectGraph', () => {
it('should return the cache project graph when nothing has changed', () => {
const cached = {
nodes: {
mylib: {
name: 'mylib',
type: 'lib',
data: {
files: [
{
file: 'index.ts',
ext: 'ts',
hash: 'hash1',
},
],
},
},
},
dependencies: { mylib: [] },
} as any;
const r = extractCachedPartOfProjectGraph(
{
mylib: [
{
file: 'index.ts',
ext: 'ts',
hash: 'hash1',
},
],
},
createNxJson({}),
createCache({
nodes: { ...cached.nodes },
dependencies: { ...cached.dependencies },
})
);
expect(r.filesDifferentFromCache).toEqual({});
expect(r.cachedPartOfProjectGraph).toEqual(cached);
});
it('should handle cases when no projects are added', () => {
const cached = {
nodes: {
mylib: {
name: 'mylib',
type: 'lib',
data: {
files: [
{
file: 'index.ts',
ext: 'ts',
hash: 'hash1',
},
],
},
},
},
dependencies: { mylib: [] },
} as any;
const r = extractCachedPartOfProjectGraph(
{
mylib: [
{
file: 'index.ts',
ext: 'ts',
hash: 'hash1',
},
],
secondlib: [
{
file: 'index.ts',
ext: 'ts',
hash: 'hash2',
},
],
},
createNxJson({}),
createCache({
nodes: { ...cached.nodes },
dependencies: { ...cached.dependencies },
})
);
expect(r.filesDifferentFromCache).toEqual({
secondlib: [
{
file: 'index.ts',
ext: 'ts',
hash: 'hash2',
},
],
});
expect(r.cachedPartOfProjectGraph).toEqual(cached);
});
it('should handle cases when files change', () => {
const cached = {
nodes: {
mylib: {
name: 'mylib',
type: 'lib',
data: {
files: [
{
file: 'index.ts',
ext: 'ts',
hash: 'hash1',
},
],
},
},
},
dependencies: { mylib: [] },
} as any;
const r = extractCachedPartOfProjectGraph(
{
mylib: [
{
file: 'index.ts',
ext: 'ts',
hash: 'hash2',
},
],
},
createNxJson({}),
createCache({
nodes: { ...cached.nodes },
dependencies: { ...cached.dependencies },
})
);
expect(r.filesDifferentFromCache).toEqual({
mylib: [
{
file: 'index.ts',
ext: 'ts',
hash: 'hash2',
},
],
});
expect(r.cachedPartOfProjectGraph).toEqual({
nodes: {},
dependencies: {},
});
});
it('should handle cases when implicits change', () => {
const cached = {
nodes: {
mylib: {
name: 'mylib',
type: 'lib',
data: {
files: [
{
file: 'index.ts',
ext: 'ts',
hash: 'hash1',
},
],
implicitDependencies: ['otherlib'],
},
},
},
dependencies: {
mylib: [{ type: 'static', source: 'mylib', target: 'otherlib' }],
},
} as any;
const r = extractCachedPartOfProjectGraph(
{
mylib: [
{
file: 'index.ts',
ext: 'ts',
hash: 'hash1',
},
],
},
createNxJson({
projects: {
mylib: {
implicitDependencies: [],
},
},
}),
createCache({
nodes: { ...cached.nodes },
dependencies: { ...cached.dependencies },
})
);
expect(r.filesDifferentFromCache).toEqual({
mylib: [
{
file: 'index.ts',
ext: 'ts',
hash: 'hash1',
},
],
});
expect(r.cachedPartOfProjectGraph).toEqual({
nodes: {},
dependencies: {},
});
});
});
function createCache(p: Partial<ProjectGraphCache>): ProjectGraphCache {
const defaults: ProjectGraphCache = {
version: '3.0',
deps: {
'@nrwl/workspace': '12.0.0',
plugin: '1.0.0',
},
pathMappings: {
mylib: ['libs/mylib/index.ts'],
},
nxJsonPlugins: [{ name: 'plugin', version: '1.0.0' }],
nodes: {
mylib: {} as any,
},
dependencies: { mylib: [] },
};
return { ...defaults, ...p };
}
function createPackageJsonDeps(
p: Record<string, string>
): Record<string, string> {
const defaults = {
'@nrwl/workspace': '12.0.0',
plugin: '1.0.0',
};
return { ...defaults, ...p };
}
function createWorkspaceJson(p: any): WorkspaceJsonConfiguration {
const defaults = {
projects: { mylib: {} },
} as any;
return { ...defaults, ...p };
}
function createNxJson(p: Partial<NxJsonConfiguration>): NxJsonConfiguration {
const defaults: NxJsonConfiguration = {
npmScope: '',
projects: { mylib: {} },
workspaceLayout: {} as any,
targetDependencies: {},
plugins: ['plugin'],
};
return { ...defaults, ...p };
}
function createTsConfigJson(paths?: { [k: string]: any }): any {
const r = {
compilerOptions: {
paths: {
mylib: ['libs/mylib/index.ts'],
},
},
} as any;
if (paths) {
r.compilerOptions.paths = paths;
}
return r;
}
});

View File

@ -1,8 +1,10 @@
import { FileData, filesChanged } from '../file-utils'; import { FileData, filesChanged } from '../file-utils';
import type { import type {
NxJsonConfiguration,
ProjectGraph, ProjectGraph,
ProjectGraphDependency, ProjectGraphDependency,
ProjectGraphNode, ProjectGraphNode,
WorkspaceJsonConfiguration,
} from '@nrwl/devkit'; } from '@nrwl/devkit';
import { join } from 'path'; import { join } from 'path';
import { appRootPath } from '@nrwl/tao/src/utils/app-root'; import { appRootPath } from '@nrwl/tao/src/utils/app-root';
@ -23,7 +25,9 @@ import {
export interface ProjectGraphCache { export interface ProjectGraphCache {
version: string; version: string;
rootFiles: FileData[]; deps: Record<string, string>;
pathMappings: Record<string, any>;
nxJsonPlugins: { name: string; version: string }[];
nodes: Record<string, ProjectGraphNode>; nodes: Record<string, ProjectGraphNode>;
dependencies: Record<string, ProjectGraphDependency[]>; dependencies: Record<string, ProjectGraphDependency[]>;
} }
@ -74,68 +78,125 @@ export function readCache(): false | ProjectGraphCache {
} }
export function writeCache( export function writeCache(
rootFiles: FileData[], packageJsonDeps: Record<string, string>,
nxJson: NxJsonConfiguration,
tsConfig: { compilerOptions: { paths: { [k: string]: any } } },
projectGraph: ProjectGraph projectGraph: ProjectGraph
): void { ): void {
performance.mark('write cache:start'); performance.mark('write cache:start');
writeJsonFile(nxDepsPath, { const nxJsonPlugins = (nxJson.plugins || []).map((p) => ({
version: '2.0', name: p,
rootFiles, version: packageJsonDeps[p],
}));
const newValue: ProjectGraphCache = {
version: '3.0',
deps: packageJsonDeps,
pathMappings: tsConfig.compilerOptions.paths,
nxJsonPlugins,
nodes: projectGraph.nodes, nodes: projectGraph.nodes,
dependencies: projectGraph.dependencies, dependencies: projectGraph.dependencies,
}); };
writeJsonFile(nxDepsPath, newValue);
performance.mark('write cache:end'); performance.mark('write cache:end');
performance.measure('write cache', 'write cache:start', 'write cache:end'); performance.measure('write cache', 'write cache:start', 'write cache:end');
} }
export function differentFromCache( export function shouldRecomputeWholeGraph(
cache: ProjectGraphCache,
packageJsonDeps: Record<string, string>,
workspaceJson: WorkspaceJsonConfiguration,
nxJson: NxJsonConfiguration,
tsConfig: { compilerOptions: { paths: { [k: string]: any } } }
): boolean {
if (cache.deps['@nrwl/workspace'] !== packageJsonDeps['@nrwl/workspace']) {
return true;
}
// we have a cached project that is no longer present
if (
Object.keys(cache.nodes).some(
(p) =>
cache.nodes[p].type != 'app' &&
cache.nodes[p].type != 'lib' &&
!workspaceJson.projects[p]
)
) {
return true;
}
// a path mapping for an existing project has changed
if (
Object.keys(cache.pathMappings).some(
(t) =>
JSON.stringify(cache.pathMappings[t]) !=
JSON.stringify(tsConfig.compilerOptions.paths[t])
)
) {
return true;
}
// a new plugin has been added
if ((nxJson.plugins || []).length !== cache.nxJsonPlugins.length) return true;
// a plugin has changed
if (
(nxJson.plugins || []).some((t) => {
const matchingPlugin = cache.nxJsonPlugins.find((p) => p.name === t);
if (!matchingPlugin) return true;
return matchingPlugin.version !== packageJsonDeps[t];
})
) {
return true;
}
return false;
}
/*
This can only be invoked when the list of projects is either the same
or new projects have been added, so every project in the cache has a corresponding
project in fileMap
*/
export function extractCachedPartOfProjectGraph(
fileMap: ProjectFileMap, fileMap: ProjectFileMap,
nxJson: NxJsonConfiguration,
c: ProjectGraphCache c: ProjectGraphCache
): { ): {
noDifference: boolean;
filesDifferentFromCache: ProjectFileMap; filesDifferentFromCache: ProjectFileMap;
partiallyConstructedProjectGraph?: ProjectGraph; cachedPartOfProjectGraph: ProjectGraph;
} { } {
const currentProjects = Object.keys(fileMap) const currentProjects = Object.keys(fileMap).filter(
.sort() (name) => fileMap[name].length > 0
.filter((name) => fileMap[name].length > 0); );
const previousProjects = Object.keys(c.nodes)
.sort()
.filter((name) => c.nodes[name].data.files.length > 0);
// Projects changed -> compute entire graph
if (
currentProjects.length !== previousProjects.length ||
currentProjects.some((val, idx) => val !== previousProjects[idx])
) {
return {
filesDifferentFromCache: fileMap,
partiallyConstructedProjectGraph: null,
noDifference: false,
};
}
// Projects are same -> compute projects with file changes
const filesDifferentFromCache: ProjectFileMap = {}; const filesDifferentFromCache: ProjectFileMap = {};
// Re-compute nodes and dependencies for projects whose files changed
currentProjects.forEach((p) => { currentProjects.forEach((p) => {
if (filesChanged(c.nodes[p].data.files, fileMap[p])) { if (!c.nodes[p] || filesChanged(c.nodes[p].data.files, fileMap[p])) {
filesDifferentFromCache[p] = fileMap[p]; filesDifferentFromCache[p] = fileMap[p];
delete c.dependencies[p];
delete c.nodes[p];
} }
}); });
// Re-compute nodes and dependencies for each project in file map. // Re-compute nodes and dependencies for projects whose implicit deps changed
Object.keys(filesDifferentFromCache).forEach((key) => { Object.keys(nxJson.projects || {}).forEach((p) => {
delete c.dependencies[key]; if (
nxJson.projects[p]?.implicitDependencies &&
JSON.stringify(c.nodes[p].data.implicitDependencies) !==
JSON.stringify(nxJson.projects[p].implicitDependencies)
) {
filesDifferentFromCache[p] = fileMap[p];
delete c.dependencies[p];
delete c.nodes[p];
}
}); });
const partiallyConstructedProjectGraph = {
nodes: c.nodes,
dependencies: c.dependencies,
};
return { return {
filesDifferentFromCache, filesDifferentFromCache,
partiallyConstructedProjectGraph, cachedPartOfProjectGraph: {
noDifference: Object.keys(filesDifferentFromCache).length === 0, nodes: c.nodes,
dependencies: c.dependencies,
},
}; };
} }

View File

@ -17,14 +17,5 @@ export function buildImplicitProjectDependencies(
addDependency(DependencyType.implicit, source, target); addDependency(DependencyType.implicit, source, target);
}); });
} }
// TODO(v10): remove this because implicit dependencies are generated now..
if (source.endsWith('-e2e')) {
const target = source.replace(/-e2e$/, '');
// Only add if expected source actually exists, otherwise this will error out.
if (nodes[target]) {
addDependency(DependencyType.implicit, source, target);
}
}
}); });
} }

View File

@ -11,28 +11,16 @@ export {
DependencyType, DependencyType,
} from '@nrwl/devkit'; } from '@nrwl/devkit';
/**
* @deprecated
*/
export type ProjectGraphNodeRecords = Record<string, ProjectGraphNode>; export type ProjectGraphNodeRecords = Record<string, ProjectGraphNode>;
/**
* @deprecated
*/
export type AddProjectNode = (node: ProjectGraphNode) => void; export type AddProjectNode = (node: ProjectGraphNode) => void;
/**
* @deprecated
*/
export type AddProjectDependency = ( export type AddProjectDependency = (
type: DependencyType | string, type: DependencyType | string,
source: string, source: string,
target: string target: string
) => void; ) => void;
/**
* @deprecated
*/
export interface ProjectGraphContext { export interface ProjectGraphContext {
workspaceJson: any; workspaceJson: any;
nxJson: NxJsonConfiguration; nxJson: NxJsonConfiguration;

View File

@ -162,9 +162,7 @@ describe('project graph', () => {
api: [ api: [
{ type: DependencyType.static, source: 'api', target: 'npm:express' }, { type: DependencyType.static, source: 'api', target: 'npm:express' },
], ],
'demo-e2e': [ 'demo-e2e': [],
{ type: DependencyType.implicit, source: 'demo-e2e', target: 'demo' },
],
demo: [ demo: [
{ type: DependencyType.static, source: 'demo', target: 'ui' }, { type: DependencyType.static, source: 'demo', target: 'ui' },
{ {
@ -193,21 +191,19 @@ describe('project graph', () => {
}); });
}); });
it('should update the graph if the workspace file changes ', async () => { it('should update the graph if a project got renamed', async () => {
let graph = await createProjectGraphAsync(); let graph = await createProjectGraphAsync();
expect(graph.nodes).toMatchObject({ expect(graph.nodes).toMatchObject({
demo: { name: 'demo', type: 'app' }, demo: { name: 'demo', type: 'app' },
}); });
workspaceJson.projects.demo.projectType = 'library'; workspaceJson.projects.renamed = workspaceJson.projects.demo;
//wait a tick to ensure the modified time of workspace.json will be after the creation of the project graph file
await new Promise((resolve) => setTimeout(resolve, 1));
fs.writeFileSync('/root/workspace.json', JSON.stringify(workspaceJson)); fs.writeFileSync('/root/workspace.json', JSON.stringify(workspaceJson));
defaultFileHasher.init(); defaultFileHasher.init();
graph = await createProjectGraphAsync(); graph = await createProjectGraphAsync();
expect(graph.nodes).toMatchObject({ expect(graph.nodes).toMatchObject({
demo: { name: 'demo', type: 'lib' }, renamed: { name: 'renamed', type: 'app' },
}); });
}); });

View File

@ -2,27 +2,31 @@ import type {
FileData, FileData,
NxJsonConfiguration, NxJsonConfiguration,
NxJsonProjectConfiguration, NxJsonProjectConfiguration,
NxPlugin,
ProjectConfiguration, ProjectConfiguration,
ProjectFileMap, ProjectFileMap,
WorkspaceJsonConfiguration,
ProjectGraphProcessorContext,
NxPlugin,
ProjectGraph, ProjectGraph,
ProjectGraphProcessorContext,
WorkspaceJsonConfiguration,
} from '@nrwl/devkit'; } from '@nrwl/devkit';
import { ProjectGraphBuilder, readJsonFile } from '@nrwl/devkit';
import { ProjectGraphBuilder } from '@nrwl/devkit';
import { appRootPath } from '@nrwl/tao/src/utils/app-root'; import { appRootPath } from '@nrwl/tao/src/utils/app-root';
import { join } from 'path';
import { performance } from 'perf_hooks';
import { assertWorkspaceValidity } from '../assert-workspace-validity'; import { assertWorkspaceValidity } from '../assert-workspace-validity';
import { createProjectFileMap } from '../file-graph'; import { createProjectFileMap } from '../file-graph';
import { import {
filesChanged,
readNxJson, readNxJson,
readWorkspaceFiles, readWorkspaceFiles,
readWorkspaceJson, readWorkspaceJson,
rootWorkspaceFileData,
} from '../file-utils'; } from '../file-utils';
import { normalizeNxJson } from '../normalize-nx-json'; import { normalizeNxJson } from '../normalize-nx-json';
import {
extractCachedPartOfProjectGraph,
readCache,
shouldRecomputeWholeGraph,
writeCache,
} from '../nx-deps/nx-deps-cache';
import { import {
BuildDependencies, BuildDependencies,
buildExplicitPackageJsonDependencies, buildExplicitPackageJsonDependencies,
@ -34,17 +38,16 @@ import {
buildNpmPackageNodes, buildNpmPackageNodes,
buildWorkspaceProjectNodes, buildWorkspaceProjectNodes,
} from './build-nodes'; } from './build-nodes';
import {
differentFromCache,
readCache,
writeCache,
} from '../nx-deps/nx-deps-cache';
import { performance } from 'perf_hooks';
export async function createProjectGraphAsync(): Promise<ProjectGraph> { export async function createProjectGraphAsync(): Promise<ProjectGraph> {
return createProjectGraph(); return createProjectGraph();
} }
function readCombinedDeps() {
const json = readJsonFile(join(appRootPath, 'package.json'));
return { ...json.dependencies, ...json.devDependencies };
}
// TODO(v13): remove this deprecated function // TODO(v13): remove this deprecated function
/** /**
* @deprecated This function is deprecated in favor of the new asynchronous version {@link createProjectGraphAsync} * @deprecated This function is deprecated in favor of the new asynchronous version {@link createProjectGraphAsync}
@ -58,30 +61,29 @@ export function createProjectGraph(
let cache = cacheEnabled ? readCache() : false; let cache = cacheEnabled ? readCache() : false;
assertWorkspaceValidity(workspaceJson, nxJson); assertWorkspaceValidity(workspaceJson, nxJson);
const normalizedNxJson = normalizeNxJson(nxJson); const normalizedNxJson = normalizeNxJson(nxJson);
const rootFiles = rootWorkspaceFileData();
const projectFileMap = createProjectFileMap(workspaceJson, workspaceFiles); const projectFileMap = createProjectFileMap(workspaceJson, workspaceFiles);
const packageJsonDeps = readCombinedDeps();
if (cache && !filesChanged(rootFiles, cache.rootFiles)) { const rootTsConfig = readRootTsConfig();
const diff = differentFromCache(projectFileMap, cache); if (
if (diff.noDifference) { cache &&
return addWorkspaceFiles( cache.version === '3.0' &&
diff.partiallyConstructedProjectGraph, !shouldRecomputeWholeGraph(
workspaceFiles cache,
); packageJsonDeps,
} workspaceJson,
normalizedNxJson,
rootTsConfig
)
) {
const diff = extractCachedPartOfProjectGraph(projectFileMap, nxJson, cache);
const ctx = { const ctx = {
workspaceJson, workspaceJson,
nxJson: normalizedNxJson, nxJson: normalizedNxJson,
fileMap: diff.filesDifferentFromCache, fileMap: diff.filesDifferentFromCache,
}; };
const projectGraph = buildProjectGraph( const projectGraph = buildProjectGraph(ctx, diff.cachedPartOfProjectGraph);
ctx,
diff.partiallyConstructedProjectGraph
);
if (cacheEnabled) { if (cacheEnabled) {
writeCache(rootFiles, projectGraph); writeCache(packageJsonDeps, nxJson, rootTsConfig, projectGraph);
} }
return addWorkspaceFiles(projectGraph, workspaceFiles); return addWorkspaceFiles(projectGraph, workspaceFiles);
} else { } else {
@ -92,7 +94,7 @@ export function createProjectGraph(
}; };
const projectGraph = buildProjectGraph(ctx, null); const projectGraph = buildProjectGraph(ctx, null);
if (cacheEnabled) { if (cacheEnabled) {
writeCache(rootFiles, projectGraph); writeCache(packageJsonDeps, nxJson, rootTsConfig, projectGraph);
} }
return addWorkspaceFiles(projectGraph, workspaceFiles); return addWorkspaceFiles(projectGraph, workspaceFiles);
} }
@ -110,31 +112,41 @@ function addWorkspaceFiles(
return { ...projectGraph, allWorkspaceFiles }; return { ...projectGraph, allWorkspaceFiles };
} }
function buildProjectGraph( type BuilderContext = {
ctx: {
nxJson: NxJsonConfiguration<string[]>; nxJson: NxJsonConfiguration<string[]>;
workspaceJson: WorkspaceJsonConfiguration; workspaceJson: WorkspaceJsonConfiguration;
fileMap: ProjectFileMap; fileMap: ProjectFileMap;
}, };
projectGraph: ProjectGraph
) {
performance.mark('build project graph:start');
const builder = new ProjectGraphBuilder(projectGraph);
const buildNodesFns: BuildNodes[] = [
buildWorkspaceProjectNodes,
buildNpmPackageNodes,
];
const buildDependenciesFns: BuildDependencies[] = [
buildExplicitTypeScriptDependencies,
buildImplicitProjectDependencies,
buildExplicitPackageJsonDependencies,
];
buildNodesFns.forEach((f) => f(ctx, builder.addNode.bind(builder)));
buildDependenciesFns.forEach((f) =>
f(ctx, builder.nodes, builder.addDependency.bind(builder))
);
const r = builder.getProjectGraph();
function buildProjectGraph(ctx: BuilderContext, projectGraph: ProjectGraph) {
performance.mark('build project graph:start');
const builder = new ProjectGraphBuilder(projectGraph);
const addNode = builder.addNode.bind(builder);
const addDependency = builder.addDependency.bind(builder);
buildWorkspaceProjectNodes(ctx, addNode);
buildNpmPackageNodes(ctx, addNode);
buildExplicitTypeScriptDependencies(ctx, builder.nodes, addDependency);
buildExplicitPackageJsonDependencies(ctx, builder.nodes, addDependency);
buildImplicitProjectDependencies(ctx, builder.nodes, addDependency);
const initProjectGraph = builder.getProjectGraph();
const r = updateProjectGraphWithPlugins(ctx, initProjectGraph);
performance.mark('build project graph:end');
performance.measure(
'build project graph',
'build project graph:start',
'build project graph:end'
);
return r;
}
function updateProjectGraphWithPlugins(
ctx: BuilderContext,
initProjectGraph: ProjectGraph
) {
const plugins = (ctx.nxJson.plugins || []).map((path) => { const plugins = (ctx.nxJson.plugins || []).map((path) => {
const pluginPath = require.resolve(path, { const pluginPath = require.resolve(path, {
paths: [appRootPath], paths: [appRootPath],
@ -161,19 +173,18 @@ function buildProjectGraph(
fileMap: ctx.fileMap, fileMap: ctx.fileMap,
}; };
const result = plugins.reduce((graph, plugin) => { return plugins.reduce((graph, plugin) => {
if (!plugin.processProjectGraph) { if (!plugin.processProjectGraph) {
return graph; return graph;
} }
return plugin.processProjectGraph(graph, context); return plugin.processProjectGraph(graph, context);
}, r); }, initProjectGraph);
}
performance.mark('build project graph:end');
performance.measure( function readRootTsConfig() {
'build project graph', try {
'build project graph:start', return readJsonFile(join(appRootPath, 'tsconfig.base.json'));
'build project graph:end' } catch (e) {
); return readJsonFile(join(appRootPath, 'tsconfig.json'));
return result; }
} }