Skip to content

Commit 6de7103

Browse files
committed
refactor(@angular/cli): emove old package manager utilities
This removes the old implementation of the package-manager.
1 parent d9cd609 commit 6de7103

File tree

8 files changed

+141
-398
lines changed

8 files changed

+141
-398
lines changed

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

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ import { Parser as yargsParser } from 'yargs/helpers';
1919
import { getAnalyticsUserId } from '../analytics/analytics';
2020
import { AnalyticsCollector } from '../analytics/analytics-collector';
2121
import { EventCustomDimension, EventCustomMetric } from '../analytics/analytics-parameters';
22+
import { PackageManager } from '../package-managers';
2223
import { considerSettingUpAutocompletion } from '../utilities/completion';
2324
import { AngularWorkspace } from '../utilities/config';
2425
import { memoize } from '../utilities/memoize';
25-
import { PackageManagerUtils } from '../utilities/package-manager';
2626
import { Option, addSchemaOptionsToCommand } from './utilities/json-schema';
2727

2828
export type Options<T> = { [key in keyof T as CamelCaseKey<key>]: T[key] };
@@ -39,16 +39,16 @@ export enum CommandScope {
3939
}
4040

4141
export interface CommandContext {
42-
currentDirectory: string;
43-
root: string;
44-
workspace?: AngularWorkspace;
45-
globalConfiguration: AngularWorkspace;
46-
logger: logging.Logger;
47-
packageManager: PackageManagerUtils;
48-
yargsInstance: Argv<{}>;
42+
readonly currentDirectory: string;
43+
readonly root: string;
44+
readonly workspace?: AngularWorkspace;
45+
readonly globalConfiguration: AngularWorkspace;
46+
readonly logger: logging.Logger;
47+
readonly packageManager: PackageManager;
48+
readonly yargsInstance: Argv<{}>;
4949

5050
/** Arguments parsed in free-from without parser configuration. */
51-
args: {
51+
readonly args: {
5252
positional: string[];
5353
options: {
5454
help: boolean;
@@ -60,8 +60,10 @@ export interface CommandContext {
6060

6161
export type OtherOptions = Record<string, unknown>;
6262

63-
export interface CommandModuleImplementation<T extends {} = {}>
64-
extends Omit<YargsCommandModule<{}, T>, 'builder' | 'handler'> {
63+
export interface CommandModuleImplementation<T extends {} = {}> extends Omit<
64+
YargsCommandModule<{}, T>,
65+
'builder' | 'handler'
66+
> {
6567
/** Scope in which the command can be executed in. */
6668
scope: CommandScope;
6769

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

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9-
import { logging } from '@angular-devkit/core';
9+
import { JsonValue, isJsonObject, logging } from '@angular-devkit/core';
10+
import { readFile } from 'node:fs/promises';
11+
import { join } from 'node:path';
1012
import yargs from 'yargs';
1113
import { Parser as yargsParser } from 'yargs/helpers';
1214
import {
@@ -15,10 +17,10 @@ import {
1517
RootCommands,
1618
RootCommandsAliases,
1719
} from '../commands/command-config';
20+
import { PackageManagerName, createPackageManager } from '../package-managers';
1821
import { colors } from '../utilities/color';
19-
import { AngularWorkspace, getWorkspace } from '../utilities/config';
22+
import { AngularWorkspace, getProjectByCwd, getWorkspace } from '../utilities/config';
2023
import { assertIsError } from '../utilities/error';
21-
import { PackageManagerUtils } from '../utilities/package-manager';
2224
import { VERSION } from '../utilities/version';
2325
import { CommandContext, CommandModuleError } from './command-module';
2426
import {
@@ -34,11 +36,12 @@ export async function runCommand(args: string[], logger: logging.Logger): Promis
3436
$0,
3537
_,
3638
help = false,
39+
dryRun = false,
3740
jsonHelp = false,
3841
getYargsCompletions = false,
3942
...rest
4043
} = yargsParser(args, {
41-
boolean: ['help', 'json-help', 'get-yargs-completions'],
44+
boolean: ['help', 'json-help', 'get-yargs-completions', 'dry-run'],
4245
alias: { 'collection': 'c' },
4346
});
4447

@@ -62,14 +65,25 @@ export async function runCommand(args: string[], logger: logging.Logger): Promis
6265
const root = workspace?.basePath ?? process.cwd();
6366
const localYargs = yargs(args);
6467

68+
const packageManager = await createPackageManager({
69+
cwd: root,
70+
logger,
71+
dryRun,
72+
configuredPackageManager: await getConfiguredPackageManager(
73+
root,
74+
workspace,
75+
globalConfiguration,
76+
),
77+
});
78+
6579
const context: CommandContext = {
6680
globalConfiguration,
6781
workspace,
6882
logger,
6983
currentDirectory: process.cwd(),
7084
yargsInstance: localYargs,
7185
root,
72-
packageManager: new PackageManagerUtils({ globalConfiguration, workspace, root }),
86+
packageManager,
7387
args: {
7488
positional: positional.map((v) => v.toString()),
7589
options: {
@@ -163,3 +177,58 @@ async function getCommandsToRegister(
163177

164178
return Promise.all(commands.map((command) => command.factory().then((m) => m.default)));
165179
}
180+
181+
/**
182+
* Gets the configured package manager by checking package.json, or the local and global angular.json files.
183+
*
184+
* @param root The root directory of the workspace.
185+
* @param localWorkspace The local workspace.
186+
* @param globalWorkspace The global workspace.
187+
* @returns The package manager name.
188+
*/
189+
async function getConfiguredPackageManager(
190+
root: string,
191+
localWorkspace: AngularWorkspace | undefined,
192+
globalWorkspace: AngularWorkspace,
193+
): Promise<PackageManagerName | undefined> {
194+
let result: PackageManagerName | undefined;
195+
196+
try {
197+
const packageJsonPath = join(root, 'package.json');
198+
const pkgJson = JSON.parse(await readFile(packageJsonPath, 'utf-8')) as JsonValue;
199+
result = getPackageManager(pkgJson);
200+
} catch {}
201+
202+
if (result) {
203+
return result;
204+
}
205+
206+
if (localWorkspace) {
207+
const project = getProjectByCwd(localWorkspace);
208+
if (project) {
209+
result = getPackageManager(localWorkspace.projects.get(project)?.extensions['cli']);
210+
}
211+
212+
result ??= getPackageManager(localWorkspace.extensions['cli']);
213+
}
214+
215+
result ??= getPackageManager(globalWorkspace.extensions['cli']);
216+
217+
return result;
218+
}
219+
220+
/**
221+
* Get the package manager name from a JSON value.
222+
* @param source The JSON value to get the package manager name from.
223+
* @returns The package manager name.
224+
*/
225+
function getPackageManager(source: JsonValue | undefined): PackageManagerName | undefined {
226+
if (source && isJsonObject(source)) {
227+
const value = source['packageManager'];
228+
if (typeof value === 'string') {
229+
return value.split('@', 1)[0] as unknown as PackageManagerName;
230+
}
231+
}
232+
233+
return undefined;
234+
}

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

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,9 @@ import {
2525
} from '../../command-builder/schematics-command-module';
2626
import {
2727
NgAddSaveDependency,
28-
PackageManager,
2928
PackageManagerError,
3029
PackageManifest,
3130
PackageMetadata,
32-
createPackageManager,
3331
} from '../../package-managers';
3432
import { assertIsError } from '../../utilities/error';
3533
import { isTTY } from '../../utilities/tty';
@@ -46,7 +44,6 @@ interface AddCommandArgs extends SchematicsCommandArgs {
4644
}
4745

4846
interface AddCommandTaskContext {
49-
packageManager: PackageManager;
5047
packageIdentifier: npa.Result;
5148
savePackage?: NgAddSaveDependency;
5249
collectionName?: string;
@@ -334,13 +331,9 @@ export default class AddCommandModule
334331
} catch {}
335332
}
336333

337-
context.packageManager = await createPackageManager({
338-
cwd: this.context.root,
339-
logger: this.context.logger,
340-
dryRun: context.dryRun,
341-
tempDirectory,
342-
});
343-
task.output = `Using package manager: ${color.dim(context.packageManager.name)}`;
334+
this.context.packageManager.temporaryDirectory = tempDirectory;
335+
336+
task.output = `Using package manager: ${color.dim(this.context.packageManager.name)}`;
344337
}
345338

346339
private async findCompatiblePackageVersionTask(
@@ -349,7 +342,8 @@ export default class AddCommandModule
349342
options: Options<AddCommandArgs>,
350343
): Promise<void> {
351344
const { registry, verbose } = options;
352-
const { packageManager, packageIdentifier } = context;
345+
const { packageIdentifier } = context;
346+
const { packageManager } = this.context;
353347
const packageName = packageIdentifier.name;
354348

355349
assert(packageName, 'Registry package identifiers should always have a name.');
@@ -446,7 +440,8 @@ export default class AddCommandModule
446440
rejectionReasons: string[];
447441
},
448442
): Promise<PackageManifest | null> {
449-
const { packageManager, packageIdentifier } = context;
443+
const { packageIdentifier } = context;
444+
const { packageManager } = this.context;
450445
const { registry, verbose, rejectionReasons } = options;
451446
const packageName = packageIdentifier.name;
452447
assert(packageName, 'Package name must be defined.');
@@ -524,9 +519,12 @@ export default class AddCommandModule
524519

525520
let manifest;
526521
try {
527-
manifest = await context.packageManager.getManifest(context.packageIdentifier.toString(), {
528-
registry,
529-
});
522+
manifest = await this.context.packageManager.getManifest(
523+
context.packageIdentifier.toString(),
524+
{
525+
registry,
526+
},
527+
);
530528
} catch (e) {
531529
assertIsError(e);
532530
throw new CommandError(
@@ -585,7 +583,8 @@ export default class AddCommandModule
585583
options: Options<AddCommandArgs>,
586584
): Promise<void> {
587585
const { registry } = options;
588-
const { packageManager, packageIdentifier, savePackage } = context;
586+
const { packageIdentifier, savePackage } = context;
587+
const { packageManager } = this.context;
589588

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

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

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,7 @@ import {
2020
Options,
2121
} from '../../command-builder/command-module';
2222
import { SchematicEngineHost } from '../../command-builder/utilities/schematic-engine-host';
23-
import {
24-
InstalledPackage,
25-
PackageManager,
26-
PackageManifest,
27-
createPackageManager,
28-
} from '../../package-managers';
23+
import { InstalledPackage, PackageManager, PackageManifest } from '../../package-managers';
2924
import { colors } from '../../utilities/color';
3025
import { disableVersionCheck } from '../../utilities/environment-options';
3126
import { assertIsError } from '../../utilities/error';
@@ -169,13 +164,7 @@ export default class UpdateCommandModule extends CommandModule<UpdateCommandArgs
169164
}
170165

171166
async run(options: Options<UpdateCommandArgs>): Promise<number | void> {
172-
const { logger } = this.context;
173-
// Instantiate the package manager
174-
const packageManager = await createPackageManager({
175-
cwd: this.context.root,
176-
logger,
177-
configuredPackageManager: this.context.packageManager.name,
178-
});
167+
const { logger, packageManager } = this.context;
179168

180169
// Check if the current installed CLI version is older than the latest compatible version.
181170
// Skip when running `ng update` without a package name as this will not trigger an actual update.
@@ -247,7 +236,7 @@ export default class UpdateCommandModule extends CommandModule<UpdateCommandArgs
247236

248237
const workflow = new NodeWorkflow(this.context.root, {
249238
packageManager: packageManager.name,
250-
packageManagerForce: await shouldForcePackageManager(packageManager, logger, options.verbose),
239+
packageManagerForce: shouldForcePackageManager(packageManager, logger, options.verbose),
251240
// __dirname -> favor @schematics/update from this package
252241
// Otherwise, use packages from the active workspace (migrations)
253242
resolvePaths: this.resolvePaths,
@@ -537,7 +526,7 @@ export default class UpdateCommandModule extends CommandModule<UpdateCommandArgs
537526

538527
if (success) {
539528
const { root: commandRoot } = this.context;
540-
const ignorePeerDependencies = await shouldForcePackageManager(
529+
const ignorePeerDependencies = shouldForcePackageManager(
541530
packageManager,
542531
logger,
543532
options.verbose,

packages/angular/cli/src/commands/update/utilities/cli-version.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,16 +175,16 @@ export async function runTempBinary(
175175
* @param verbose Whether to log verbose output.
176176
* @returns True if the package manager should be forced, false otherwise.
177177
*/
178-
export async function shouldForcePackageManager(
178+
export function shouldForcePackageManager(
179179
packageManager: PackageManager,
180180
logger: logging.LoggerApi,
181181
verbose: boolean,
182-
): Promise<boolean> {
182+
): boolean {
183183
// npm 7+ can fail due to it incorrectly resolving peer dependencies that have valid SemVer
184184
// ranges during an update. Update will set correct versions of dependencies within the
185185
// package.json file. The force option is set to workaround these errors.
186186
if (packageManager.name === 'npm') {
187-
const version = await packageManager.getVersion();
187+
const version = packageManager.version;
188188
if (semver.gte(version, '7.0.0')) {
189189
if (verbose) {
190190
logger.info('NPM 7+ detected -- enabling force option for package installation');

packages/angular/cli/src/package-managers/factory.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* found in the LICENSE file at https://angular.dev/license
77
*/
88

9+
import assert from 'node:assert';
910
import { major } from 'semver';
1011
import { discover } from './discovery';
1112
import { Host, NodeJS_HOST } from './host';
@@ -110,9 +111,8 @@ export async function createPackageManager(options: {
110111
configuredPackageManager?: PackageManagerName;
111112
logger?: Logger;
112113
dryRun?: boolean;
113-
tempDirectory?: string;
114114
}): Promise<PackageManager> {
115-
const { cwd, configuredPackageManager, logger, dryRun, tempDirectory } = options;
115+
const { cwd, configuredPackageManager, logger, dryRun } = options;
116116
const host = NodeJS_HOST;
117117

118118
const { name, source } = await determinePackageManager(
@@ -131,13 +131,12 @@ export async function createPackageManager(options: {
131131
const packageManager = new PackageManager(host, cwd, descriptor, {
132132
dryRun,
133133
logger,
134-
tempDirectory,
135134
});
136135

137136
// Do not verify if the package manager is installed during a dry run.
138137
if (!dryRun) {
139138
try {
140-
await packageManager.getVersion();
139+
assert(packageManager.version);
141140
} catch {
142141
if (source === 'default') {
143142
throw new Error(

0 commit comments

Comments
 (0)