Skip to content

Commit 94b9bb5

Browse files
committed
perf(@angular/cli): lazily initialize package manager in command context
This change refactors the `CommandContext` to defer the initialization of the `packageManager` instance until it is first accessed. This optimization avoids unnecessary configuration discovery and disk I/O for commands that do not require the package manager, such as `ng help`, and certain shell completions. As part of this refactoring: - The `packageManager` property in `CommandContext` is now a `Promise<PackageManager>`. - Internal command modules and utility functions have been updated to `await` the package manager's lazy initialization. - In `@angular-devkit/core`, `CoreSchemaRegistry` was updated to rename `_sourceMap` to `_sourceProvider` and optimize the resolution of smart defaults by awaiting values during the `_set` operation.
1 parent 2320ba8 commit 94b9bb5

File tree

12 files changed

+53
-42
lines changed

12 files changed

+53
-42
lines changed

goldens/public-api/angular_devkit/core/index.api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -982,7 +982,7 @@ interface SimpleMemoryHostStats {
982982
// @public (undocumented)
983983
interface SmartDefaultProvider<T> {
984984
// (undocumented)
985-
(schema: JsonObject): T | Observable<T>;
985+
(schema: JsonObject): T | Observable<T> | Promise<T>;
986986
}
987987

988988
// @public

packages/angular/cli/src/command-builder/architect-base-command-module.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ export abstract class ArchitectBaseCommandModule<T extends object>
189189
} catch (e) {
190190
assertIsError(e);
191191
if (e.code === 'MODULE_NOT_FOUND') {
192-
this.warnOnMissingNodeModules();
192+
await this.warnOnMissingNodeModules();
193193
throw new CommandModuleError(`Could not find the '${builderConf}' builder's node package.`);
194194
}
195195

@@ -203,7 +203,7 @@ export abstract class ArchitectBaseCommandModule<T extends object>
203203
);
204204
}
205205

206-
private warnOnMissingNodeModules(): void {
206+
private async warnOnMissingNodeModules(): Promise<void> {
207207
const basePath = this.context.workspace?.basePath;
208208
if (!basePath) {
209209
return;
@@ -218,7 +218,9 @@ export abstract class ArchitectBaseCommandModule<T extends object>
218218
} catch {}
219219

220220
this.context.logger.warn(
221-
`Node packages may not be installed. Try installing with '${this.context.packageManager.name} install'.`,
221+
`Node packages may not be installed. Try installing with '${
222+
(await this.context.packageManager).name
223+
} install'.`,
222224
);
223225
}
224226

packages/angular/cli/src/command-builder/command-module.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,12 @@ export abstract class CommandModule<T extends {} = {}> implements CommandModuleI
153153
['version', 'update', 'analytics'].includes(this.commandName),
154154
);
155155

156+
const packageManager = await this.context.packageManager;
157+
156158
return userId
157159
? new AnalyticsCollector(this.context.logger, userId, {
158-
name: this.context.packageManager.name,
159-
version: await this.context.packageManager.getVersion(),
160+
name: packageManager.name,
161+
version: await packageManager.getVersion(),
160162
})
161163
: undefined;
162164
}

packages/angular/cli/src/command-builder/command-runner.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
RootCommands,
1919
RootCommandsAliases,
2020
} from '../commands/command-config';
21-
import { createPackageManager } from '../package-managers';
21+
import { PackageManager, createPackageManager } from '../package-managers';
2222
import { ConfiguredPackageManagerInfo } from '../package-managers/factory';
2323
import { colors } from '../utilities/color';
2424
import { AngularWorkspace, getProjectByCwd, getWorkspace } from '../utilities/config';
@@ -65,28 +65,32 @@ export async function runCommand(args: string[], logger: logging.Logger): Promis
6565
}
6666

6767
const root = workspace?.basePath ?? process.cwd();
68-
const cacheConfig = workspace && getCacheConfig(workspace);
69-
const packageManager = await createPackageManager({
70-
cwd: root,
71-
logger,
72-
dryRun: dryRun || help || jsonHelp || getYargsCompletions,
73-
tempDirectory: cacheConfig?.enabled ? cacheConfig.path : undefined,
74-
configuredPackageManager: await getConfiguredPackageManager(
75-
root,
76-
workspace,
77-
globalConfiguration,
78-
),
79-
});
80-
8168
const localYargs = yargs(args);
69+
let packageManager: Promise<PackageManager> | undefined;
8270
const context: CommandContext = {
8371
globalConfiguration,
8472
workspace,
8573
logger,
8674
currentDirectory: process.cwd(),
8775
yargsInstance: localYargs,
8876
root,
89-
packageManager,
77+
get packageManager() {
78+
return (packageManager ??= (async () => {
79+
const cacheConfig = workspace && getCacheConfig(workspace);
80+
81+
return createPackageManager({
82+
cwd: root,
83+
logger,
84+
dryRun: dryRun || help || jsonHelp || getYargsCompletions,
85+
tempDirectory: cacheConfig?.enabled ? cacheConfig.path : undefined,
86+
configuredPackageManager: await getConfiguredPackageManager(
87+
root,
88+
workspace,
89+
globalConfiguration,
90+
),
91+
});
92+
})());
93+
},
9094
args: {
9195
positional: positional.map((v) => v.toString()),
9296
options: {

packages/angular/cli/src/command-builder/definitions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export interface CommandContext {
2828
workspace?: AngularWorkspace;
2929
globalConfiguration: AngularWorkspace;
3030
logger: logging.Logger;
31-
packageManager: PackageManager;
31+
readonly packageManager: Promise<PackageManager>;
3232
yargsInstance: Argv<{}>;
3333

3434
/** Arguments parsed in free-from without parser configuration. */

packages/angular/cli/src/command-builder/schematics-command-module.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ export abstract class SchematicsCommandModule
108108
collectionName: string,
109109
options: SchematicsExecutionOptions,
110110
): Promise<NodeWorkflow> {
111-
const { logger, root, packageManager } = this.context;
111+
const { logger, root } = this.context;
112+
const packageManager = await this.context.packageManager;
112113
const { force, dryRun, packageRegistry } = options;
113-
114114
const workflow = new NodeWorkflow(root, {
115115
force,
116116
dryRun,

packages/angular/cli/src/commands/add/cli.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,8 @@ export default class AddCommandModule
199199
[
200200
{
201201
title: 'Determining Package Manager',
202-
task: (_context, task) =>
203-
(task.output = `Using package manager: ${color.dim(this.context.packageManager.name)}`),
202+
task: async (_context, task) =>
203+
(task.output = `Using package manager: ${color.dim((await this.context.packageManager).name)}`),
204204
rendererOptions: { persistentOutput: true },
205205
},
206206
{
@@ -318,7 +318,7 @@ export default class AddCommandModule
318318
): Promise<void> {
319319
const { registry, verbose } = options;
320320
const { packageIdentifier } = context;
321-
const { packageManager } = this.context;
321+
const packageManager = await this.context.packageManager;
322322
const packageName = packageIdentifier.name;
323323

324324
assert(packageName, 'Registry package identifiers should always have a name.');
@@ -416,7 +416,7 @@ export default class AddCommandModule
416416
},
417417
): Promise<PackageManifest | null> {
418418
const { packageIdentifier } = context;
419-
const { packageManager } = this.context;
419+
const packageManager = await this.context.packageManager;
420420
const { registry, verbose, rejectionReasons } = options;
421421
const packageName = packageIdentifier.name;
422422
assert(packageName, 'Package name must be defined.');
@@ -494,7 +494,9 @@ export default class AddCommandModule
494494

495495
let manifest;
496496
try {
497-
manifest = await this.context.packageManager.getManifest(context.packageIdentifier, {
497+
manifest = await (
498+
await this.context.packageManager
499+
).getManifest(context.packageIdentifier, {
498500
registry,
499501
});
500502
} catch (e) {
@@ -567,7 +569,7 @@ export default class AddCommandModule
567569
): Promise<void> {
568570
const { registry } = options;
569571
const { packageIdentifier, savePackage } = context;
570-
const { packageManager } = this.context;
572+
const packageManager = await this.context.packageManager;
571573

572574
// Only show if installation will actually occur
573575
task.title = 'Installing package';

packages/angular/cli/src/commands/new/cli.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export default class NewCommandModule
7575
workflow.registry.addSmartDefaultProvider('ng-cli-version', () => VERSION.full);
7676
workflow.registry.addSmartDefaultProvider(
7777
'packageManager',
78-
() => this.context.packageManager.name,
78+
async () => (await this.context.packageManager).name,
7979
);
8080

8181
return this.runSchematic({

packages/angular/cli/src/commands/update/cli.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,8 @@ export default class UpdateCommandModule extends CommandModule<UpdateCommandArgs
163163
}
164164

165165
async run(options: Options<UpdateCommandArgs>): Promise<number | void> {
166-
const { logger, packageManager } = this.context;
166+
const { logger } = this.context;
167+
const packageManager = await this.context.packageManager;
167168

168169
// Check if the current installed CLI version is older than the latest compatible version.
169170
// Skip when running `ng update` without a package name as this will not trigger an actual update.
@@ -517,7 +518,7 @@ export default class UpdateCommandModule extends CommandModule<UpdateCommandArgs
517518
verbose: options.verbose,
518519
force: options.force,
519520
next: options.next,
520-
packageManager: this.context.packageManager.name,
521+
packageManager: (await this.context.packageManager).name,
521522
packages: packagesToUpdate,
522523
},
523524
);

packages/angular/cli/src/commands/version/version-info.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ export async function gatherVersionInfo(context: CommandContext): Promise<Versio
121121
architecture: process.arch,
122122
},
123123
packageManager: {
124-
name: context.packageManager.name,
125-
version: await context.packageManager.getVersion(),
124+
name: (await context.packageManager).name,
125+
version: await (await context.packageManager).getVersion(),
126126
},
127127
},
128128
packages,

0 commit comments

Comments
 (0)