From 0cd843169884a57d92c1ec114f7e778720b5ef24 Mon Sep 17 00:00:00 2001 From: Alan Agius Date: Mon, 2 Dec 2019 14:35:37 +0100 Subject: [PATCH] fix(bazel): don't rely on @angular/core being as a depedency to install @angular/bazel (#34181) With this change we fix the logic to detect if a package is installed, removing a package and add a package by using the CLI schematic helpers. Also we save `@angular/bazel` package directly as a `devDependency` when doing `ng-add`. Closes #34164 PR Close #34181 --- packages/bazel/package.json | 3 + packages/bazel/src/schematics/ng-add/index.ts | 145 ++++++------------ .../bazel/src/schematics/ng-add/index_spec.ts | 11 +- 3 files changed, 54 insertions(+), 105 deletions(-) diff --git a/packages/bazel/package.json b/packages/bazel/package.json index 86b02ae744..4781f93cd3 100644 --- a/packages/bazel/package.json +++ b/packages/bazel/package.json @@ -51,6 +51,9 @@ }, "builders": "./src/builders/builders.json", "schematics": "./src/schematics/collection.json", + "ng-add": { + "save": "devDependencies" + }, "ng-update": { "packageGroup": "NG_UPDATE_PACKAGE_GROUP" }, diff --git a/packages/bazel/src/schematics/ng-add/index.ts b/packages/bazel/src/schematics/ng-add/index.ts index 9bcac30797..744f8dfe9b 100644 --- a/packages/bazel/src/schematics/ng-add/index.ts +++ b/packages/bazel/src/schematics/ng-add/index.ts @@ -12,15 +12,17 @@ import {JsonAstObject, parseJsonAst} from '@angular-devkit/core'; import {Rule, SchematicContext, SchematicsException, Tree, apply, applyTemplates, chain, mergeWith, url} from '@angular-devkit/schematics'; import {NodePackageInstallTask} from '@angular-devkit/schematics/tasks'; import {getWorkspace, getWorkspacePath} from '@schematics/angular/utility/config'; +import {NodeDependencyType, addPackageJsonDependency, getPackageJsonDependency, removePackageJsonDependency} from '@schematics/angular/utility/dependencies'; import {findPropertyInAstObject, insertPropertyInAstObjectInOrder} from '@schematics/angular/utility/json-utils'; import {validateProjectName} from '@schematics/angular/utility/validation'; -import {isJsonAstObject, removeKeyValueInAstObject, replacePropertyInAstObject} from '../utility/json-utils'; +import {isJsonAstObject, replacePropertyInAstObject} from '../utility/json-utils'; import {findE2eArchitect} from '../utility/workspace-utils'; import {Schema} from './schema'; + /** * Packages that build under Bazel require additional dev dependencies. This * function adds those dependencies to "devDependencies" section in @@ -28,57 +30,41 @@ import {Schema} from './schema'; */ function addDevDependenciesToPackageJson(options: Schema) { return (host: Tree) => { - const packageJson = 'package.json'; - if (!host.exists(packageJson)) { - throw new Error(`Could not find ${packageJson}`); - } - const packageJsonContent = host.read(packageJson); - if (!packageJsonContent) { - throw new Error('Failed to read package.json content'); - } - const jsonAst = parseJsonAst(packageJsonContent.toString()) as JsonAstObject; - const deps = findPropertyInAstObject(jsonAst, 'dependencies') as JsonAstObject; - const devDeps = findPropertyInAstObject(jsonAst, 'devDependencies') as JsonAstObject; - - const angularCoreNode = findPropertyInAstObject(deps, '@angular/core'); - if (!angularCoreNode) { + const angularCore = getPackageJsonDependency(host, '@angular/core'); + if (!angularCore) { throw new Error('@angular/core dependency not found in package.json'); } - const angularCoreVersion = angularCoreNode.value as string; - const devDependencies: {[k: string]: string} = { - '@angular/bazel': angularCoreVersion, - '@bazel/bazel': '1.1.0', - '@bazel/ibazel': '^0.10.2', - '@bazel/karma': '0.40.0', - '@bazel/protractor': '0.40.0', - '@bazel/rollup': '0.40.0', - '@bazel/terser': '0.40.0', - '@bazel/typescript': '0.40.0', - 'history-server': '^1.3.1', - 'rollup': '^1.25.2', - 'rollup-plugin-commonjs': '^10.1.0', - 'rollup-plugin-node-resolve': '^5.2.0', - 'terser': '^4.3.9', - }; + // TODO: use a Record when the tsc lib setting allows us + const devDependencies: [string, string][] = [ + ['@angular/bazel', angularCore.version], + ['@bazel/bazel', '1.1.0'], + ['@bazel/ibazel', '^0.10.2'], + ['@bazel/karma', '0.40.0'], + ['@bazel/protractor', '0.40.0'], + ['@bazel/rollup', '0.40.0'], + ['@bazel/terser', '0.40.0'], + ['@bazel/typescript', '0.40.0'], + ['history-server', '^1.3.1'], + ['rollup', '^1.25.2'], + ['rollup-plugin-commonjs', '^10.1.0'], + ['rollup-plugin-node-resolve', '^5.2.0'], + ['terser', '^4.3.9'], + ]; - const recorder = host.beginUpdate(packageJson); - for (const packageName of Object.keys(devDependencies)) { - const existingDep = findPropertyInAstObject(deps, packageName); - if (existingDep) { - const content = packageJsonContent.toString(); - removeKeyValueInAstObject(recorder, content, deps, packageName); - } - const version = devDependencies[packageName]; - const indent = 4; - if (findPropertyInAstObject(devDeps, packageName)) { - replacePropertyInAstObject(recorder, devDeps, packageName, version, indent); - } else { - insertPropertyInAstObjectInOrder(recorder, devDeps, packageName, version, indent); + for (const [name, version] of devDependencies) { + const dep = getPackageJsonDependency(host, name); + if (dep && dep.type !== NodeDependencyType.Dev) { + removePackageJsonDependency(host, name); } + + addPackageJsonDependency(host, { + name, + version, + type: NodeDependencyType.Dev, + overwrite: true, + }); } - host.commitUpdate(recorder); - return host; }; } @@ -88,38 +74,13 @@ function addDevDependenciesToPackageJson(options: Schema) { */ function removeObsoleteDependenciesFromPackageJson(options: Schema) { return (host: Tree) => { - const packageJson = 'package.json'; - if (!host.exists(packageJson)) { - throw new Error(`Could not find ${packageJson}`); - } - const buffer = host.read(packageJson); - if (!buffer) { - throw new Error('Failed to read package.json content'); - } - const content = buffer.toString(); - const jsonAst = parseJsonAst(content) as JsonAstObject; - const deps = findPropertyInAstObject(jsonAst, 'dependencies') as JsonAstObject; - const devDeps = findPropertyInAstObject(jsonAst, 'devDependencies') as JsonAstObject; - const depsToRemove = [ '@angular-devkit/build-angular', ]; - const recorder = host.beginUpdate(packageJson); - for (const packageName of depsToRemove) { - const depNode = findPropertyInAstObject(deps, packageName); - if (depNode) { - removeKeyValueInAstObject(recorder, content, deps, packageName); - } - const devDepNode = findPropertyInAstObject(devDeps, packageName); - if (devDepNode) { - removeKeyValueInAstObject(recorder, content, devDeps, packageName); - } + removePackageJsonDependency(host, packageName); } - - host.commitUpdate(recorder); - return host; }; } @@ -166,7 +127,7 @@ function updateGitignore() { * Change the architect in angular.json to use Bazel builder. */ function updateAngularJsonToUseBazelBuilder(options: Schema): Rule { - return (host: Tree, context: SchematicContext) => { + return (host: Tree) => { const name = options.name !; const workspacePath = getWorkspacePath(host); if (!workspacePath) { @@ -277,39 +238,25 @@ function backupAngularJson(): Rule { */ function upgradeRxjs() { return (host: Tree, context: SchematicContext) => { - const packageJson = 'package.json'; - if (!host.exists(packageJson)) { - throw new Error(`Could not find ${packageJson}`); + const rxjsNode = getPackageJsonDependency(host, 'rxjs'); + if (!rxjsNode) { + throw new Error(`Failed to find rxjs dependency.`); } - const content = host.read(packageJson); - if (!content) { - throw new Error('Failed to read package.json content'); - } - const jsonAst = parseJsonAst(content.toString()); - if (!isJsonAstObject(jsonAst)) { - throw new Error(`Failed to parse JSON for ${packageJson}`); - } - const deps = findPropertyInAstObject(jsonAst, 'dependencies'); - if (!isJsonAstObject(deps)) { - throw new Error(`Failed to find dependencies in ${packageJson}`); - } - const rxjs = findPropertyInAstObject(deps, 'rxjs'); - if (!rxjs) { - throw new Error(`Failed to find rxjs in dependencies of ${packageJson}`); - } - const value = rxjs.value as string; // value can be version or range - const match = value.match(/(\d)+\.(\d)+.(\d)+$/); + + const match = rxjsNode.version.match(/(\d)+\.(\d)+.(\d)+$/); if (match) { const [_, major, minor] = match; - if (major < '6' || (major === '6' && minor < '4')) { - const recorder = host.beginUpdate(packageJson); - replacePropertyInAstObject(recorder, deps, 'rxjs', '~6.4.0'); - host.commitUpdate(recorder); + if (major < '6' || (major === '6' && minor < '5')) { + addPackageJsonDependency(host, { + ...rxjsNode, + version: '~6.5.3', + overwrite: true, + }); } } else { context.logger.info( 'Could not determine version of rxjs. \n' + - 'Please make sure that version is at least 6.4.0.'); + 'Please make sure that version is at least 6.5.3.'); } return host; }; diff --git a/packages/bazel/src/schematics/ng-add/index_spec.ts b/packages/bazel/src/schematics/ng-add/index_spec.ts index e89f160c20..133584ac9e 100644 --- a/packages/bazel/src/schematics/ng-add/index_spec.ts +++ b/packages/bazel/src/schematics/ng-add/index_spec.ts @@ -73,7 +73,7 @@ describe('ng-add schematic', () => { message = e.message; } - expect(message).toBe('Could not find package.json'); + expect(message).toBe('Could not read package.json.'); }); it('throws if angular.json is not found', async() => { @@ -104,7 +104,6 @@ describe('ng-add schematic', () => { expect(Object.keys(json)).toContain('devDependencies'); expect(Object.keys(json.dependencies)).toContain(core); expect(Object.keys(json.devDependencies)).toContain(bazel); - expect(json.dependencies[core]).toBe(json.devDependencies[bazel]); }); it('should add @bazel/* dev dependencies', async() => { @@ -273,9 +272,9 @@ describe('ng-add schematic', () => { ['~6.3.3', true], ['^6.3.3', true], ['~6.3.11', true], - ['6.4.0', false], - ['~6.4.0', false], - ['~6.4.1', false], + ['6.4.0', true], + ['~6.4.0', true], + ['~6.4.1', true], ['6.5.0', false], ['~6.5.0', false], ['^6.5.0', false], @@ -298,7 +297,7 @@ describe('ng-add schematic', () => { const content = host.readContent('/package.json'); const json = JSON.parse(content); if (upgrade) { - expect(json.dependencies.rxjs).toBe('~6.4.0'); + expect(json.dependencies.rxjs).toBe('~6.5.3'); } else { expect(json.dependencies.rxjs).toBe(version); }