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:

committed by
Jason Aden

parent
3deda898d0
commit
7033f39c61
@ -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),
|
||||
]));
|
||||
};
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -9,5 +9,5 @@ export interface Schema {
|
||||
/**
|
||||
* The name of the project.
|
||||
*/
|
||||
name: string;
|
||||
name?: string;
|
||||
}
|
||||
|
@ -15,6 +15,5 @@
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
|
@ -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),
|
||||
]);
|
||||
|
Reference in New Issue
Block a user