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
This commit is contained in:
Alan Agius 2019-12-02 14:35:37 +01:00 committed by Miško Hevery
parent 755d2d572f
commit 0cd8431698
3 changed files with 54 additions and 105 deletions

View File

@ -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"
},

View File

@ -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<string, string> 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;
};

View File

@ -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);
}