fix(bazel): Read latest versions from latest-versions.ts & use semver check (#27526)

When @angular/bazel is installed, a postinstall script is run to make sure that
the npm version is *exactly* the same as the Angular repository install by
Bazel. This check is overly stringent. Instead, it should enforce that the
version satisfies the range check instead. This is consistent with the range
defined in angular-cli/packages/schematics/angular/utility/latest-versions.ts.

This commit also fixes the Bazel workspace to use the same Rxjs version if it's
already installed.

PR Close #27526
This commit is contained in:
Keen Yee Liau
2018-12-06 15:46:36 -08:00
committed by Alex Rickabaugh
parent 50687e11cf
commit 30a3b49830
7 changed files with 1580 additions and 23 deletions

View File

@ -11,25 +11,27 @@
import {strings} from '@angular-devkit/core';
import {Rule, SchematicContext, SchematicsException, Tree, apply, applyTemplates, mergeWith, move, url} from '@angular-devkit/schematics';
import {getWorkspace} from '@schematics/angular/utility/config';
import {latestVersions} from '@schematics/angular/utility/latest-versions';
import {validateProjectName} from '@schematics/angular/utility/validation';
import {Schema as BazelWorkspaceOptions} from './schema';
/**
* Look for package.json file for @angular/core in node_modules and extract its
* version.
* Look for package.json file for package with `packageName` in node_modules and
* extract its version.
*/
function findAngularVersion(options: BazelWorkspaceOptions, host: Tree): string|null {
function findVersion(projectName: string, packageName: string, host: Tree): string|null {
// Need to look in multiple locations because we could be working in a subtree.
const candidates = [
'node_modules/@angular/core/package.json',
`${options.name}/node_modules/@angular/core/package.json`,
`node_modules/${packageName}/package.json`,
`${projectName}/node_modules/${packageName}/package.json`,
];
for (const candidate of candidates) {
if (host.exists(candidate)) {
try {
const packageJson = JSON.parse(host.read(candidate).toString());
if (packageJson.name === '@angular/core' && packageJson.version) {
if (packageJson.name === packageName && packageJson.version) {
return packageJson.version;
}
} catch {
@ -39,6 +41,15 @@ function findAngularVersion(options: BazelWorkspaceOptions, host: Tree): string|
return null;
}
/**
* Clean the version string and return version in the form "1.2.3". Return
* null if version string is invalid. This is similar to semver.clean() but
* takes characters like '^' and '~' into account.
*/
export function clean(version: string): string|null {
const matches = version.match(/(\d+\.\d+\.\d+)/);
return matches ? matches.pop() : null;
}
export default function(options: BazelWorkspaceOptions): Rule {
return (host: Tree, context: SchematicContext) => {
@ -54,13 +65,25 @@ export default function(options: BazelWorkspaceOptions): Rule {
}
const appDir = `${newProjectRoot}/${options.name}`;
// If user already has angular installed, Bazel should use that version
const existingAngularVersion = findAngularVersion(options, host);
// If the project already has some deps installed, Bazel should use existing
// versions.
const existingVersions = {
Angular: findVersion(options.name, '@angular/core', host),
RxJs: findVersion(options.name, 'rxjs', host),
};
for (const name of Object.keys(existingVersions)) {
const version = existingVersions[name];
if (version) {
context.logger.info(`Bazel will reuse existing version for ${name}: ${version}`);
}
}
const workspaceVersions = {
'ANGULAR_VERSION': existingAngularVersion || '7.1.1',
'RULES_SASS_VERSION': '1.14.1',
'RXJS_VERSION': '6.3.3',
'ANGULAR_VERSION': existingVersions.Angular || clean(latestVersions.Angular),
'RXJS_VERSION': existingVersions.RxJs || clean(latestVersions.RxJs),
// TODO(kyliau): Consider moving this to latest-versions.ts
'RULES_SASS_VERSION': '1.15.1',
};
return mergeWith(apply(url('./files'), [

View File

@ -8,6 +8,7 @@
import {HostTree} from '@angular-devkit/schematics';
import {SchematicTestRunner, UnitTestTree} from '@angular-devkit/schematics/testing';
import {clean} from './index';
describe('Bazel-workspace Schematic', () => {
const schematicRunner =
@ -59,3 +60,14 @@ describe('Bazel-workspace Schematic', () => {
});
});
});
describe('clean', () => {
[['1.2.3', '1.2.3'], [' 1.2.3', '1.2.3'], ['1.2.3 ', '1.2.3'], ['~1.2.3', '1.2.3'],
['^1.2.3', '1.2.3'], ['v1.2.3', '1.2.3'], ['1.2', null], ['a.b.c', null],
].forEach(([version, want]) => {
it(`should match ${version} with ${want}`, () => {
const got = clean(version);
expect(got).toBe(want);
});
});
});

View File

@ -35,8 +35,9 @@ function addDevDependenciesToPackageJson(options: Schema) {
const devDependencies: {[k: string]: string} = {
'@angular/bazel': angularCoreVersion,
'@bazel/karma': '0.21.0',
'@bazel/typescript': '0.21.0',
// TODO(kyliau): Consider moving this to latest-versions.ts
'@bazel/karma': '^0.21.0',
'@bazel/typescript': '^0.21.0',
};
const recorder = host.beginUpdate(packageJson);

View File

@ -41,6 +41,16 @@ describe('Ng-new Schematic', () => {
expect(json.dependencies[core]).toBe(json.devDependencies[bazel]);
});
it('should add @bazel/* dev dependencies', () => {
const options = {...defaultOptions};
const host = schematicRunner.runSchematic('ng-new', options);
const content = host.readContent('/demo/package.json');
const json = JSON.parse(content);
const devDeps = Object.keys(json.devDependencies);
expect(devDeps).toContain('@bazel/karma');
expect(devDeps).toContain('@bazel/typescript');
});
it('should create Bazel workspace file', () => {
const options = {...defaultOptions};
const host = schematicRunner.runSchematic('ng-new', options);