Skip to content

Commit d7feb2f

Browse files
committed
refactor(@angular/cli): correctly handle yarn classic tag manifest fetching
Introduces a `requiresManifestTagLookup` property to `PackageManagerDescriptor` to control whether a package manager needs an explicit metadata lookup for tags (or when no `fetchSpec` is provided) before attempting to fetch the full registry manifest. This change optimizes manifest fetching by enabling a preliminary metadata lookup for package managers like `yarn-classic` that require it to resolve tags to concrete versions.
1 parent 94d157f commit d7feb2f

File tree

3 files changed

+28
-7
lines changed

3 files changed

+28
-7
lines changed

packages/angular/cli/src/package-managers/package-manager-descriptor.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
parseNpmLikeManifest,
2121
parseNpmLikeMetadata,
2222
parseYarnClassicDependencies,
23-
parseYarnLegacyManifest,
23+
parseYarnClassicManifest,
2424
parseYarnModernDependencies,
2525
} from './parsers';
2626

@@ -73,6 +73,9 @@ export interface PackageManagerDescriptor {
7373
/** The command to fetch the registry manifest of a package. */
7474
readonly getManifestCommand: readonly string[];
7575

76+
/** Whether a tag needs a lookup prior to fetching a registry manifest. */
77+
readonly requiresManifestTagLookup?: boolean;
78+
7679
/** A function that formats the arguments for field-filtered registry views. */
7780
readonly viewCommandFieldArgFormatter?: (fields: readonly string[]) => string[];
7881

@@ -166,9 +169,10 @@ export const SUPPORTED_PACKAGE_MANAGERS = {
166169
versionCommand: ['--version'],
167170
listDependenciesCommand: ['list', '--depth=0', '--json'],
168171
getManifestCommand: ['info', '--json'],
172+
requiresManifestTagLookup: true,
169173
outputParsers: {
170174
listDependencies: parseYarnClassicDependencies,
171-
getRegistryManifest: parseYarnLegacyManifest,
175+
getRegistryManifest: parseYarnClassicManifest,
172176
getRegistryMetadata: parseNpmLikeMetadata,
173177
},
174178
},

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,14 @@ export class PackageManager {
156156
return { stdout: '', stderr: '' };
157157
}
158158

159-
return this.host.runCommand(this.descriptor.binary, finalArgs, {
159+
const commandResult = await this.host.runCommand(this.descriptor.binary, finalArgs, {
160160
...runOptions,
161161
cwd: executionDirectory,
162162
stdio: 'pipe',
163163
env: finalEnv,
164164
});
165+
166+
return { stdout: commandResult.stdout.trim(), stderr: commandResult.stderr.trim() };
165167
}
166168

167169
/**
@@ -395,13 +397,26 @@ export class PackageManager {
395397
switch (type) {
396398
case 'range':
397399
case 'version':
398-
case 'tag':
400+
case 'tag': {
399401
if (!name) {
400402
throw new Error(`Could not parse package name from specifier: ${specifier}`);
401403
}
402404

403405
// `fetchSpec` is the version, range, or tag.
404-
return this.getRegistryManifest(name, fetchSpec ?? 'latest', options);
406+
let versionSpec = fetchSpec ?? 'latest';
407+
if ((type === 'tag' || !fetchSpec) && this.descriptor.requiresManifestTagLookup) {
408+
const metadata = await this.getRegistryMetadata(name, options);
409+
if (!metadata) {
410+
return null;
411+
}
412+
versionSpec = metadata['dist-tags'][versionSpec];
413+
if (!versionSpec) {
414+
return null;
415+
}
416+
}
417+
418+
return this.getRegistryManifest(name, versionSpec, options);
419+
}
405420
case 'directory': {
406421
if (!fetchSpec) {
407422
throw new Error(`Could not parse directory path from specifier: ${specifier}`);

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ export function parseNpmLikeMetadata(stdout: string, logger?: Logger): PackageMe
274274
* @param logger An optional logger instance.
275275
* @returns The package manifest object.
276276
*/
277-
export function parseYarnLegacyManifest(stdout: string, logger?: Logger): PackageManifest | null {
277+
export function parseYarnClassicManifest(stdout: string, logger?: Logger): PackageManifest | null {
278278
logger?.debug(`Parsing yarn classic manifest...`);
279279
logStdout(stdout, logger);
280280

@@ -287,5 +287,7 @@ export function parseYarnLegacyManifest(stdout: string, logger?: Logger): Packag
287287
const data = JSON.parse(stdout);
288288

289289
// Yarn classic wraps the manifest in a `data` property.
290-
return data.data ?? data;
290+
const manifest = data.data as PackageManifest;
291+
292+
return manifest;
291293
}

0 commit comments

Comments
 (0)