feat(bazel): re-introduce @nrwl/bazel

This commit is contained in:
Jason Jean 2019-11-26 23:09:01 -05:00 committed by Victor Savkin
parent fb4fccb713
commit 2e2167d9f3
21 changed files with 537 additions and 5 deletions

View File

@ -1,5 +1,6 @@
[
"angular",
"bazel",
"cypress",
"express",
"insights",

View File

@ -1,5 +1,6 @@
[
"angular",
"bazel",
"cypress",
"express",
"insights",

View File

@ -1,5 +1,6 @@
[
"angular",
"bazel",
"cypress",
"express",
"insights",

53
e2e/bazel.test.ts Normal file
View File

@ -0,0 +1,53 @@
import {
checkFilesExist,
ensureProject,
forEachCli,
readFile,
runCLI,
uniq,
updateFile
} from './utils';
forEachCli(currentCLIName => {
describe('Bazel', () => {
const ngapp = uniq('ngapp');
const reactapp = uniq('reactapp');
const nglib = uniq('nglib');
const reactlib = uniq('reactlib');
it('should generate build files for apps', () => {
ensureProject();
runCLI(`generate @nrwl/angular:app ${ngapp}`);
runCLI('generate @nrwl/bazel:sync');
checkFilesExist(`apps/${ngapp}/BUILD.bazel`);
runCLI(`generate @nrwl/react:app ${reactapp}`);
runCLI('generate @nrwl/bazel:sync');
checkFilesExist(`apps/${reactapp}/BUILD.bazel`);
});
it('should generate build files for libs', () => {
runCLI(`generate @nrwl/angular:lib ${nglib}`);
runCLI('generate @nrwl/bazel:sync');
checkFilesExist(`libs/${nglib}/BUILD.bazel`);
runCLI(`generate @nrwl/angular:lib ${reactlib}`);
runCLI('generate @nrwl/bazel:sync');
checkFilesExist(`libs/${reactlib}/BUILD.bazel`);
});
it('should add dependencies to build files', () => {
updateFile(`apps/${ngapp}/src/main.ts`, `import '@proj/${nglib}';`);
runCLI('generate @nrwl/bazel:sync');
expect(readFile(`apps/${ngapp}/BUILD.bazel`)).toContain(
`//libs/${nglib}:${nglib}`
);
updateFile(`apps/${reactapp}/src/main.ts`, `import '@proj/${reactlib}';`);
runCLI('generate @nrwl/bazel:sync');
expect(readFile(`apps/${reactapp}/BUILD.bazel`)).toContain(
`//libs/${reactlib}:${reactlib}`
);
});
});
});

View File

@ -0,0 +1,13 @@
{
"name": "Nx Bazel",
"version": "0.1",
"extends": [],
"schematics": {
"sync": {
"factory": "./src/schematics/sync/sync",
"schema": "./src/schematics/sync/schema.json",
"description": "Sync Build Files",
"hidden": true
}
}
}

0
packages/bazel/index.ts Normal file
View File

View File

@ -0,0 +1,3 @@
{
"schematics": {}
}

View File

@ -0,0 +1,54 @@
{
"name": "@nrwl/bazel",
"version": "0.0.1",
"description": "Bazel Plugin for Nx",
"repository": {
"type": "git",
"url": "git+https://github.com/nrwl/nx.git"
},
"keywords": [
"Monorepo",
"Bazel",
"Angular",
"React",
"Web",
"Node",
"Nest",
"Jest",
"Cypress",
"CLI"
],
"main": "index.js",
"types": "index.d.ts",
"author": "Victor Savkin",
"license": "MIT",
"bugs": {
"url": "https://github.com/nrwl/nx/issues"
},
"homepage": "https://nx.dev",
"schematics": "./collection.json",
"builders": "./builders.json",
"ng-update": {
"requirements": {},
"migrations": "./migrations.json",
"packageGroup": [
"@nrwl/workspace",
"@nrwl/angular",
"@nrwl/cypress",
"@nrwl/express",
"@nrwl/jest",
"@nrwl/nest",
"@nrwl/node",
"@nrwl/react",
"@nrwl/web"
]
},
"dependencies": {
"@angular-devkit/core": "8.3.14",
"@angular-devkit/schematics": "8.3.14",
"@nrwl/cli": "*"
},
"peerDependencies": {
"@nrwl/workspace": "*"
}
}

View File

@ -0,0 +1,39 @@
# ==== Generated by Nx
# ==== Do not touch
load("@npm//@nrwl/cli:index.bzl", "nx", "nx_test")
filegroup(
name = "<%= project.name %>",
srcs = glob(["**"]),
visibility = ["//visibility:public"],
)
nx(
name = "build",
args = [
"build",
"<%= project.name%>",
"--outputPath=$@",
],
data = [
":<%= project.name %>",
# Dependencies
<% for (dep of dependencies) { %>"//<%= dep %>",
<% } %>
# Root Files
"//:angular.json",
"//:package.json",
"//:tsconfig.json",
# NOTE: Use @npm//node_modules causes bundling to fail due to Babel errors.
"@npm//@angular-devkit/build-angular",
"@npm//@angular/cli",
"@npm//@angular/core",
"@npm//@angular/platform-browser-dynamic",
"@npm//@angular/router",
"@npm//@babel/preset-env",
"@npm//@nrwl/workspace",
"@npm//inquirer",
],
output_dir = True,
)

View File

@ -0,0 +1,7 @@
exports_files(
[
<% for (rootFile of rootFiles) { %>"<%= rootFile %>",
<% } %>
],
visibility = ["//:__subpackages__"],
)

View File

@ -0,0 +1,37 @@
# Bazel workspace created by @bazel/create 0.38.1
# Declares that this directory is the root of a Bazel workspace.
# See https://docs.bazel.build/versions/master/build-ref.html#workspace
workspace(
# How this workspace would be referenced with absolute labels from another workspace
name = "<%= name %>",
# Map the @npm bazel workspace to the node_modules directory.
# This lets Bazel use the same node_modules as other local tooling.
managed_directories = {"@npm": ["node_modules"]},
)
# Install the nodejs "bootstrap" package
# This provides the basic tools for running and packaging nodejs programs in Bazel
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "build_bazel_rules_nodejs",
sha256 = "c612d6b76eaa17540e8b8c806e02701ed38891460f9ba3303f4424615437887a",
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.42.1/rules_nodejs-0.42.1.tar.gz"],
)
# The yarn_install rule runs yarn anytime the package.json or yarn.lock file changes.
# It also extracts and installs any Bazel rules distributed in an npm package.
load("@build_bazel_rules_nodejs//:index.bzl", "yarn_install")
yarn_install(
# Name this npm so that Bazel Label references look like @npm//package
name = "npm",
data = ["//patches"],
package_json = "//:package.json",
yarn_lock = "//:yarn.lock",
)
# Install any Bazel rules which were extracted earlier by the yarn_install rule.
load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies")
install_bazel_dependencies()

View File

@ -0,0 +1,8 @@
{
"$schema": "http://json-schema.org/schema",
"id": "SchematicsNxApp",
"title": "Nx Application Options Schema",
"type": "object",
"properties": {},
"required": []
}

View File

@ -0,0 +1,94 @@
import { Tree } from '@angular-devkit/schematics';
import { createEmptyWorkspace } from '@nrwl/workspace/testing';
import { runSchematic } from '../utils/testing';
import { stripIndents } from '@angular-devkit/core/src/utils/literals';
describe('@nrwl/bazel:sync', () => {
let tree: Tree;
beforeEach(() => {
tree = Tree.empty();
tree = createEmptyWorkspace(tree);
tree.create('tools/schematics/custom/index.ts', '');
});
describe('WORKSPACE', () => {
it('should be created', async () => {
const result = await runSchematic('sync', {}, tree);
expect(result.exists('WORKSPACE')).toEqual(true);
});
it('should name the workspace validly', async () => {
const result = await runSchematic('sync', {}, tree);
const contents = stripIndents`${result.readContent('WORKSPACE')}`;
expect(contents).toContain('name = "test_name"');
});
it('should import nodejs bazel rules', async () => {
const result = await runSchematic('sync', {}, tree);
const contents = stripIndents`${result.readContent('WORKSPACE')}`;
expect(contents).toContain(stripIndents`
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "build_bazel_rules_nodejs",
sha256 = "c612d6b76eaa17540e8b8c806e02701ed38891460f9ba3303f4424615437887a",
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.42.1/rules_nodejs-0.42.1.tar.gz"],
)
`);
});
it('should import yarn bazel rules and install', async () => {
const result = await runSchematic('sync', {}, tree);
const contents = stripIndents`${result.readContent('WORKSPACE')}`;
expect(contents).toContain(stripIndents`
load("@build_bazel_rules_nodejs//:index.bzl", "yarn_install")
yarn_install(
# Name this npm so that Bazel Label references look like @npm//package
name = "npm",
data = ["//patches"],
package_json = "//:package.json",
yarn_lock = "//:yarn.lock",
)
# Install any Bazel rules which were extracted earlier by the yarn_install rule.
load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies")
install_bazel_dependencies()
`);
});
});
describe('root BUILD.bazel', () => {
it('should be created', async () => {
const result = await runSchematic('sync', {}, tree);
expect(result.exists('BUILD.bazel')).toEqual(true);
});
it('should export root files', async () => {
const result = await runSchematic('sync', {}, tree);
const contents = stripIndents`${result.readContent('BUILD.bazel')}`;
expect(contents).toContain(stripIndents`exports_files(
[
"workspace.json",
"package.json",
"nx.json",
"tsconfig.json",
"tslint.json",
],
visibility = ["//:__subpackages__"],
)
`);
});
});
});

View File

@ -0,0 +1,119 @@
import {
apply,
chain,
MergeStrategy,
mergeWith,
move,
Rule,
Source,
template,
Tree,
url
} from '@angular-devkit/schematics';
import {
getProjectGraphFromHost,
readJsonInTree,
ProjectGraph,
ProjectGraphNode
} from '@nrwl/workspace';
import { join, normalize } from '@angular-devkit/core';
function createBuildFile(
project: ProjectGraphNode,
projectGraph: ProjectGraph
): Source {
return apply(url('./files/build-file'), [
template({
tmpl: '',
project,
projectGraph,
dependencies: projectGraph.dependencies[project.name]
? projectGraph.dependencies[project.name].map(
dep => `//${projectGraph.nodes[dep.target].data.root}:${dep.target}`
)
: []
})
]);
}
function updateBuildFile(
project: ProjectGraphNode,
projectGraph: ProjectGraph
): Rule {
return (host, context) => {
const buildFile = createBuildFile(project, projectGraph);
const buildFilePath = join(normalize(project.data.root), 'BUILD.bazel');
return mergeWith(
apply(buildFile, [
sourceHost => {
if (host.exists(buildFilePath)) {
const contents = sourceHost.read('BUILD.bazel').toString();
const customPart = host
.read(buildFilePath)
.toString()
.split('# ==== Generated by Nx')[0];
host.delete(buildFilePath);
sourceHost.overwrite('BUILD.bazel', customPart + contents);
}
},
move(project.data.root)
]),
MergeStrategy.Overwrite
);
};
}
function createWorkspaceFile() {
return host => {
return mergeWith(
apply(url('./files/workspace-file'), [
template({
tmpl: '',
name: readJsonInTree(host, '/package.json').name.replace('-', '_')
}),
() => {
if (host.exists('WORKSPACE')) {
host.delete('WORKSPACE');
}
}
]),
MergeStrategy.Overwrite
);
};
}
function createRootBuildFile() {
return host => {
return mergeWith(
apply(url('./files/root-build-file'), [
template({
tmpl: '',
rootFiles: host
.getDir('/')
.subfiles.filter(f => f !== 'WORKSPACE' && f !== 'BUILD.bazel')
}),
() => {
if (host.exists('BUILD.bazel')) {
host.delete('BUILD.bazel');
}
}
]),
MergeStrategy.Overwrite
);
};
}
export default (): Rule => {
return (host: Tree) => {
const projectGraph = getProjectGraphFromHost(host);
return chain([
createWorkspaceFile(),
createRootBuildFile(),
...Object.values(projectGraph.nodes).map(project =>
updateBuildFile(project, projectGraph)
)
]);
};
};

View File

@ -0,0 +1,20 @@
import { join } from 'path';
import { SchematicTestRunner } from '@angular-devkit/schematics/testing';
import { Tree, Rule } from '@angular-devkit/schematics';
const testRunner = new SchematicTestRunner(
'@nrwl/bazel',
join(__dirname, '../../../collection.json')
);
export function runSchematic<SchemaOptions = any>(
schematicName: string,
options: SchemaOptions,
tree: Tree
) {
return testRunner.runSchematicAsync(schematicName, options, tree).toPromise();
}
export function callRule(rule: Rule, tree: Tree) {
return testRunner.callRule(rule, tree).toPromise();
}

View File

@ -44,6 +44,7 @@ export {
createOrUpdate,
findNodes,
updatePackageJsonDependencies,
getProjectGraphFromHost,
readWorkspace
} from './src/utils/ast-utils';

View File

@ -11,6 +11,13 @@ import * as stripJsonComments from 'strip-json-comments';
import { serializeJson } from './fileutils';
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
import { getWorkspacePath } from './cli-config-utils';
import {
createProjectGraph,
ProjectGraph
} from '../command-line/project-graph';
import { NxJson } from '../command-line/shared';
import { FileData } from '../command-line/file-utils';
import { extname, join, normalize, Path } from '@angular-devkit/core';
function nodesByPosition(first: ts.Node, second: ts.Node): number {
return first.getStart() - second.getStart();
@ -374,6 +381,78 @@ export function readJsonInTree<T = any>(host: Tree, path: string): T {
}
}
/**
* Method for utilizing the project graph in schematics
*/
export function getProjectGraphFromHost(host: Tree): ProjectGraph {
const workspaceJson = readJsonInTree(host, getWorkspacePath(host));
const nxJson = readJsonInTree<NxJson>(host, '/nx.json');
const fileRead = (f: string) => host.read(f).toString();
const workspaceFiles: FileData[] = [];
const mtime = +Date.now();
workspaceFiles.push(
...allFilesInDirInHost(host, normalize(''), { recursive: false }).map(f =>
getFileDataInHost(host, f, mtime)
)
);
workspaceFiles.push(
...allFilesInDirInHost(host, normalize('tools')).map(f =>
getFileDataInHost(host, f, mtime)
)
);
// Add files for workspace projects
Object.keys(workspaceJson.projects).forEach(projectName => {
const project = workspaceJson.projects[projectName];
workspaceFiles.push(
...allFilesInDirInHost(host, normalize(project.root)).map(f =>
getFileDataInHost(host, f, mtime)
)
);
});
return createProjectGraph(workspaceJson, nxJson, workspaceFiles, fileRead);
}
export function getFileDataInHost(
host: Tree,
path: Path,
mtime: number
): FileData {
return {
file: path,
ext: extname(normalize(path)),
mtime
};
}
export function allFilesInDirInHost(
host: Tree,
path: Path,
options: {
recursive: boolean;
} = { recursive: true }
): Path[] {
const dir = host.getDir(path);
const res: Path[] = [];
dir.subfiles.forEach(p => {
res.push(join(path, p));
});
if (!options.recursive) {
return res;
}
dir.subdirs.forEach(p => {
res.push(...allFilesInDirInHost(host, join(path, p)));
});
return res;
}
/**
* This method is specifically for updating JSON in a Tree
* @param path Path of JSON file in the Tree

View File

@ -33,6 +33,7 @@ export function createEmptyWorkspace(tree: Tree): Tree {
tree.create(
'/package.json',
JSON.stringify({
name: 'test-name',
dependencies: {},
devDependencies: {}
})

View File

@ -55,6 +55,7 @@ cp README.md build/packages/tao
cp README.md build/packages/eslint-plugin-nx
cp README.md build/packages/linter
cp README.md build/packages/insights
cp README.md build/packages/bazel
cp LICENSE build/packages/builders
cp LICENSE build/packages/schematics
@ -76,6 +77,7 @@ cp LICENSE build/packages/tao
cp LICENSE build/packages/eslint-plugin-nx
cp LICENSE build/packages/linter
cp LICENSE build/packages/insights
cp LICENSE build/packages/bazel
echo "Nx libraries available at build/packages:"
ls build/packages

View File

@ -17,13 +17,13 @@ cd build/packages
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i "" "s|exports.nxVersion = '\*';|exports.nxVersion = '$NX_VERSION';|g" {react,next,web,jest,node,express,nest,cypress,storybook,angular,workspace}/src/utils/versions.js
sed -i "" "s|\*|$NX_VERSION|g" {schematics,react,next,web,jest,node,express,nest,cypress,storybook,angular,workspace,cli,linter,insights,tao,eslint-plugin-nx,create-nx-workspace}/package.json
sed -i "" "s|\*|$NX_VERSION|g" {schematics,react,next,web,jest,node,express,nest,cypress,storybook,angular,workspace,cli,linter,bazel,insights,tao,eslint-plugin-nx,create-nx-workspace}/package.json
sed -i "" "s|NX_VERSION|$NX_VERSION|g" create-nx-workspace/bin/create-nx-workspace.js
sed -i "" "s|ANGULAR_CLI_VERSION|$ANGULAR_CLI_VERSION|g" create-nx-workspace/bin/create-nx-workspace.js
sed -i "" "s|TYPESCRIPT_VERSION|$TYPESCRIPT_VERSION|g" create-nx-workspace/bin/create-nx-workspace.js
else
sed -i "s|exports.nxVersion = '\*';|exports.nxVersion = '$NX_VERSION';|g" {react,next,web,jest,node,express,nest,cypress,storybook,angular,workspace}/src/utils/versions.js
sed -i "s|\*|$NX_VERSION|g" {schematics,react,next,web,jest,node,express,nest,cypress,storybook,angular,workspace,cli,linter,insights,tao,eslint-plugin-nx,create-nx-workspace}/package.json
sed -i "s|\*|$NX_VERSION|g" {schematics,react,next,web,jest,node,express,nest,cypress,storybook,angular,workspace,cli,linter,bazel,insights,tao,eslint-plugin-nx,create-nx-workspace}/package.json
sed -i "s|NX_VERSION|$NX_VERSION|g" create-nx-workspace/bin/create-nx-workspace.js
sed -i "s|ANGULAR_CLI_VERSION|$ANGULAR_CLI_VERSION|g" create-nx-workspace/bin/create-nx-workspace.js
sed -i "s|TYPESCRIPT_VERSION|$TYPESCRIPT_VERSION|g" create-nx-workspace/bin/create-nx-workspace.js
@ -31,9 +31,9 @@ fi
if [[ $NX_VERSION == "*" ]]; then
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -E -i "" "s|\"@nrwl\/([^\"]+)\": \"\\*\"|\"@nrwl\/\1\": \"file:$PWD\/\1\"|" {schematics,jest,web,react,next,node,express,nest,cypress,storybook,angular,workspace,insights,linter,cli,tao,eslint-plugin-nx,create-nx-workspace}/package.json
sed -E -i "" "s|\"@nrwl\/([^\"]+)\": \"\\*\"|\"@nrwl\/\1\": \"file:$PWD\/\1\"|" {schematics,jest,web,react,next,node,express,nest,cypress,storybook,angular,workspace,insights,linter,bazel,cli,tao,eslint-plugin-nx,create-nx-workspace}/package.json
else
echo $PWD
sed -E -i "s|\"@nrwl\/([^\"]+)\": \"\\*\"|\"@nrwl\/\1\": \"file:$PWD\/\1\"|" {schematics,jest,web,react,next,node,express,nest,cypress,storybook,angular,workspace,insights,linter,cli,tao,eslint-plugin-nx,create-nx-workspace}/package.json
sed -E -i "s|\"@nrwl\/([^\"]+)\": \"\\*\"|\"@nrwl\/\1\": \"file:$PWD\/\1\"|" {schematics,jest,web,react,next,node,express,nest,cypress,storybook,angular,workspace,insights,linter,bazel,cli,tao,eslint-plugin-nx,create-nx-workspace}/package.json
fi
fi

View File

@ -24,7 +24,6 @@
"exclude": [
"tmp",
"build",
"packages/bazel",
"packages/schematics/src/*/files/**/*",
"packages/workspace/src/*/files/**/*",
"packages/jest/src/*/files/**/*",