From ece6b2d6be5e3541f40dfffd11360e262594ca01 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Fri, 2 Nov 2018 00:57:53 +0100 Subject: [PATCH] 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`. --- .../project-tsconfig-paths.spec.ts | 59 +++++++++++++++++++ .../upgrade-rules/project-tsconfig-paths.ts | 59 +++++++++++++------ 2 files changed, 101 insertions(+), 17 deletions(-) create mode 100644 src/cdk/schematics/ng-update/upgrade-rules/project-tsconfig-paths.spec.ts diff --git a/src/cdk/schematics/ng-update/upgrade-rules/project-tsconfig-paths.spec.ts b/src/cdk/schematics/ng-update/upgrade-rules/project-tsconfig-paths.spec.ts new file mode 100644 index 00000000000..633b2f4526f --- /dev/null +++ b/src/cdk/schematics/ng-update/upgrade-rules/project-tsconfig-paths.spec.ts @@ -0,0 +1,59 @@ +import {HostTree} from '@angular-devkit/schematics'; +import {UnitTestTree} from '@angular-devkit/schematics/testing'; +import {getProjectTsConfigPaths} from './project-tsconfig-paths'; + +describe('ng-update project-tsconfig-paths', () => { + + let testTree: UnitTestTree; + + beforeEach(() => { + testTree = new UnitTestTree(new HostTree()); + }); + + it('should detect build tsconfig path inside of angular.json file', () => { + testTree.create('/my-custom-config.json', ''); + testTree.create('/angular.json', JSON.stringify({ + projects: { + my_name: { + architect: { + build: { + options: { + tsConfig: './my-custom-config.json' + } + } + } + } + } + })); + + expect(getProjectTsConfigPaths(testTree)).toEqual(['./my-custom-config.json']); + }); + + it('should detect test tsconfig path inside of .angular.json file', () => { + testTree.create('/my-test-config.json', ''); + testTree.create('/.angular.json', JSON.stringify({ + projects: { + with_tests: { + architect: { + test: { + options: { + tsConfig: './my-test-config.json' + } + } + } + } + } + })); + + expect(getProjectTsConfigPaths(testTree)).toEqual(['./my-test-config.json']); + }); + + it('should detect common tsconfigs if no workspace config could be found', () => { + testTree.create('/tsconfig.json', ''); + testTree.create('/src/tsconfig.json', ''); + testTree.create('/src/tsconfig.app.json', ''); + + expect(getProjectTsConfigPaths(testTree)) + .toEqual(['./tsconfig.json', './src/tsconfig.json', './src/tsconfig.app.json']); + }); +}); diff --git a/src/cdk/schematics/ng-update/upgrade-rules/project-tsconfig-paths.ts b/src/cdk/schematics/ng-update/upgrade-rules/project-tsconfig-paths.ts index 46b085eeebb..f65897025c0 100644 --- a/src/cdk/schematics/ng-update/upgrade-rules/project-tsconfig-paths.ts +++ b/src/cdk/schematics/ng-update/upgrade-rules/project-tsconfig-paths.ts @@ -7,7 +7,6 @@ */ import {Tree} from '@angular-devkit/schematics'; -import {getWorkspace} from '@schematics/angular/utility/config'; /** * Gets all tsconfig paths from a CLI project by reading the workspace configuration @@ -22,26 +21,52 @@ export function getProjectTsConfigPaths(tree: Tree): string[] { ]); // Add any tsconfig directly referenced in a build or test task of the angular.json workspace. - const workspace = getWorkspace(tree); + const workspace = getWorkspaceConfigGracefully(tree); - for (const project of Object.values(workspace.projects)) { - ['build', 'test'].forEach(targetName => { - if (project.targets && - project.targets[targetName] && - project.targets[targetName].options && - project.targets[targetName].options.tsConfig) { - tsconfigPaths.add(project.targets[targetName].options.tsConfig); - } + if (workspace) { + for (const project of Object.values(workspace.projects)) { + ['build', 'test'].forEach(targetName => { + if (project.targets && + project.targets[targetName] && + project.targets[targetName].options && + project.targets[targetName].options.tsConfig) { + tsconfigPaths.add(project.targets[targetName].options.tsConfig); + } - if (project.architect && - project.architect[targetName] && - project.architect[targetName].options && - project.architect[targetName].options.tsConfig) { - tsconfigPaths.add(project.architect[targetName].options.tsConfig); - } - }); + if (project.architect && + project.architect[targetName] && + project.architect[targetName].options && + project.architect[targetName].options.tsConfig) { + tsconfigPaths.add(project.architect[targetName].options.tsConfig); + } + }); + } } // Filter out tsconfig files that don't exist in the CLI project. return Array.from(tsconfigPaths).filter(p => tree.exists(p)); } + +/** Name of the default Angular CLI workspace configuration files. */ +const defaultWorkspaceConfigPaths = ['/angular.json', '/.angular.json']; + +/** + * Resolve the workspace configuration of the specified tree gracefully. We cannot use the utility + * functions from the default Angular schematics because those might not be present in older + * versions of the CLI. Also it's important to resolve the workspace gracefully because + * the CLI project could be still using `.angular-cli.json` instead of thew new config. + */ +function getWorkspaceConfigGracefully(tree: Tree): any { + const path = defaultWorkspaceConfigPaths.filter(filePath => tree.exists(filePath))[0]; + const configBuffer = tree.read(path); + + if (!path || !configBuffer) { + return null; + } + + try { + return JSON.parse(configBuffer.toString()); + } catch { + return null; + } +}