Skip to content

Commit e1e4ac8

Browse files
fix(core): avoid redundant project graph requests in ngcli adapter (#34907)
## Current Behavior The Angular devkit adapter (`ngcli-adapter.ts`) calls `createProjectGraphAsync()` in multiple places, even though callers (executors, generate command, migrate command) already have the project graph available. This results in redundant requests to create the graph. ## Expected Behavior Reuse the project graph from callers when available, falling back to reading from cache (`readCachedProjectGraph()`) or `createProjectGraphAsync()` only when necessary. --------- Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
1 parent 17de526 commit e1e4ac8

File tree

4 files changed

+56
-20
lines changed

4 files changed

+56
-20
lines changed

packages/nx/src/adapter/ngcli-adapter.ts

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ import {
4444
} from '../generators/utils/project-configuration';
4545
import {
4646
createProjectGraphAsync,
47+
readCachedProjectGraph,
4748
readProjectsConfigurationFromProjectGraph,
4849
} from '../project-graph/project-graph';
50+
import type { ProjectGraph } from '../config/project-graph';
4951
import { readJsonFile } from '../utils/fileutils';
5052
import { getNxRequirePaths } from '../utils/installation-directory';
5153
import { parseJson } from '../utils/json';
@@ -76,6 +78,14 @@ import {
7678
} from '../config/schema-utils';
7779
import { resolveNxTokensInOptions } from '../project-graph/utils/project-configuration-utils';
7880

81+
function getProjectGraph(): Promise<ProjectGraph> {
82+
try {
83+
return Promise.resolve(readCachedProjectGraph());
84+
} catch {
85+
return createProjectGraphAsync();
86+
}
87+
}
88+
7989
export async function createBuilderContext(
8090
builderInfo: {
8191
builderName: string;
@@ -85,7 +95,10 @@ export async function createBuilderContext(
8595
context: ExecutorContext
8696
) {
8797
require('./compat');
88-
const fsHost = new NxScopedHostForBuilders(context.root);
98+
const fsHost = new NxScopedHostForBuilders(
99+
context.root,
100+
context.projectGraph
101+
);
89102
// the top level import is not patched because it is imported before the
90103
// patching happens so we require it here to use the patched version below
91104
const { workspaces } = require('@angular-devkit/core');
@@ -213,12 +226,13 @@ export async function scheduleTarget(
213226
runOptions: any;
214227
projects: Record<string, ProjectConfiguration>;
215228
},
216-
verbose: boolean
229+
verbose: boolean,
230+
projectGraph: ProjectGraph
217231
): Promise<Observable<import('@angular-devkit/architect').BuilderOutput>> {
218232
const { Architect } = require('@angular-devkit/architect');
219233

220234
const logger = getLogger(verbose);
221-
const fsHost = new NxScopedHostForBuilders(root);
235+
const fsHost = new NxScopedHostForBuilders(root, projectGraph);
222236
const { workspace } = await workspaces.readWorkspace(
223237
'angular.json',
224238
workspaces.createWorkspaceHost(fsHost)
@@ -456,7 +470,10 @@ async function runSchematic(
456470
type AngularProjectConfiguration = ProjectConfiguration & { prefix?: string };
457471

458472
export class NxScopedHost extends virtualFs.ScopedHost<any> {
459-
constructor(private root: string) {
473+
constructor(
474+
private root: string,
475+
protected _projectGraph?: ProjectGraph
476+
) {
460477
super(new NodeJsSyncHost(), normalize(root));
461478
}
462479

@@ -466,7 +483,10 @@ export class NxScopedHost extends virtualFs.ScopedHost<any> {
466483
isAngularPluginInstalled()
467484
) {
468485
return this.readMergedWorkspaceConfiguration().pipe(
469-
map((r) => stringToArrayBuffer(JSON.stringify(toOldFormat(r))))
486+
// structuredClone to avoid toOldFormat mutating shared graph objects
487+
map((r) =>
488+
stringToArrayBuffer(JSON.stringify(toOldFormat(structuredClone(r))))
489+
)
470490
);
471491
} else {
472492
return super.read(path);
@@ -475,7 +495,7 @@ export class NxScopedHost extends virtualFs.ScopedHost<any> {
475495

476496
protected readMergedWorkspaceConfiguration() {
477497
return zip(
478-
from(createProjectGraphAsync()),
498+
this._projectGraph ? of(this._projectGraph) : from(getProjectGraph()),
479499
this.readExistingAngularJson(),
480500
this.readJson<NxJsonConfiguration>('nx.json')
481501
).pipe(
@@ -704,9 +724,13 @@ export class NxScopedHost extends virtualFs.ScopedHost<any> {
704724
* the project graph to access the expanded targets.
705725
*/
706726
export class NxScopedHostForBuilders extends NxScopedHost {
727+
constructor(root: string, projectGraph: ProjectGraph) {
728+
super(root, projectGraph);
729+
}
730+
707731
protected readMergedWorkspaceConfiguration() {
708732
return zip(
709-
from(createProjectGraphAsync()),
733+
of(this._projectGraph),
710734
this.readExistingAngularJson(),
711735
this.readJson<NxJsonConfiguration>('nx.json')
712736
).pipe(
@@ -754,9 +778,10 @@ export function arrayBufferToString(buffer: any) {
754778
export class NxScopeHostUsedForWrappedSchematics extends NxScopedHost {
755779
constructor(
756780
root: string,
757-
private readonly host: Tree
781+
private readonly host: Tree,
782+
projectGraph: ProjectGraph
758783
) {
759-
super(root);
784+
super(root, projectGraph);
760785
}
761786

762787
read(path: Path): Observable<FileBuffer> {
@@ -867,7 +892,8 @@ export async function generate(
867892
root: string,
868893
opts: GenerateOptions,
869894
projects: Record<string, ProjectConfiguration>,
870-
verbose: boolean
895+
verbose: boolean,
896+
projectGraph: ProjectGraph
871897
) {
872898
const logger = getLogger(verbose);
873899
const fsHost = new NxScopeHostUsedForWrappedSchematics(
@@ -876,7 +902,8 @@ export async function generate(
876902
root,
877903
verbose,
878904
`ng-cli generator: ${opts.collectionName}:${opts.generatorName}`
879-
)
905+
),
906+
projectGraph
880907
);
881908
const workflow = createWorkflow(fsHost, root, opts, projects);
882909
const collection = getCollection(workflow, opts.collectionName);
@@ -961,7 +988,8 @@ export async function runMigration(
961988
packageName: string,
962989
migrationName: string,
963990
projects: Record<string, ProjectConfiguration>,
964-
isVerbose: boolean
991+
isVerbose: boolean,
992+
projectGraph: ProjectGraph
965993
) {
966994
const logger = getLogger(isVerbose);
967995
const fsHost = new NxScopeHostUsedForWrappedSchematics(
@@ -970,7 +998,8 @@ export async function runMigration(
970998
root,
971999
isVerbose,
9721000
`ng-cli migration: ${packageName}:${migrationName}`
973-
)
1001+
),
1002+
projectGraph
9741003
);
9751004
const workflow = createWorkflow(fsHost, root, {}, projects);
9761005
const collection = resolveMigrationsCollection(packageName);
@@ -1084,7 +1113,7 @@ export function wrapAngularDevkitSchematic(
10841113
host: Tree,
10851114
generatorOptions: { [k: string]: any }
10861115
): Promise<GeneratorCallback> => {
1087-
const graph = await createProjectGraphAsync();
1116+
const graph = await getProjectGraph();
10881117
const { projects } = readProjectsConfigurationFromProjectGraph(graph);
10891118

10901119
if (
@@ -1124,7 +1153,11 @@ export function wrapAngularDevkitSchematic(
11241153
}
11251154
};
11261155

1127-
const fsHost = new NxScopeHostUsedForWrappedSchematics(host.root, host);
1156+
const fsHost = new NxScopeHostUsedForWrappedSchematics(
1157+
host.root,
1158+
host,
1159+
graph
1160+
);
11281161

11291162
const logger = getLogger(generatorOptions.verbose);
11301163
const options = {

packages/nx/src/command-line/generate/generate.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,8 @@ export async function generate(args: { [k: string]: any }) {
420420
generatorOptions: combinedOpts,
421421
},
422422
projectsConfigurations.projects,
423-
args.verbose
423+
args.verbose,
424+
projectGraph
424425
);
425426
}
426427
});

packages/nx/src/command-line/migrate/migrate.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,13 +1669,14 @@ export async function runNxOrAngularMigration(
16691669
logger.info('');
16701670
} else {
16711671
const ngCliAdapter = await getNgCompatLayer();
1672+
const migrationProjectGraph = await createProjectGraphAsync();
16721673
const { madeChanges, loggingQueue } = await ngCliAdapter.runMigration(
16731674
root,
16741675
migration.package,
16751676
migration.name,
1676-
readProjectsConfigurationFromProjectGraph(await createProjectGraphAsync())
1677-
.projects,
1678-
isVerbose
1677+
readProjectsConfigurationFromProjectGraph(migrationProjectGraph).projects,
1678+
isVerbose,
1679+
migrationProjectGraph
16791680
);
16801681

16811682
logger.info(`Ran ${migration.name} from ${migration.package}`);

packages/nx/src/command-line/run/run.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,8 @@ async function runExecutorInternal<T extends { success: boolean }>(
220220
runOptions: combinedOptions,
221221
projects: projectsConfigurations.projects,
222222
},
223-
isVerbose
223+
isVerbose,
224+
projectGraph
224225
);
225226
const { eachValueFrom } = await import('../../adapter/rxjs-for-await');
226227
return eachValueFrom(observable as any);

0 commit comments

Comments
 (0)