Skip to content

Commit f10f8b9

Browse files
devversionjelbourn
authored andcommitted
fix(ng-update): do not fail if @schematics/angular version is outdated (#13929)
* Currently the `ng update` could potentially fail if the version of `@schematics/angular` is outdated on the given project. We should avoid to depend on `@schematics/angular` for the `ng-update` schematic. * No longer fails if the workspace configuration could not be found. It's very uncommon that someone tries to update a CLI project that still uses `.angular-cli.json` because: 1) The CLI version for the project doesn't support `ng update` with custom downstream migrations 2) In case the developer runs a different CLI version for _that_ outdated project, the CLI technically does run for an invalid project. Even though, this is a rare case, it doesn't hurt not throwing if the workspace configuration couldn't be found when running `ng update`.
1 parent 1a9472e commit f10f8b9

File tree

2 files changed

+103
-19
lines changed

2 files changed

+103
-19
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import {HostTree} from '@angular-devkit/schematics';
2+
import {UnitTestTree} from '@angular-devkit/schematics/testing';
3+
import {getProjectTsConfigPaths} from './project-tsconfig-paths';
4+
5+
describe('ng-update project-tsconfig-paths', () => {
6+
7+
let testTree: UnitTestTree;
8+
9+
beforeEach(() => {
10+
testTree = new UnitTestTree(new HostTree());
11+
});
12+
13+
it('should detect build tsconfig path inside of angular.json file', () => {
14+
testTree.create('/my-custom-config.json', '');
15+
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+
}
27+
}));
28+
29+
expect(getProjectTsConfigPaths(testTree)).toEqual(['./my-custom-config.json']);
30+
});
31+
32+
it('should detect test tsconfig path inside of .angular.json file', () => {
33+
testTree.create('/my-test-config.json', '');
34+
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+
}
46+
}));
47+
48+
expect(getProjectTsConfigPaths(testTree)).toEqual(['./my-test-config.json']);
49+
});
50+
51+
it('should detect common tsconfigs if no workspace config could be found', () => {
52+
testTree.create('/tsconfig.json', '');
53+
testTree.create('/src/tsconfig.json', '');
54+
testTree.create('/src/tsconfig.app.json', '');
55+
56+
expect(getProjectTsConfigPaths(testTree))
57+
.toEqual(['./tsconfig.json', './src/tsconfig.json', './src/tsconfig.app.json']);
58+
});
59+
});

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

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
*/
88

99
import {Tree} from '@angular-devkit/schematics';
10-
import {getWorkspace} from '@schematics/angular/utility/config';
1110

1211
/**
1312
* Gets all tsconfig paths from a CLI project by reading the workspace configuration
@@ -22,26 +21,52 @@ export function getProjectTsConfigPaths(tree: Tree): string[] {
2221
]);
2322

2423
// Add any tsconfig directly referenced in a build or test task of the angular.json workspace.
25-
const workspace = getWorkspace(tree);
26-
27-
for (const project of Object.values(workspace.projects)) {
28-
['build', 'test'].forEach(targetName => {
29-
if (project.targets &&
30-
project.targets[targetName] &&
31-
project.targets[targetName].options &&
32-
project.targets[targetName].options.tsConfig) {
33-
tsconfigPaths.add(project.targets[targetName].options.tsConfig);
34-
}
35-
36-
if (project.architect &&
37-
project.architect[targetName] &&
38-
project.architect[targetName].options &&
39-
project.architect[targetName].options.tsConfig) {
40-
tsconfigPaths.add(project.architect[targetName].options.tsConfig);
41-
}
42-
});
24+
const workspace = getWorkspaceConfigGracefully(tree);
25+
26+
if (workspace) {
27+
for (const project of Object.values<any>(workspace.projects)) {
28+
['build', 'test'].forEach(targetName => {
29+
if (project.targets &&
30+
project.targets[targetName] &&
31+
project.targets[targetName].options &&
32+
project.targets[targetName].options.tsConfig) {
33+
tsconfigPaths.add(project.targets[targetName].options.tsConfig);
34+
}
35+
36+
if (project.architect &&
37+
project.architect[targetName] &&
38+
project.architect[targetName].options &&
39+
project.architect[targetName].options.tsConfig) {
40+
tsconfigPaths.add(project.architect[targetName].options.tsConfig);
41+
}
42+
});
43+
}
4344
}
4445

4546
// Filter out tsconfig files that don't exist in the CLI project.
4647
return Array.from(tsconfigPaths).filter(p => tree.exists(p));
4748
}
49+
50+
/** Name of the default Angular CLI workspace configuration files. */
51+
const defaultWorkspaceConfigPaths = ['/angular.json', '/.angular.json'];
52+
53+
/**
54+
* Resolve the workspace configuration of the specified tree gracefully. We cannot use the utility
55+
* functions from the default Angular schematics because those might not be present in older
56+
* versions of the CLI. Also it's important to resolve the workspace gracefully because
57+
* the CLI project could be still using `.angular-cli.json` instead of thew new config.
58+
*/
59+
function getWorkspaceConfigGracefully(tree: Tree): any {
60+
const path = defaultWorkspaceConfigPaths.filter(filePath => tree.exists(filePath))[0];
61+
const configBuffer = tree.read(path);
62+
63+
if (!path || !configBuffer) {
64+
return null;
65+
}
66+
67+
try {
68+
return JSON.parse(configBuffer.toString());
69+
} catch {
70+
return null;
71+
}
72+
}

0 commit comments

Comments
 (0)