From c87c83fbee3241d05e4c43a0c5f1bd4a5088ead9 Mon Sep 17 00:00:00 2001 From: Alex Eagle Date: Mon, 25 May 2020 18:03:40 -0700 Subject: [PATCH] feat(angular): introduce an Angular CLI builder This replaces the one being removed in https://github.com/angular/angular/pull/37190 --- .bazelignore | 1 + .github/BUILD.bazel | 1 + .github/CODEOWNERS | 1 + WORKSPACE | 6 + commitlint.config.js | 1 + packages/angular/BUILD.bazel | 23 ++++ packages/angular/README.md | 29 +++++ packages/angular/package-lock.json | 121 ++++++++++++++++++++ packages/angular/package.json | 29 +++++ packages/angular/src/builders/builders.json | 9 ++ packages/angular/src/builders/index.ts | 37 ++++++ packages/angular/src/builders/schema.d.ts | 34 ++++++ packages/angular/src/builders/schema.json | 31 +++++ packages/angular/tsconfig.json | 8 ++ packages/angular/types.d.ts | 7 ++ 15 files changed, 338 insertions(+) create mode 100644 packages/angular/BUILD.bazel create mode 100644 packages/angular/README.md create mode 100644 packages/angular/package-lock.json create mode 100644 packages/angular/package.json create mode 100644 packages/angular/src/builders/builders.json create mode 100644 packages/angular/src/builders/index.ts create mode 100644 packages/angular/src/builders/schema.d.ts create mode 100644 packages/angular/src/builders/schema.json create mode 100644 packages/angular/tsconfig.json create mode 100644 packages/angular/types.d.ts diff --git a/.bazelignore b/.bazelignore index 6b0c98882b..f0993589e3 100644 --- a/.bazelignore +++ b/.bazelignore @@ -3,3 +3,4 @@ dist bazel-out e2e/symlinked_node_modules_yarn/node_modules e2e/symlinked_node_modules_npm/node_modules/ +packages/angular/node_modules diff --git a/.github/BUILD.bazel b/.github/BUILD.bazel index d55ddc2bca..86fecd7a80 100644 --- a/.github/BUILD.bazel +++ b/.github/BUILD.bazel @@ -7,6 +7,7 @@ generate_codeowners( # do not sort owners = [ "//:OWNERS", + "//packages/angular:OWNERS", "//packages/labs:OWNERS", "//packages/rollup:OWNERS", "//examples:OWNERS.examples_nestjs", diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 42a2492328..fb54ed0b51 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,6 +2,7 @@ # Don't edit it directly * @gregmagolan @soldair @alexeagle +/packages/angular/** @alan-agius4 @alexeagle /packages/labs/** @mrmeku @alexeagle /packages/rollup/** @jbedard @alexeagle /examples/nestjs/** @zachgrayio @zMotivat0r @rayman1104 @siberex @alexeagle diff --git a/WORKSPACE b/WORKSPACE index 15b2063225..c44f0ed30d 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -83,6 +83,12 @@ npm_install( package_lock_json = "//packages/node-patches:package-lock.json", ) +npm_install( + name = "angular_deps", + package_json = "//packages/angular:package.json", + package_lock_json = "//packages/angular:package-lock.json", +) + # Install all Bazel dependencies needed for npm packages that supply Bazel rules load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies") diff --git a/commitlint.config.js b/commitlint.config.js index cfb2a4b10b..bd5af95b10 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -5,6 +5,7 @@ module.exports = { 'scope-enum': [ 2, 'always', [ + 'angular', 'builtin', 'create', 'examples', diff --git a/packages/angular/BUILD.bazel b/packages/angular/BUILD.bazel new file mode 100644 index 0000000000..de35009e25 --- /dev/null +++ b/packages/angular/BUILD.bazel @@ -0,0 +1,23 @@ +load("@build_bazel_rules_nodejs//:tools/defaults.bzl", "codeowners", "pkg_npm") +load("//packages/typescript:index.bzl", "ts_project") + +codeowners( + teams = ["@alan-agius4"], +) + +ts_project( + tsc = "@angular_deps//typescript/bin:tsc", + deps = ["@angular_deps//:node_modules"], +) + +pkg_npm( + name = "npm_package", + srcs = [ + "README.md", + "package.json", + "src/builders/builders.json", + "src/builders/schema.d.ts", + "src/builders/schema.json", + ], + deps = ["tsconfig"], +) diff --git a/packages/angular/README.md b/packages/angular/README.md new file mode 100644 index 0000000000..b64b059d47 --- /dev/null +++ b/packages/angular/README.md @@ -0,0 +1,29 @@ +# Angular support for Bazel + +This package is a replacement for parts of the deprecated @angular/bazel package previously maintained by the Angular team. + +Currently, this only provides an Angular CLI Builder, which can execute Bazel when triggered by `ng build`, `ng test`, etc. +See https://angular.io/guide/cli-builder for more info about Builders. + +This builder assumes you have already created Bazel configurations (WORKSPACE and BUILD files). +There is presently no tooling to generate these automatically that's supported by either Angular team or rules_nodejs maintainers. +See the `@bazel/create` package for a quickstart to creating a Bazel workspace, or look at examples in rules_nodejs. + +To use it, you would just install this package (it doesn't hook into `ng add` because it has no schematics): + +```sh +$ npm install --save-dev @bazel/angular +``` + +Then edit your `angular.json` to invoke Bazel. For example, to have `ng build` do `bazel build //:all` you would edit the `architect` block to have: + +```json +"architect": { + "build": { + "builder": "@bazel/angular:build", + "options": { + "targetLabel": "//:all", + "bazelCommand": "build" + } +} +``` diff --git a/packages/angular/package-lock.json b/packages/angular/package-lock.json new file mode 100644 index 0000000000..5cf5f1fed4 --- /dev/null +++ b/packages/angular/package-lock.json @@ -0,0 +1,121 @@ +{ + "name": "@bazel/angular", + "version": "0.0.0-PLACEHOLDER", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@angular-devkit/architect": { + "version": "0.901.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.901.7.tgz", + "integrity": "sha512-yW/PUEqle55QihOFbmeNXaVTodhfeXkteoFDUpz+YpX3xiQDXDtNbIJSzKOQTojtBKdSMKMvZkQLr+RAa7/1EA==", + "requires": { + "@angular-devkit/core": "9.1.7", + "rxjs": "6.5.4" + } + }, + "@angular-devkit/core": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-9.1.7.tgz", + "integrity": "sha512-guvolu9Cl+qYMTtedLZD9wCqustJjdqzJ2psD2C1Sr1LrX9T0mprmDldR/YnhsitThveJEb6sM/0EvqWxoSvKw==", + "requires": { + "ajv": "6.12.0", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.5.4", + "source-map": "0.7.3" + } + }, + "@bazel/bazelisk": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@bazel/bazelisk/-/bazelisk-1.4.0.tgz", + "integrity": "sha512-VNI/jF7baQiBy4x+u8gmSDsFehqaAuzMyLuCj0j6/aZCZSw2OssytJVj73m8sFYbXgj67D8iYEQ0gbuoafDk6w==" + }, + "@bazel/ibazel": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/@bazel/ibazel/-/ibazel-0.13.1.tgz", + "integrity": "sha512-FO1hBKpzpeBL0adnFYF2Dwl/7gox6ccKM6bb+x26AXrQpLbinXPuTi4zeXRL/MW4383mF6i4RovLCmwUU/YW0w==" + }, + "@types/node": { + "version": "14.0.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.5.tgz", + "integrity": "sha512-90hiq6/VqtQgX8Sp0EzeIsv3r+ellbGj4URKj5j30tLlZvRUpnAe9YbYnjl3pJM93GyXU0tghHhvXHq+5rnCKA==", + "dev": true + }, + "ajv": { + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", + "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "rxjs": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", + "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", + "requires": { + "tslib": "^1.9.0" + } + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + }, + "typescript": { + "version": "3.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.3.tgz", + "integrity": "sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + } + } +} diff --git a/packages/angular/package.json b/packages/angular/package.json new file mode 100644 index 0000000000..4e5e587ee7 --- /dev/null +++ b/packages/angular/package.json @@ -0,0 +1,29 @@ +{ + "name": "@bazel/angular", + "description": "Run Bazel under the Angular CLI", + "license": "Apache-2.0", + "version": "0.0.0-PLACEHOLDER", + "repository": { + "type": "git", + "url": "https://github.com/bazelbuild/rules_nodejs.git", + "directory": "packages/angular" + }, + "bugs": { + "url": "https://github.com/bazelbuild/rules_nodejs/issues" + }, + "keywords": [ + "angular", + "bazel" + ], + "builders": "./src/builders/builders.json", + "dependencies": { + "@bazel/bazelisk": "^1.4.0", + "@bazel/ibazel": "^0.13.1", + "@angular-devkit/architect": "^0.901.7" + }, + "devDependencies": { + "@angular-devkit/core": "^9.1.7", + "@types/node": "^14.0.5", + "typescript": "^3.9.3" + } +} diff --git a/packages/angular/src/builders/builders.json b/packages/angular/src/builders/builders.json new file mode 100644 index 0000000000..eb00811fbd --- /dev/null +++ b/packages/angular/src/builders/builders.json @@ -0,0 +1,9 @@ +{ + "builders": { + "build": { + "implementation": "./index", + "schema": "./schema.json", + "description": "Executes Bazel on a target." + } + } +} diff --git a/packages/angular/src/builders/index.ts b/packages/angular/src/builders/index.ts new file mode 100644 index 0000000000..3a84d37690 --- /dev/null +++ b/packages/angular/src/builders/index.ts @@ -0,0 +1,37 @@ +import {BuilderContext, BuilderOutput, createBuilder} from '@angular-devkit/architect'; +import {JsonObject} from '@angular-devkit/core'; +import {getNativeBinary as bazeliskBin} from '@bazel/bazelisk/bazelisk'; +import {getNativeBinary as ibazelBin} from '@bazel/ibazel'; +import {spawn} from 'child_process'; +import {Schema} from './schema'; + +async function _bazelBuilder( + options: JsonObject&Schema, + context: BuilderContext, + ): Promise { + const {bazelCommand, targetLabel, watch} = options; + const binary = watch ? ibazelBin() : bazeliskBin(); + if (typeof binary !== 'string') { + // this happens if no binary is located for the current platform + return {success: false}; + } else { + try { + const ps = spawn(binary, [bazelCommand, targetLabel], {stdio: 'inherit'}); + + function shutdown() { + ps.kill('SIGTERM'); + } + + process.on('SIGINT', shutdown); + process.on('SIGTERM', shutdown); + return new Promise(resolve => { + ps.on('close', e => resolve({success: e === 0})); + }); + } catch (err) { + context.logger.error(err.message); + return {success: false}; + } + } +} + +export default createBuilder(_bazelBuilder); diff --git a/packages/angular/src/builders/schema.d.ts b/packages/angular/src/builders/schema.d.ts new file mode 100644 index 0000000000..cb46b9b7b5 --- /dev/null +++ b/packages/angular/src/builders/schema.d.ts @@ -0,0 +1,34 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +/** + * Options for Bazel Builder + */ +export interface Schema { + /** + * Common commands supported by Bazel. + */ + bazelCommand: BazelCommand; + /** + * Target to be executed under Bazel. + */ + targetLabel: string; + /** + * If true, watch the filesystem using ibazel. + */ + watch?: boolean; +} + +/** + * Common commands supported by Bazel. + */ +export enum BazelCommand { + Build = 'build', + Run = 'run', + Test = 'test', +} diff --git a/packages/angular/src/builders/schema.json b/packages/angular/src/builders/schema.json new file mode 100644 index 0000000000..fc73a59d9e --- /dev/null +++ b/packages/angular/src/builders/schema.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json-schema.org/schema", + "title": "Bazel builder schema", + "description": "Options for Bazel Builder", + "type": "object", + "properties": { + "targetLabel": { + "type": "string", + "description": "Target to be executed under Bazel." + }, + "bazelCommand": { + "type": "string", + "description": "Common commands supported by Bazel.", + "enum": [ + "run", + "build", + "test" + ] + }, + "watch": { + "type": "boolean", + "description": "If true, watch the filesystem using ibazel.", + "default": false + } + }, + "additionalProperties": false, + "required": [ + "targetLabel", + "bazelCommand" + ] + } diff --git a/packages/angular/tsconfig.json b/packages/angular/tsconfig.json new file mode 100644 index 0000000000..91a19671ad --- /dev/null +++ b/packages/angular/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "strict": true, + "target": "ES2018", + "module": "CommonJS", + "types": ["node"] + } +} diff --git a/packages/angular/types.d.ts b/packages/angular/types.d.ts new file mode 100644 index 0000000000..8f9f72e1f7 --- /dev/null +++ b/packages/angular/types.d.ts @@ -0,0 +1,7 @@ +declare module '@bazel/bazelisk/bazelisk' { + function getNativeBinary(): Promise|string; +} + +declare module '@bazel/ibazel' { + function getNativeBinary(): Promise|string; +}