Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion packages/angular/cli/src/commands/update/schematic/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -773,11 +773,31 @@ function _getAllDependencies(tree: Tree): Array<readonly [string, VersionRange]>
'/package.json',
) as PackageManifest;

return [
const allDeps = [
...(Object.entries(peerDependencies || {}) as Array<[string, VersionRange]>),
...(Object.entries(devDependencies || {}) as Array<[string, VersionRange]>),
...(Object.entries(dependencies || {}) as Array<[string, VersionRange]>),
];

// Resolve pnpm catalog: protocol references to actual version ranges.
// The catalog: protocol is not a valid semver range and must be resolved
// before downstream processing.
return allDeps.map(([name, specifier]) => {
if (!specifier.startsWith('catalog:')) {
return [name, specifier] as const;
}

// Fall back to the installed version from node_modules
const pkgJsonPath = `/node_modules/${name}/package.json`;
if (tree.exists(pkgJsonPath)) {
const { version } = tree.readJson(pkgJsonPath) as PackageManifest;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The call to tree.readJson could potentially throw an error if the package.json file is malformed. While tree.exists is checked, it does not guarantee valid JSON content. Consider wrapping this in a try-catch block to gracefully handle potential parsing errors, ensuring the update process isn't halted by a single corrupted metadata file.

if (version) {
return [name, `^${version}` as VersionRange] as const;
}
}

return [name, specifier] as const;
});
}

function _formatVersion(version: string | undefined) {
Expand All @@ -804,6 +824,11 @@ function _formatVersion(version: string | undefined) {
* @throws When the specifier cannot be parsed.
*/
function isPkgFromRegistry(name: string, specifier: string): boolean {
// pnpm catalog: protocol always references registry packages
if (specifier.startsWith('catalog:')) {
return true;
}

const result = npa.resolve(name, specifier);

return !!result.registry;
Expand Down
29 changes: 29 additions & 0 deletions packages/angular/cli/src/commands/update/schematic/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,35 @@ describe('@schematics/update', () => {
expect(dependencies['@angular-devkit-tests/update-base']).toBe('1.1.0');
});

it('should not error with pnpm catalog: protocol', async () => {
const newTree = new UnitTestTree(
new HostTree(
new virtualFs.test.TestHost({
'/package.json': `{
"name": "blah",
"dependencies": {
"@angular-devkit-tests/update-base": "catalog:"
}
}`,
'/node_modules/@angular-devkit-tests/update-base/package.json': `{
"name": "@angular-devkit-tests/update-base",
"version": "1.0.0"
}`,
}),
),
);

const resultTree = await schematicRunner.runSchematic(
'update',
{
packages: ['@angular-devkit-tests/update-base'],
},
newTree,
);
const { dependencies } = JSON.parse(resultTree.readContent('/package.json'));
expect(dependencies['@angular-devkit-tests/update-base']).toBe('1.1.0');
});

it('updates Angular as compatible with Angular N-1', async () => {
// Add the basic migration package.
const content = virtualFs.fileBufferToString(host.sync.read(normalize('/package.json')));
Expand Down
Loading