Skip to content

Commit d0cd9c0

Browse files
devversionmmalerba
authored andcommitted
fix(schematics): do not run migrations multiple times (#15424)
Currently when someone runs the update schematic within a CLI project, the migration could be executed multiple times for the same `tsconfig` path This can happen because the `getProjectTsConfigPaths` function _can_ incorrectly return the same tsconfig multiple times. The paths are not properly deduped as we don't normalize the determined project tsconfig paths. e.g. `/tsconfig.json` exists and the common tsconfig path is detected. At the same time the workspace configuration specifies `tsconfig.json` in the project options. Both paths refer to the same tsconfig project, but are different strings and therefore aren't filtered out by the `Set` which contains all possible tsconfig locations --> migrations are executed for both paths.
1 parent 10968cd commit d0cd9c0

File tree

2 files changed

+29
-42
lines changed

2 files changed

+29
-42
lines changed

src/cdk/schematics/ng-update/upgrade-rules/project-tsconfig-paths.spec.ts

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {UnitTestTree} from '@angular-devkit/schematics/testing';
33
import {getProjectTsConfigPaths} from './project-tsconfig-paths';
44

55
describe('ng-update project-tsconfig-paths', () => {
6-
76
let testTree: UnitTestTree;
87

98
beforeEach(() => {
@@ -13,47 +12,37 @@ describe('ng-update project-tsconfig-paths', () => {
1312
it('should detect build tsconfig path inside of angular.json file', () => {
1413
testTree.create('/my-custom-config.json', '');
1514
testTree.create('/angular.json', JSON.stringify({
16-
projects: {
17-
my_name: {
18-
architect: {
19-
build: {
20-
options: {
21-
tsConfig: './my-custom-config.json'
22-
}
23-
}
24-
}
25-
}
26-
}
15+
projects: {my_name: {architect: {build: {options: {tsConfig: './my-custom-config.json'}}}}}
2716
}));
2817

29-
expect(getProjectTsConfigPaths(testTree)).toEqual(['./my-custom-config.json']);
18+
expect(getProjectTsConfigPaths(testTree)).toEqual(['my-custom-config.json']);
3019
});
3120

3221
it('should detect test tsconfig path inside of .angular.json file', () => {
3322
testTree.create('/my-test-config.json', '');
3423
testTree.create('/.angular.json', JSON.stringify({
35-
projects: {
36-
with_tests: {
37-
architect: {
38-
test: {
39-
options: {
40-
tsConfig: './my-test-config.json'
41-
}
42-
}
43-
}
44-
}
45-
}
24+
projects: {with_tests: {architect: {test: {options: {tsConfig: './my-test-config.json'}}}}}
4625
}));
4726

48-
expect(getProjectTsConfigPaths(testTree)).toEqual(['./my-test-config.json']);
27+
expect(getProjectTsConfigPaths(testTree)).toEqual(['my-test-config.json']);
4928
});
5029

5130
it('should detect common tsconfigs if no workspace config could be found', () => {
5231
testTree.create('/tsconfig.json', '');
5332
testTree.create('/src/tsconfig.json', '');
5433
testTree.create('/src/tsconfig.app.json', '');
5534

56-
expect(getProjectTsConfigPaths(testTree))
57-
.toEqual(['./tsconfig.json', './src/tsconfig.json', './src/tsconfig.app.json']);
35+
expect(getProjectTsConfigPaths(testTree)).toEqual([
36+
'tsconfig.json', 'src/tsconfig.json', 'src/tsconfig.app.json'
37+
]);
38+
});
39+
40+
it('should not return duplicate tsconfig files', () => {
41+
testTree.create('/tsconfig.json', '');
42+
testTree.create('/.angular.json', JSON.stringify({
43+
projects: {app: {architect: {test: {options: {tsConfig: 'tsconfig.json'}}}}}
44+
}));
45+
46+
expect(getProjectTsConfigPaths(testTree)).toEqual(['tsconfig.json']);
5847
});
5948
});

src/cdk/schematics/ng-update/upgrade-rules/project-tsconfig-paths.ts

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,22 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import {normalize} from '@angular-devkit/core';
910
import {Tree} from '@angular-devkit/schematics';
1011

12+
/** Name of the default Angular CLI workspace configuration files. */
13+
const defaultWorkspaceConfigPaths = ['/angular.json', '/.angular.json'];
14+
1115
/**
1216
* Gets all tsconfig paths from a CLI project by reading the workspace configuration
1317
* and looking for common tsconfig locations.
1418
*/
1519
export function getProjectTsConfigPaths(tree: Tree): string[] {
1620
// Start with some tsconfig paths that are generally used within CLI projects.
1721
const tsconfigPaths = new Set<string>([
18-
'./tsconfig.json',
19-
'./src/tsconfig.json',
20-
'./src/tsconfig.app.json',
22+
'tsconfig.json',
23+
'src/tsconfig.json',
24+
'src/tsconfig.app.json',
2125
]);
2226

2327
// Add any tsconfig directly referenced in a build or test task of the angular.json workspace.
@@ -26,18 +30,15 @@ export function getProjectTsConfigPaths(tree: Tree): string[] {
2630
if (workspace) {
2731
for (const project of Object.values<any>(workspace.projects)) {
2832
['build', 'test'].forEach(targetName => {
29-
if (project.targets &&
30-
project.targets[targetName] &&
31-
project.targets[targetName].options &&
33+
if (project.targets && project.targets[targetName] && project.targets[targetName].options &&
3234
project.targets[targetName].options.tsConfig) {
33-
tsconfigPaths.add(project.targets[targetName].options.tsConfig);
35+
tsconfigPaths.add(normalize(project.targets[targetName].options.tsConfig));
3436
}
3537

36-
if (project.architect &&
37-
project.architect[targetName] &&
38+
if (project.architect && project.architect[targetName] &&
3839
project.architect[targetName].options &&
3940
project.architect[targetName].options.tsConfig) {
40-
tsconfigPaths.add(project.architect[targetName].options.tsConfig);
41+
tsconfigPaths.add(normalize(project.architect[targetName].options.tsConfig));
4142
}
4243
});
4344
}
@@ -47,18 +48,15 @@ export function getProjectTsConfigPaths(tree: Tree): string[] {
4748
return Array.from(tsconfigPaths).filter(p => tree.exists(p));
4849
}
4950

50-
/** Name of the default Angular CLI workspace configuration files. */
51-
const defaultWorkspaceConfigPaths = ['/angular.json', '/.angular.json'];
52-
5351
/**
5452
* Resolve the workspace configuration of the specified tree gracefully. We cannot use the utility
5553
* functions from the default Angular schematics because those might not be present in older
5654
* versions of the CLI. Also it's important to resolve the workspace gracefully because
5755
* the CLI project could be still using `.angular-cli.json` instead of thew new config.
5856
*/
5957
function getWorkspaceConfigGracefully(tree: Tree): any {
60-
const path = defaultWorkspaceConfigPaths.filter(filePath => tree.exists(filePath))[0];
61-
const configBuffer = tree.read(path);
58+
const path = defaultWorkspaceConfigPaths.find(filePath => tree.exists(filePath));
59+
const configBuffer = tree.read(path!);
6260

6361
if (!path || !configBuffer) {
6462
return null;

0 commit comments

Comments
 (0)