Skip to content

Commit 7e7de68

Browse files
alan-agius4dgp1130
authored andcommitted
feat(@schematics/angular): update Angular dependencies to use ^ as version prefix
With this change we update Angular dependencies to use `^` instead of `~` as version prefix. Closes #21925
1 parent 28b0cdc commit 7e7de68

File tree

4 files changed

+229
-3
lines changed

4 files changed

+229
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
{
2-
"schematics": {}
2+
"schematics": {
3+
"update-angular-packages-version-prefix": {
4+
"version": "14.0.0",
5+
"factory": "./update-14/angular-packages-version-prefix",
6+
"description": "Update Angular packages 'dependencies' and 'devDependencies' version prefix to '^' instead of '~'."
7+
}
8+
}
39
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import { Rule } from '@angular-devkit/schematics';
10+
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
11+
import { NodeDependencyType } from '../../utility/dependencies';
12+
import { JSONFile } from '../../utility/json-file';
13+
14+
const PACKAGES_REGEXP = /^@(?:angular|nguniversal|schematics|angular-devkit)\/|^ng-packagr$/;
15+
16+
/**
17+
* This migrations updates Angular packages 'dependencies' and 'devDependencies' version prefix to '^' instead of '~'.
18+
*
19+
* @example
20+
* **Before**
21+
* ```json
22+
* dependencies: {
23+
* "@angular/animations": "~13.1.0",
24+
* "@angular/common": "~13.1.0"
25+
* }
26+
* ```
27+
*
28+
* **After**
29+
* ```json
30+
* dependencies: {
31+
* "@angular/animations": "^13.1.0",
32+
* "@angular/common": "^13.1.0"
33+
* }
34+
* ```
35+
*/
36+
export default function (): Rule {
37+
return (tree, context) => {
38+
const json = new JSONFile(tree, '/package.json');
39+
updateVersionPrefixToTilde(json, NodeDependencyType.Default);
40+
updateVersionPrefixToTilde(json, NodeDependencyType.Dev);
41+
42+
context.addTask(new NodePackageInstallTask());
43+
};
44+
}
45+
46+
function updateVersionPrefixToTilde(json: JSONFile, dependencyType: NodeDependencyType): void {
47+
const dependencyTypePath = [dependencyType];
48+
const dependencies = json.get(dependencyTypePath);
49+
50+
if (!dependencies || typeof dependencies !== 'object') {
51+
return;
52+
}
53+
54+
const updatedDependencies = new Map<string, string>();
55+
for (const [name, version] of Object.entries(dependencies)) {
56+
if (typeof version === 'string' && version.charAt(0) === '~' && PACKAGES_REGEXP.test(name)) {
57+
updatedDependencies.set(name, `^${version.substring(1)}`);
58+
}
59+
}
60+
61+
if (updatedDependencies.size) {
62+
json.modify(dependencyTypePath, {
63+
...dependencies,
64+
...Object.fromEntries(updatedDependencies),
65+
});
66+
}
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
import { EmptyTree } from '@angular-devkit/schematics';
10+
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
11+
12+
describe('Migration to update Angular packages version prefix to `^` instead of `~`', () => {
13+
const packageJsonPath = '/package.json';
14+
const schematicName = 'update-angular-packages-version-prefix';
15+
const schematicRunner = new SchematicTestRunner(
16+
'migrations',
17+
require.resolve('../migration-collection.json'),
18+
);
19+
20+
let tree: UnitTestTree;
21+
beforeEach(() => {
22+
tree = new UnitTestTree(new EmptyTree());
23+
});
24+
25+
it(`should replace Angular packages versioned with '~' to '^'`, async () => {
26+
tree.create(
27+
packageJsonPath,
28+
JSON.stringify({
29+
dependencies: {
30+
'@angular/animations': '~13.1.0',
31+
'@angular/common': '~13.1.0',
32+
'@angular/compiler': '~13.1.0',
33+
'@angular/core': '~13.1.0',
34+
'@angular/forms': '~13.1.0',
35+
'@angular/platform-browser': '~13.1.0',
36+
'@angular/platform-browser-dynamic': '~13.1.0',
37+
'@angular/router': '~13.1.0',
38+
'@nguniversal/commom': '^13.1.0',
39+
'rxjs': '~7.4.0',
40+
'tslib': '^2.3.0',
41+
'zone.js': '~0.11.4',
42+
},
43+
devDependencies: {
44+
'@angular-devkit/build-angular': '~13.1.3',
45+
'@angular/cli': '~13.1.3',
46+
'@angular/compiler-cli': '~13.1.0',
47+
'@angular/localize': '^13.1.3',
48+
'@types/jasmine': '~3.10.0',
49+
'@types/node': '^12.11.1',
50+
'jasmine-core': '~3.10.0',
51+
'karma': '~6.3.0',
52+
'karma-chrome-launcher': '~3.1.0',
53+
'karma-coverage': '~2.1.0',
54+
'karma-jasmine': '~4.0.0',
55+
'karma-jasmine-html-reporter': '~1.7.0',
56+
'ng-packagr': '~13.1.3',
57+
'typescript': '~4.5.2',
58+
},
59+
}),
60+
);
61+
62+
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
63+
const pkg = JSON.parse(newTree.readContent(packageJsonPath));
64+
65+
expect(pkg['dependencies']).toEqual({
66+
'@angular/animations': '^13.1.0',
67+
'@angular/common': '^13.1.0',
68+
'@angular/compiler': '^13.1.0',
69+
'@angular/core': '^13.1.0',
70+
'@angular/forms': '^13.1.0',
71+
'@angular/platform-browser': '^13.1.0',
72+
'@angular/platform-browser-dynamic': '^13.1.0',
73+
'@angular/router': '^13.1.0',
74+
'@nguniversal/commom': '^13.1.0',
75+
'rxjs': '~7.4.0',
76+
'tslib': '^2.3.0',
77+
'zone.js': '~0.11.4',
78+
});
79+
80+
expect(pkg['devDependencies']).toEqual({
81+
'@angular-devkit/build-angular': '^13.1.3',
82+
'@angular/cli': '^13.1.3',
83+
'@angular/compiler-cli': '^13.1.0',
84+
'@angular/localize': '^13.1.3',
85+
'@types/jasmine': '~3.10.0',
86+
'@types/node': '^12.11.1',
87+
'jasmine-core': '~3.10.0',
88+
'karma': '~6.3.0',
89+
'karma-chrome-launcher': '~3.1.0',
90+
'karma-coverage': '~2.1.0',
91+
'karma-jasmine': '~4.0.0',
92+
'karma-jasmine-html-reporter': '~1.7.0',
93+
'ng-packagr': '^13.1.3',
94+
'typescript': '~4.5.2',
95+
});
96+
});
97+
98+
it('should not replace pinned Angular packages versions', async () => {
99+
tree.create(
100+
packageJsonPath,
101+
JSON.stringify({
102+
dependencies: {
103+
'@angular/animations': '13.1.0',
104+
'@angular/core': '~13.1.0',
105+
},
106+
devDependencies: {
107+
'@angular-devkit/build-angular': '13.1.3',
108+
},
109+
}),
110+
);
111+
112+
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
113+
const pkg = JSON.parse(newTree.readContent(packageJsonPath));
114+
115+
expect(pkg['dependencies']['@angular/animations']).toBe('13.1.0');
116+
expect(pkg['dependencies']['@angular/core']).toBe('^13.1.0');
117+
expect(pkg['devDependencies']['@angular-devkit/build-angular']).toBe('13.1.3');
118+
});
119+
120+
it('should not error when `dependencies` is missing', async () => {
121+
tree.create(
122+
packageJsonPath,
123+
JSON.stringify({
124+
devDependencies: {
125+
'@angular-devkit/build-angular': '~13.1.3',
126+
},
127+
}),
128+
);
129+
130+
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
131+
const pkg = JSON.parse(newTree.readContent(packageJsonPath));
132+
133+
expect(pkg['dependencies']).toBeUndefined();
134+
expect(pkg['devDependencies']['@angular-devkit/build-angular']).toBe('^13.1.3');
135+
});
136+
137+
it('should not error when `devDependencies` is missing', async () => {
138+
tree.create(
139+
packageJsonPath,
140+
JSON.stringify({
141+
dependencies: {
142+
'@angular-devkit/build-angular': '~13.1.3',
143+
},
144+
}),
145+
);
146+
147+
const newTree = await schematicRunner.runSchematicAsync(schematicName, {}, tree).toPromise();
148+
const pkg = JSON.parse(newTree.readContent(packageJsonPath));
149+
150+
expect(pkg['dependencies']['@angular-devkit/build-angular']).toBe('^13.1.3');
151+
expect(pkg['devDependencies']).toBeUndefined();
152+
});
153+
});

packages/schematics/angular/utility/latest-versions.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ export const latestVersions: Record<string, string> & {
1515
...require('./latest-versions/package.json')['dependencies'],
1616

1717
// As Angular CLI works with same minor versions of Angular Framework, a tilde match for the current
18-
Angular: '~13.2.0-next.0',
18+
Angular: '^13.2.0-next.0',
1919

2020
// Since @angular-devkit/build-angular and @schematics/angular are always
2121
// published together from the same monorepo, and they are both
2222
// non-experimental, they will always have the same version.
23-
DevkitBuildAngular: '~' + require('../package.json')['version'],
23+
DevkitBuildAngular: '^' + require('../package.json')['version'],
2424
};

0 commit comments

Comments
 (0)