feat(core): improve project graph creation not to invalidate nxdeps when global config change
This commit is contained in:
parent
f26eb1cffc
commit
f86a07367a
@ -163,11 +163,6 @@ describe('project graph', () => {
|
||||
type: 'app',
|
||||
data: expect.anything(),
|
||||
},
|
||||
'demo-e2e': {
|
||||
name: 'demo-e2e',
|
||||
type: 'e2e',
|
||||
data: expect.anything(),
|
||||
},
|
||||
ui: {
|
||||
name: 'ui',
|
||||
type: 'lib',
|
||||
@ -175,13 +170,6 @@ describe('project graph', () => {
|
||||
},
|
||||
},
|
||||
dependencies: {
|
||||
'demo-e2e': [
|
||||
{
|
||||
type: 'implicit',
|
||||
source: 'demo-e2e',
|
||||
target: 'demo',
|
||||
},
|
||||
],
|
||||
demo: [
|
||||
{
|
||||
type: 'static',
|
||||
@ -240,21 +228,9 @@ describe('project graph', () => {
|
||||
type: 'app',
|
||||
data: expect.anything(),
|
||||
},
|
||||
'demo-e2e': {
|
||||
name: 'demo-e2e',
|
||||
type: 'e2e',
|
||||
data: expect.anything(),
|
||||
},
|
||||
},
|
||||
dependencies: {
|
||||
'npm:happy-nrwl': [],
|
||||
'demo-e2e': [
|
||||
{
|
||||
type: 'implicit',
|
||||
source: 'demo-e2e',
|
||||
target: 'demo',
|
||||
},
|
||||
],
|
||||
demo: [
|
||||
{
|
||||
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 () => {
|
||||
|
||||
358
packages/workspace/src/core/nx-deps/nx-deps-cache.spec.ts
Normal file
358
packages/workspace/src/core/nx-deps/nx-deps-cache.spec.ts
Normal 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;
|
||||
}
|
||||
});
|
||||
@ -1,8 +1,10 @@
|
||||
import { FileData, filesChanged } from '../file-utils';
|
||||
import type {
|
||||
NxJsonConfiguration,
|
||||
ProjectGraph,
|
||||
ProjectGraphDependency,
|
||||
ProjectGraphNode,
|
||||
WorkspaceJsonConfiguration,
|
||||
} from '@nrwl/devkit';
|
||||
import { join } from 'path';
|
||||
import { appRootPath } from '@nrwl/tao/src/utils/app-root';
|
||||
@ -23,7 +25,9 @@ import {
|
||||
|
||||
export interface ProjectGraphCache {
|
||||
version: string;
|
||||
rootFiles: FileData[];
|
||||
deps: Record<string, string>;
|
||||
pathMappings: Record<string, any>;
|
||||
nxJsonPlugins: { name: string; version: string }[];
|
||||
nodes: Record<string, ProjectGraphNode>;
|
||||
dependencies: Record<string, ProjectGraphDependency[]>;
|
||||
}
|
||||
@ -74,68 +78,125 @@ export function readCache(): false | ProjectGraphCache {
|
||||
}
|
||||
|
||||
export function writeCache(
|
||||
rootFiles: FileData[],
|
||||
packageJsonDeps: Record<string, string>,
|
||||
nxJson: NxJsonConfiguration,
|
||||
tsConfig: { compilerOptions: { paths: { [k: string]: any } } },
|
||||
projectGraph: ProjectGraph
|
||||
): void {
|
||||
performance.mark('write cache:start');
|
||||
writeJsonFile(nxDepsPath, {
|
||||
version: '2.0',
|
||||
rootFiles,
|
||||
const nxJsonPlugins = (nxJson.plugins || []).map((p) => ({
|
||||
name: p,
|
||||
version: packageJsonDeps[p],
|
||||
}));
|
||||
const newValue: ProjectGraphCache = {
|
||||
version: '3.0',
|
||||
deps: packageJsonDeps,
|
||||
pathMappings: tsConfig.compilerOptions.paths,
|
||||
nxJsonPlugins,
|
||||
nodes: projectGraph.nodes,
|
||||
dependencies: projectGraph.dependencies,
|
||||
});
|
||||
};
|
||||
writeJsonFile(nxDepsPath, newValue);
|
||||
performance.mark('write cache:end');
|
||||
performance.measure('write cache', 'write cache:start', 'write cache:end');
|
||||
}
|
||||
|
||||
export function differentFromCache(
|
||||
fileMap: ProjectFileMap,
|
||||
c: ProjectGraphCache
|
||||
): {
|
||||
noDifference: boolean;
|
||||
filesDifferentFromCache: ProjectFileMap;
|
||||
partiallyConstructedProjectGraph?: ProjectGraph;
|
||||
} {
|
||||
const currentProjects = Object.keys(fileMap)
|
||||
.sort()
|
||||
.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,
|
||||
};
|
||||
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;
|
||||
}
|
||||
|
||||
// Projects are same -> compute projects with file changes
|
||||
// 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,
|
||||
nxJson: NxJsonConfiguration,
|
||||
c: ProjectGraphCache
|
||||
): {
|
||||
filesDifferentFromCache: ProjectFileMap;
|
||||
cachedPartOfProjectGraph: ProjectGraph;
|
||||
} {
|
||||
const currentProjects = Object.keys(fileMap).filter(
|
||||
(name) => fileMap[name].length > 0
|
||||
);
|
||||
|
||||
const filesDifferentFromCache: ProjectFileMap = {};
|
||||
// Re-compute nodes and dependencies for projects whose files changed
|
||||
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];
|
||||
delete c.dependencies[p];
|
||||
delete c.nodes[p];
|
||||
}
|
||||
});
|
||||
|
||||
// Re-compute nodes and dependencies for each project in file map.
|
||||
Object.keys(filesDifferentFromCache).forEach((key) => {
|
||||
delete c.dependencies[key];
|
||||
// Re-compute nodes and dependencies for projects whose implicit deps changed
|
||||
Object.keys(nxJson.projects || {}).forEach((p) => {
|
||||
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 {
|
||||
filesDifferentFromCache,
|
||||
partiallyConstructedProjectGraph,
|
||||
noDifference: Object.keys(filesDifferentFromCache).length === 0,
|
||||
cachedPartOfProjectGraph: {
|
||||
nodes: c.nodes,
|
||||
dependencies: c.dependencies,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -17,14 +17,5 @@ export function buildImplicitProjectDependencies(
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -11,28 +11,16 @@ export {
|
||||
DependencyType,
|
||||
} from '@nrwl/devkit';
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export type ProjectGraphNodeRecords = Record<string, ProjectGraphNode>;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export type AddProjectNode = (node: ProjectGraphNode) => void;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export type AddProjectDependency = (
|
||||
type: DependencyType | string,
|
||||
source: string,
|
||||
target: string
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export interface ProjectGraphContext {
|
||||
workspaceJson: any;
|
||||
nxJson: NxJsonConfiguration;
|
||||
|
||||
@ -162,9 +162,7 @@ describe('project graph', () => {
|
||||
api: [
|
||||
{ type: DependencyType.static, source: 'api', target: 'npm:express' },
|
||||
],
|
||||
'demo-e2e': [
|
||||
{ type: DependencyType.implicit, source: 'demo-e2e', target: 'demo' },
|
||||
],
|
||||
'demo-e2e': [],
|
||||
demo: [
|
||||
{ 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();
|
||||
expect(graph.nodes).toMatchObject({
|
||||
demo: { name: 'demo', type: 'app' },
|
||||
});
|
||||
workspaceJson.projects.demo.projectType = 'library';
|
||||
//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));
|
||||
workspaceJson.projects.renamed = workspaceJson.projects.demo;
|
||||
fs.writeFileSync('/root/workspace.json', JSON.stringify(workspaceJson));
|
||||
|
||||
defaultFileHasher.init();
|
||||
|
||||
graph = await createProjectGraphAsync();
|
||||
expect(graph.nodes).toMatchObject({
|
||||
demo: { name: 'demo', type: 'lib' },
|
||||
renamed: { name: 'renamed', type: 'app' },
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -2,27 +2,31 @@ import type {
|
||||
FileData,
|
||||
NxJsonConfiguration,
|
||||
NxJsonProjectConfiguration,
|
||||
NxPlugin,
|
||||
ProjectConfiguration,
|
||||
ProjectFileMap,
|
||||
WorkspaceJsonConfiguration,
|
||||
ProjectGraphProcessorContext,
|
||||
NxPlugin,
|
||||
ProjectGraph,
|
||||
ProjectGraphProcessorContext,
|
||||
WorkspaceJsonConfiguration,
|
||||
} from '@nrwl/devkit';
|
||||
|
||||
import { ProjectGraphBuilder } from '@nrwl/devkit';
|
||||
|
||||
import { ProjectGraphBuilder, readJsonFile } from '@nrwl/devkit';
|
||||
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 { createProjectFileMap } from '../file-graph';
|
||||
import {
|
||||
filesChanged,
|
||||
readNxJson,
|
||||
readWorkspaceFiles,
|
||||
readWorkspaceJson,
|
||||
rootWorkspaceFileData,
|
||||
} from '../file-utils';
|
||||
import { normalizeNxJson } from '../normalize-nx-json';
|
||||
import {
|
||||
extractCachedPartOfProjectGraph,
|
||||
readCache,
|
||||
shouldRecomputeWholeGraph,
|
||||
writeCache,
|
||||
} from '../nx-deps/nx-deps-cache';
|
||||
import {
|
||||
BuildDependencies,
|
||||
buildExplicitPackageJsonDependencies,
|
||||
@ -34,17 +38,16 @@ import {
|
||||
buildNpmPackageNodes,
|
||||
buildWorkspaceProjectNodes,
|
||||
} from './build-nodes';
|
||||
import {
|
||||
differentFromCache,
|
||||
readCache,
|
||||
writeCache,
|
||||
} from '../nx-deps/nx-deps-cache';
|
||||
import { performance } from 'perf_hooks';
|
||||
|
||||
export async function createProjectGraphAsync(): Promise<ProjectGraph> {
|
||||
return createProjectGraph();
|
||||
}
|
||||
|
||||
function readCombinedDeps() {
|
||||
const json = readJsonFile(join(appRootPath, 'package.json'));
|
||||
return { ...json.dependencies, ...json.devDependencies };
|
||||
}
|
||||
|
||||
// TODO(v13): remove this deprecated function
|
||||
/**
|
||||
* @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;
|
||||
assertWorkspaceValidity(workspaceJson, nxJson);
|
||||
const normalizedNxJson = normalizeNxJson(nxJson);
|
||||
|
||||
const rootFiles = rootWorkspaceFileData();
|
||||
const projectFileMap = createProjectFileMap(workspaceJson, workspaceFiles);
|
||||
|
||||
if (cache && !filesChanged(rootFiles, cache.rootFiles)) {
|
||||
const diff = differentFromCache(projectFileMap, cache);
|
||||
if (diff.noDifference) {
|
||||
return addWorkspaceFiles(
|
||||
diff.partiallyConstructedProjectGraph,
|
||||
workspaceFiles
|
||||
);
|
||||
}
|
||||
|
||||
const packageJsonDeps = readCombinedDeps();
|
||||
const rootTsConfig = readRootTsConfig();
|
||||
if (
|
||||
cache &&
|
||||
cache.version === '3.0' &&
|
||||
!shouldRecomputeWholeGraph(
|
||||
cache,
|
||||
packageJsonDeps,
|
||||
workspaceJson,
|
||||
normalizedNxJson,
|
||||
rootTsConfig
|
||||
)
|
||||
) {
|
||||
const diff = extractCachedPartOfProjectGraph(projectFileMap, nxJson, cache);
|
||||
const ctx = {
|
||||
workspaceJson,
|
||||
nxJson: normalizedNxJson,
|
||||
fileMap: diff.filesDifferentFromCache,
|
||||
};
|
||||
const projectGraph = buildProjectGraph(
|
||||
ctx,
|
||||
diff.partiallyConstructedProjectGraph
|
||||
);
|
||||
const projectGraph = buildProjectGraph(ctx, diff.cachedPartOfProjectGraph);
|
||||
if (cacheEnabled) {
|
||||
writeCache(rootFiles, projectGraph);
|
||||
writeCache(packageJsonDeps, nxJson, rootTsConfig, projectGraph);
|
||||
}
|
||||
return addWorkspaceFiles(projectGraph, workspaceFiles);
|
||||
} else {
|
||||
@ -92,7 +94,7 @@ export function createProjectGraph(
|
||||
};
|
||||
const projectGraph = buildProjectGraph(ctx, null);
|
||||
if (cacheEnabled) {
|
||||
writeCache(rootFiles, projectGraph);
|
||||
writeCache(packageJsonDeps, nxJson, rootTsConfig, projectGraph);
|
||||
}
|
||||
return addWorkspaceFiles(projectGraph, workspaceFiles);
|
||||
}
|
||||
@ -110,31 +112,41 @@ function addWorkspaceFiles(
|
||||
return { ...projectGraph, allWorkspaceFiles };
|
||||
}
|
||||
|
||||
function buildProjectGraph(
|
||||
ctx: {
|
||||
nxJson: NxJsonConfiguration<string[]>;
|
||||
workspaceJson: WorkspaceJsonConfiguration;
|
||||
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();
|
||||
type BuilderContext = {
|
||||
nxJson: NxJsonConfiguration<string[]>;
|
||||
workspaceJson: WorkspaceJsonConfiguration;
|
||||
fileMap: ProjectFileMap;
|
||||
};
|
||||
|
||||
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 pluginPath = require.resolve(path, {
|
||||
paths: [appRootPath],
|
||||
@ -161,19 +173,18 @@ function buildProjectGraph(
|
||||
fileMap: ctx.fileMap,
|
||||
};
|
||||
|
||||
const result = plugins.reduce((graph, plugin) => {
|
||||
return plugins.reduce((graph, plugin) => {
|
||||
if (!plugin.processProjectGraph) {
|
||||
return graph;
|
||||
}
|
||||
|
||||
return plugin.processProjectGraph(graph, context);
|
||||
}, r);
|
||||
|
||||
performance.mark('build project graph:end');
|
||||
performance.measure(
|
||||
'build project graph',
|
||||
'build project graph:start',
|
||||
'build project graph:end'
|
||||
);
|
||||
return result;
|
||||
}, initProjectGraph);
|
||||
}
|
||||
|
||||
function readRootTsConfig() {
|
||||
try {
|
||||
return readJsonFile(join(appRootPath, 'tsconfig.base.json'));
|
||||
} catch (e) {
|
||||
return readJsonFile(join(appRootPath, 'tsconfig.json'));
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user