Skip to content

Commit 363fd37

Browse files
committed
fix(@angular/cli): ensure group members are updated to targeted version
When performing an update, all members of a package group should be updated to the same version as the targeted packages in that group, even if they are not explicitly passed as arguments to the command. This change separates the package group expansion and peer dependency resolution into two distinct loops. By completing the package group expansion first, we ensure that all group members are added to the update list with their group-synced versions before Evaluate peer dependencies. This prevents cases where a peer dependency might be pulled in at a newer (latest) version before its membership in a targeted group update is recognized. Closes angular#32576
1 parent 4fec6bf commit 363fd37

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,13 @@ export default function (options: UpdateSchema): Rule {
886886
lastPackagesSize = packages.size;
887887
npmPackageJsonMap.forEach((npmPackageJson) => {
888888
_addPackageGroup(tree, packages, npmDeps, npmPackageJson, logger);
889+
});
890+
} while (packages.size > lastPackagesSize);
891+
892+
// This is done in seperate loop to ensure that package groups are added before peer dependencies.
893+
do {
894+
lastPackagesSize = packages.size;
895+
npmPackageJsonMap.forEach((npmPackageJson) => {
889896
_addPeerDependencies(tree, packages, npmDeps, npmPackageJson, npmPackageJsonMap, logger);
890897
});
891898
} while (packages.size > lastPackagesSize);

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

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,4 +335,57 @@ describe('@schematics/update', () => {
335335
const resultTreeContent = resultTree.readContent('/package.json');
336336
expect(resultTreeContent.endsWith('}')).toBeTrue();
337337
});
338+
339+
it('updates group members to the same version as the targeted package', async () => {
340+
const packageJsonContent = `{
341+
"name": "test",
342+
"dependencies": {
343+
"@angular/cdk": "^19.2.19",
344+
"@angular/common": "^19.2.0",
345+
"@angular/compiler": "^19.2.0",
346+
"@angular/core": "^19.2.0",
347+
"@angular/forms": "^19.2.0",
348+
"@angular/platform-browser": "^19.2.0",
349+
"@angular/platform-browser-dynamic": "^19.2.0",
350+
"@angular/router": "^19.2.0",
351+
"rxjs": "~7.8.0",
352+
"tslib": "^2.3.0",
353+
"zone.js": "~0.15.0"
354+
},
355+
"devDependencies": {
356+
"@angular-devkit/build-angular": "^19.2.21",
357+
"@angular/cli": "^19.2.21",
358+
"@angular/compiler-cli": "^19.2.0",
359+
"typescript": "~5.7.2"
360+
}
361+
}`;
362+
363+
const inputTree = new UnitTestTree(
364+
new HostTree(
365+
new virtualFs.test.TestHost({
366+
'/package.json': packageJsonContent,
367+
}),
368+
),
369+
);
370+
371+
const resultTree = await schematicRunner.runSchematic(
372+
'update',
373+
{ force: true, packages: ['@angular/cli@20', '@angular/cdk@20', '@angular/core@20'] },
374+
inputTree,
375+
);
376+
377+
const { devDependencies, dependencies } = resultTree.readJson('/package.json') as {
378+
devDependencies: Record<string, string>;
379+
dependencies: Record<string, string>;
380+
};
381+
382+
const version20Regexp = /^\^20.\d+.\d+$/;
383+
384+
expect(devDependencies['typescript']).toMatch(/5\.9\.\d+/);
385+
expect(devDependencies['@angular/cli']).toMatch(version20Regexp);
386+
expect(devDependencies['@angular/compiler-cli']).toMatch(version20Regexp);
387+
expect(dependencies['@angular/cdk']).toMatch(version20Regexp);
388+
expect(dependencies['@angular/common']).toMatch(version20Regexp);
389+
expect(dependencies['@angular/core']).toMatch(version20Regexp);
390+
});
338391
});

0 commit comments

Comments
 (0)