fix(bazel): Bazel-workspace schematics should run in ScopedTree (#28349)

Users should be able to add Bazel workspace to an existing project.
The current approach assumes that the schematics is working on the same
tree as that of ng-new, which includes the top-level directory. Instead,
the schematic should work on the tree rooted at `appRoot` to enable
Bazel files to be added to existing project.

This change uses the newly implemented ScopedTree
a0ac4b0e3d
to achieve this.

NOTE: The version of `@angular-devkit/schematics` that is installed is
used to run the `@angular/bazel` schematic. Even if a different version
is used in the schematic itself, it has no effect.
Therefore, the *latest* Angular CLI should be used to generate the
files. As of this commit, the latest version is @angular/cli@7.3.0-rc.0

PR Close #28349
This commit is contained in:
Keen Yee Liau 2019-01-17 11:35:06 -08:00 committed by Jason Aden
parent aed48e00d2
commit 260ac20e92
9 changed files with 80 additions and 63 deletions

View File

@ -34,7 +34,7 @@
"@angular-devkit/architect": "^0.10.6",
"@angular-devkit/build-optimizer": "^0.12.2",
"@angular-devkit/core": "^7.0.4",
"@angular-devkit/schematics": "^7.0.4",
"@angular-devkit/schematics": "^7.3.0-rc.0",
"@bazel/karma": "~0.22.1",
"@bazel/typescript": "~0.22.1",
"@schematics/angular": "^7.0.4",

View File

@ -14,7 +14,7 @@
"dependencies": {
"@angular-devkit/architect": "^0.10.6",
"@angular-devkit/core": "^7.0.4",
"@angular-devkit/schematics": "^7.0.4",
"@angular-devkit/schematics": "^7.3.0-rc.0",
"@bazel/typescript": "^0.22.1",
"@schematics/angular": "^7.0.4",
"@types/node": "6.0.84",

View File

@ -21,21 +21,15 @@ import {Schema as BazelWorkspaceOptions} from './schema';
* Look for package.json file for package with `packageName` in node_modules and
* extract its version.
*/
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/${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 === packageName && packageJson.version) {
return packageJson.version;
}
} catch {
function findVersion(packageName: string, host: Tree): string|null {
const candidate = `node_modules/${packageName}/package.json`;
if (host.exists(candidate)) {
try {
const packageJson = JSON.parse(host.read(candidate).toString());
if (packageJson.name === packageName && packageJson.version) {
return packageJson.version;
}
} catch {
}
}
return null;
@ -72,23 +66,17 @@ function hasSassStylesheet(host: Tree) {
export default function(options: BazelWorkspaceOptions): Rule {
return (host: Tree, context: SchematicContext) => {
if (!options.name) {
throw new SchematicsException(`Invalid options, "name" is required.`);
const name = options.name || getWorkspace(host).defaultProject;
if (!name) {
throw new Error('Please provide a name for Bazel workspace');
}
validateProjectName(options.name);
let newProjectRoot = '';
try {
const workspace = getWorkspace(host);
newProjectRoot = workspace.newProjectRoot || '';
} catch {
}
const appDir = `${newProjectRoot}/${options.name}`;
validateProjectName(name);
// 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),
Angular: findVersion('@angular/core', host),
RxJs: findVersion('rxjs', host),
};
Object.keys(existingVersions).forEach((name: 'Angular' | 'RxJs') => {
@ -110,12 +98,11 @@ export default function(options: BazelWorkspaceOptions): Rule {
return mergeWith(apply(url('./files'), [
applyTemplates({
utils: strings,
...options,
name,
'dot': '.', ...workspaceVersions,
routing: hasRoutingModule(host),
sass: hasSassStylesheet(host),
}),
move(appDir),
]));
};
}

View File

@ -21,24 +21,24 @@ describe('Bazel-workspace Schematic', () => {
const options = {...defaultOptions};
const host = schematicRunner.runSchematic('bazel-workspace', options);
const files = host.files;
expect(files).toContain('/demo/.bazelignore');
expect(files).toContain('/demo/.bazelrc');
expect(files).toContain('/demo/BUILD.bazel');
expect(files).toContain('/demo/src/BUILD.bazel');
expect(files).toContain('/demo/WORKSPACE');
expect(files).toContain('/demo/yarn.lock');
expect(files).toContain('/.bazelignore');
expect(files).toContain('/.bazelrc');
expect(files).toContain('/BUILD.bazel');
expect(files).toContain('/src/BUILD.bazel');
expect(files).toContain('/WORKSPACE');
expect(files).toContain('/yarn.lock');
});
it('should find existing Angular version', () => {
let host = new UnitTestTree(new HostTree);
host.create('/demo/node_modules/@angular/core/package.json', JSON.stringify({
host.create('/node_modules/@angular/core/package.json', JSON.stringify({
name: '@angular/core',
version: '6.6.6',
}));
const options = {...defaultOptions};
host = schematicRunner.runSchematic('bazel-workspace', options, host);
expect(host.files).toContain('/demo/WORKSPACE');
const workspace = host.readContent('/demo/WORKSPACE');
expect(host.files).toContain('/WORKSPACE');
const workspace = host.readContent('/WORKSPACE');
expect(workspace).toMatch('ANGULAR_VERSION = "6.6.6"');
});
@ -46,19 +46,19 @@ describe('Bazel-workspace Schematic', () => {
const options = {...defaultOptions, name: 'demo-app'};
const host = schematicRunner.runSchematic('bazel-workspace', options);
const {files} = host;
expect(files).toContain('/demo-app/src/BUILD.bazel');
const content = host.readContent('/demo-app/src/BUILD.bazel');
expect(files).toContain('/src/BUILD.bazel');
const content = host.readContent('/src/BUILD.bazel');
expect(content).toContain('entry_module = "demo_app/src/main.dev"');
});
it('should add router if project contains routing module', () => {
let host = new UnitTestTree(new HostTree);
host.create('/demo/src/app/app-routing.module.ts', '');
expect(host.files).toContain('/demo/src/app/app-routing.module.ts');
host.create('/src/app/app-routing.module.ts', '');
expect(host.files).toContain('/src/app/app-routing.module.ts');
const options = {...defaultOptions};
host = schematicRunner.runSchematic('bazel-workspace', options, host);
expect(host.files).toContain('/demo/src/BUILD.bazel');
const content = host.readContent('/demo/src/BUILD.bazel');
expect(host.files).toContain('/src/BUILD.bazel');
const content = host.readContent('/src/BUILD.bazel');
expect(content).toContain('@angular//packages/router');
});
@ -66,16 +66,16 @@ describe('Bazel-workspace Schematic', () => {
it('should contain project name', () => {
const options = {...defaultOptions};
const host = schematicRunner.runSchematic('bazel-workspace', options);
expect(host.files).toContain('/demo/WORKSPACE');
const content = host.readContent('/demo/WORKSPACE');
expect(host.files).toContain('/WORKSPACE');
const content = host.readContent('/WORKSPACE');
expect(content).toContain('workspace(name = "demo")');
});
it('should convert dashes in name to underscore', () => {
const options = {...defaultOptions, name: 'demo-project'};
const host = schematicRunner.runSchematic('bazel-workspace', options);
expect(host.files).toContain('/demo-project/WORKSPACE');
const content = host.readContent('/demo-project/WORKSPACE');
expect(host.files).toContain('/WORKSPACE');
const content = host.readContent('/WORKSPACE');
expect(content).toContain('workspace(name = "demo_project"');
});
});
@ -83,35 +83,35 @@ describe('Bazel-workspace Schematic', () => {
describe('SASS', () => {
let host = new UnitTestTree(new HostTree);
beforeAll(() => {
host.create('/demo/src/app/app.component.scss', '');
expect(host.files).toContain('/demo/src/app/app.component.scss');
host.create('/src/app/app.component.scss', '');
expect(host.files).toContain('/src/app/app.component.scss');
const options = {...defaultOptions};
host = schematicRunner.runSchematic('bazel-workspace', options, host);
expect(host.files).toContain('/demo/WORKSPACE');
expect(host.files).toContain('/demo/src/BUILD.bazel');
expect(host.files).toContain('/WORKSPACE');
expect(host.files).toContain('/src/BUILD.bazel');
});
it('should download rules_sass in WORKSPACE', () => {
const content = host.readContent('/demo/WORKSPACE');
const content = host.readContent('/WORKSPACE');
expect(content).toContain('RULES_SASS_VERSION');
expect(content).toContain('io_bazel_rules_sass');
});
it('should load sass_repositories in WORKSPACE', () => {
const content = host.readContent('/demo/WORKSPACE');
const content = host.readContent('/WORKSPACE');
expect(content).toContain(
'load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")');
expect(content).toContain('sass_repositories()');
});
it('should add sass_binary rules in src/BUILD', () => {
const content = host.readContent('/demo/src/BUILD.bazel');
const content = host.readContent('/src/BUILD.bazel');
expect(content).toContain('load("@io_bazel_rules_sass//:defs.bzl", "sass_binary")');
expect(content).toMatch(/sass_binary\((.*\n)+\)/);
});
it('should add SASS targets to assets of ng_module in src/BUILD', () => {
const content = host.readContent('/demo/src/BUILD.bazel');
const content = host.readContent('/src/BUILD.bazel');
expect(content).toContain(`
assets = glob([
"**/*.css",

View File

@ -9,5 +9,5 @@ export interface Schema {
/**
* The name of the project.
*/
name: string;
name?: string;
}

View File

@ -15,6 +15,5 @@
}
},
"required": [
"name"
]
}

View File

@ -208,7 +208,9 @@ export default function(options: Schema): Rule {
}),
addDevDependenciesToPackageJson(options),
addDevAndProdMainForAot(options),
schematic('bazel-workspace', options),
schematic('bazel-workspace', options, {
scope: options.name,
}),
overwriteGitignore(options),
updateWorkspaceFileToUseBazelBuilder(options),
]);

View File

@ -32,7 +32,18 @@
rxjs "6.3.3"
source-map "0.7.3"
"@angular-devkit/schematics@7.1.2", "@angular-devkit/schematics@^7.0.4":
"@angular-devkit/core@7.3.0-rc.0":
version "7.3.0-rc.0"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-7.3.0-rc.0.tgz#e555a08d85259855ff1946f4268936a1aadd38f1"
integrity sha512-0vHuw1gIMh79tI+gRxCMn89U1DnjmBnqybVktaf9YXi9xshxd+nnFb31v7n1tJQVQiQNzGxk3hviFnkzxLZipw==
dependencies:
ajv "6.7.0"
chokidar "2.0.4"
fast-json-stable-stringify "2.0.0"
rxjs "6.3.3"
source-map "0.7.3"
"@angular-devkit/schematics@7.1.2":
version "7.1.2"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-7.1.2.tgz#847639044417d044bf1bc87f64508a0c3f99fae2"
integrity sha512-NFhHLYWf9gpGQm0s19lq+nAw3CZ0udBpoBLzCm8Crlmu6+7aAXgw7Fv5P4ukWJ/e1m7NDGVids+B6kBGXaY6Ig==
@ -40,6 +51,14 @@
"@angular-devkit/core" "7.1.2"
rxjs "6.3.3"
"@angular-devkit/schematics@^7.3.0-rc.0":
version "7.3.0-rc.0"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-7.3.0-rc.0.tgz#9f1e1f6942da36b12c81241398ed6ca8b2e65875"
integrity sha512-noqcQIOvah2G126DTFKY5Kiga8UwI9cKzyhQdNlf+8hAZpnWwTURItQ5xuMJg/XfRQLUSg9gWS2h1cI9AD7mxQ==
dependencies:
"@angular-devkit/core" "7.3.0-rc.0"
rxjs "6.3.3"
"@bazel/typescript@^0.22.1":
version "0.22.1"
resolved "https://registry.yarnpkg.com/@bazel/typescript/-/typescript-0.22.1.tgz#b52c00e8560019e2f9d273d45c04785e0ec9d9bd"
@ -79,6 +98,16 @@ ajv@6.5.3:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ajv@6.7.0:
version "6.7.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.7.0.tgz#e3ce7bb372d6577bb1839f1dfdfcbf5ad2948d96"
integrity sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==
dependencies:
fast-deep-equal "^2.0.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
ansi-regex@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"

View File

@ -61,7 +61,7 @@
rxjs "6.3.3"
source-map "0.7.3"
"@angular-devkit/schematics@7.0.5", "@angular-devkit/schematics@^7.0.4":
"@angular-devkit/schematics@7.0.5":
version "7.0.5"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-7.0.5.tgz#527bf0af5352172e92c5473a33bc07af44c77796"
integrity sha512-mWtPfBtObXXw5IWnMuOXBLn/Bv2lPxdmSqrCX9chTmxLXlFuv5e6HkzJfuF4BxjRUMaA+OW1qhnsHRJSI+p6sQ==
@ -69,7 +69,7 @@
"@angular-devkit/core" "7.0.5"
rxjs "6.3.3"
"@angular-devkit/schematics@7.3.0-rc.0":
"@angular-devkit/schematics@7.3.0-rc.0", "@angular-devkit/schematics@^7.3.0-rc.0":
version "7.3.0-rc.0"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-7.3.0-rc.0.tgz#9f1e1f6942da36b12c81241398ed6ca8b2e65875"
integrity sha512-noqcQIOvah2G126DTFKY5Kiga8UwI9cKzyhQdNlf+8hAZpnWwTURItQ5xuMJg/XfRQLUSg9gWS2h1cI9AD7mxQ==