feat: add bazel build utils

This commit is contained in:
vsavkin 2017-08-23 10:38:57 -04:00
parent f23eb23b96
commit 0a8bb236cf
10 changed files with 401 additions and 6 deletions

8
src/bazel/BUILD.bazel Normal file
View File

@ -0,0 +1,8 @@
load("@build_bazel_rules_typescript//:defs.bzl", "nodejs_binary")
exports_files(["webpack.config.js", "test.js"])
nodejs_binary(
name = "webpack",
entry_point = "webpack/bin/webpack",
visibility = ["//visibility:public"],
)

0
src/bazel/WORKSPACE Normal file
View File

109
src/bazel/karma.conf.js Normal file
View File

@ -0,0 +1,109 @@
/**
* Warning: the testing rule will change.
*
* Instead of running karma outside of bazel against the bin_dir directory, we will run it as part of the bazel process.
*/
module.exports = function(config) {
const webpackConfig = {
resolveLoader: {
alias: {
"template-loader": '@nrwl/nx/bazel/template-loader'
}
},
module: {
rules: [
{
test: /\.component\.js$/,
use: [
{loader: 'template-loader' }
]
},
{
test: /\.html$/,
use: [
{loader: 'raw-loader' }
]
},
{
test: /\.css$/,
use: [
{loader: 'raw-loader' }
]
}
]
}
};
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: `${config.opts.bin_dir}/${config.opts.app}`,
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
{ pattern: 'test.js', watched: false}
],
// list of files to exclude
exclude: [],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'test.js': ['webpack']
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: config.opts.reporters ? config.opts.reporters : (config.opts.progress ? ['progress'] : ['dots']),
webpack: webpackConfig,
webpackMiddleware: {
stats: 'errors-only'
},
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('karma-webpack')
],
coverageIstanbulReporter: {
reports: [ 'html', 'lcovonly' ],
fixWebpackSourcePaths: true
},
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
// web server port
port: config.opts.port ? config.opts.port : 9876,
// enable / disable colors in the output (reporters and logs)
colors: config.opts.colors,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.opts.log ? config.opts.log: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
// browsers: ['PhantomJS'],
browsers: ['Chrome'],
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity
});
};

View File

@ -0,0 +1,30 @@
var templateUrlRegex = /templateUrl\s*:(\s*['"`](.*?)['"`]\s*([,}]))/gm;
var stylesRegex = /styleUrls *:(\s*\[[^\]]*?\])/g;
var stringRegex = /(['`"])((?:[^\\]\\\1|.)*?)\1/g;
function replaceStringsWithRequires(string) {
return string.replace(stringRegex, function (match, quote, url) {
if (url.charAt(0) !== ".") {
url = "./" + url;
}
return "require('" + url + "')";
});
}
module.exports = function(source, sourcemap) {
// Not cacheable during unit tests;
this.cacheable && this.cacheable();
var newSource = source.replace(templateUrlRegex, function (match, url) {
return "template:" + replaceStringsWithRequires(url);
}).replace(stylesRegex, function (match, urls) {
return "styles:" + replaceStringsWithRequires(urls);
});
// Support for tests
if (this.callback) {
this.callback(null, newSource, sourcemap)
} else {
return newSource;
}
};

62
src/bazel/webpack.bzl Normal file
View File

@ -0,0 +1,62 @@
def _collect_es5_sources_impl(target, ctx):
result = set()
if hasattr(ctx.rule.attr, "srcs"):
for dep in ctx.rule.attr.srcs:
if hasattr(dep, "es5_sources"):
result += dep.es5_sources
if hasattr(target, "typescript"):
result += target.typescript.es5_sources
return struct(es5_sources = result)
_collect_es5_sources = aspect(
_collect_es5_sources_impl,
attr_aspects = ["deps", "srcs"],
)
def _webpack_bundle_impl(ctx):
inputs = set()
for s in ctx.attr.srcs:
if hasattr(s, "es5_sources"):
inputs += s.es5_sources
config = ctx.files.config
if ctx.attr.mode == 'prod':
main = ctx.new_file('bundles/main.bundle.prod.js')
polyfills = ctx.new_file('bundles/polyfills.bundle.prod.js')
vendor = ctx.new_file('bundles/vendor.bundle.prod.js')
styles = ctx.new_file('bundles/styles.bundle.prod.js')
else:
main = ctx.new_file('bundles/main.bundle.js')
polyfills = ctx.new_file('bundles/polyfills.bundle.js')
vendor = ctx.new_file('bundles/vendor.bundle.js')
styles = ctx.new_file('bundles/styles.bundle.js')
inputs += [config]
args = []
if ctx.attr.mode == 'prod':
args += ['-p']
args += ['--config', config.path]
args += ['--env.bin_dir', ctx.configuration.bin_dir.path]
args += ['--env.package', ctx.label.package]
args += ['--env.mode', ctx.attr.mode]
ctx.action(
progress_message = "Webpack bundling %s" % ctx.label,
inputs = inputs.to_list(),
outputs = [main, polyfills, vendor, styles],
executable = ctx.executable._webpack,
arguments = args,
)
return DefaultInfo(files=depset([main, polyfills, vendor, styles]))
webpack_bundle = rule(implementation = _webpack_bundle_impl,
attrs = {
"srcs": attr.label_list(allow_files=True, aspects=[_collect_es5_sources]),
"config": attr.label(allow_single_file=True, mandatory=True),
"mode": attr.string(default="dev"),
"_webpack": attr.label(default=Label("@nrwl//:webpack"), executable=True, cfg="host")
}
)

185
src/bazel/webpack.config.js Normal file
View File

@ -0,0 +1,185 @@
const fs = require('fs');
const path = require('path');
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
const CircularDependencyPlugin = require('circular-dependency-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { NoEmitOnErrorsPlugin, SourceMapDevToolPlugin, NamedModulesPlugin } = require('webpack');
const { GlobCopyWebpackPlugin, NamedLazyChunksWebpackPlugin, BaseHrefWebpackPlugin } = require('@angular/cli/plugins/webpack');
const { CommonsChunkPlugin } = require('webpack').optimize;
const nodeModules = path.join(process.cwd(), 'node_modules');
const realNodeModules = fs.realpathSync(nodeModules);
const entryPoints = ["inline", "polyfills", "styles", "vendor", "main"];
const baseHref = "";
module.exports = function(env) {
const name = path.parse(env.package).name;
const apps = JSON.parse(fs.readFileSync(path.join(process.cwd(), '.angular-cli.json'), 'UTF-8')).apps;
const appConfig = apps.filter(a => a.name === name)[0];
const out = path.join(process.cwd(), env.bin, env.package, 'bundles');
const src = path.join(process.cwd(), env.bin, appConfig.root);
// victor todo: remove it when ng_module rule is fixed
const alias = Object.assign({}, {
'@angular/core/core': '@angular/core/@angular/core.es5',
'@angular/common/common': '@angular/common/@angular/common.es5',
'@angular/platform-browser/platform-browser': '@angular/platform-browser/@angular/platform-browser.es5'
});
return {
"resolve": {
"extensions": [
".js"
],
"modules": [
"./node_modules"
],
"symlinks": true,
alias
},
"resolveLoader": {
"modules": [
"./node_modules"
]
},
"entry": {
"main": [
tsToJs(path.join(src, appConfig.main))
],
"polyfills": [
tsToJs(path.join(src, appConfig.polyfills))
],
"styles": appConfig.styles.map(s => path.join(src, s))
},
"output": {
"path": out,
"filename": "[name].bundle.js",
"chunkFilename": "[id].chunk.js"
},
"module": {
"rules": [
{
"enforce": "pre",
"test": /\.js$/,
"loader": "source-map-loader",
"exclude": [
/(\\|\/)node_modules(\\|\/)/
]
},
{
"test": /\.html$/,
"loader": "raw-loader"
},
{
"test": /\.(eot|svg|cur)$/,
"loader": "file-loader?name=[name].[hash:20].[ext]"
},
{
"test": /\.(jpg|png|webp|gif|otf|ttf|woff|woff2|ani)$/,
"loader": "url-loader?name=[name].[hash:20].[ext]&limit=10000"
}
]
},
"plugins": [
new NoEmitOnErrorsPlugin(),
new GlobCopyWebpackPlugin({
"patterns": [
"assets",
"favicon.ico"
],
"globOptions": {
"cwd": src,
"dot": true,
"ignore": "**/.gitkeep"
}
}),
new ProgressPlugin(),
new CircularDependencyPlugin({
"exclude": /(\\|\/)node_modules(\\|\/)/,
"failOnError": false
}),
new NamedLazyChunksWebpackPlugin(),
new HtmlWebpackPlugin({
"template": path.join(src, 'index.html'),
"filename": "./index.html",
"hash": false,
"inject": true,
"compile": true,
"favicon": false,
"minify": false,
"cache": true,
"showErrors": true,
"chunks": "all",
"excludeChunks": [],
"xhtml": true,
"chunksSortMode": function sort(left, right) {
let leftIndex = entryPoints.indexOf(left.names[0]);
let rightindex = entryPoints.indexOf(right.names[0]);
if (leftIndex > rightindex) {
return 1;
}
else if (leftIndex < rightindex) {
return -1;
}
else {
return 0;
}
}
}),
new BaseHrefWebpackPlugin({}),
new CommonsChunkPlugin({
"name": [
"inline"
],
"minChunks": null
}),
new CommonsChunkPlugin({
"name": [
"vendor"
],
"minChunks": (module) => {
return module.resource
&& (module.resource.startsWith(nodeModules)
|| module.resource.startsWith(realNodeModules));
},
"chunks": [
"main"
]
}),
new SourceMapDevToolPlugin({
"filename": "[file].map[query]",
"moduleFilenameTemplate": "[resource-path]",
"fallbackModuleFilenameTemplate": "[resource-path]?[hash]",
"sourceRoot": "webpack:///"
}),
new CommonsChunkPlugin({
"name": [
"main"
],
"minChunks": 2,
"async": "common"
}),
new NamedModulesPlugin({}),
],
"node": {
"fs": "empty",
"global": true,
"crypto": "empty",
"tls": "empty",
"net": "empty",
"process": true,
"module": false,
"clearImmediate": false,
"setImmediate": false
},
"devServer": {
"historyApiFallback": true
}
};
};
function tsToJs(s) {
return `${s.substring(0, s.length - 3)}.js`;
}

View File

@ -1,6 +1,6 @@
package(default_visibility = ["//visibility:public"]) package(default_visibility = ["//visibility:public"])
load("@build_bazel_rules_angular//:defs.bzl", "ng_module") load("@build_bazel_rules_angular//:defs.bzl", "ng_module")
load("@build_bazel_rules_nrwl//:webpack.bzl", "webpack_bundle") load("@nrwl//:webpack.bzl", "webpack_bundle")
ng_module( ng_module(
name = "compile", name = "compile",
@ -9,6 +9,7 @@ ng_module(
tsconfig = "//:tsconfig.json" tsconfig = "//:tsconfig.json"
) )
# temporary work-around to handle static
genrule( genrule(
name = "copy_static", name = "copy_static",
srcs = ["src"] + glob(["src/**/*"], exclude=["**/*.ts"]), srcs = ["src"] + glob(["src/**/*"], exclude=["**/*.ts"]),
@ -31,5 +32,5 @@ filegroup(
webpack_bundle( webpack_bundle(
name = "<%= name %>", name = "<%= name %>",
srcs = ["compile_and_static"], srcs = ["compile_and_static"],
config = "@build_bazel_rules_nrwl//:webpack.config.js" config = "@nrwl//:webpack.config.js"
) )

View File

@ -9,7 +9,7 @@ import {insert} from '../utility/ast-utils';
function addBootstrap(path: string): Rule { function addBootstrap(path: string): Rule {
return (host: Tree) => { return (host: Tree) => {
const modulePath = `${path}/app/app.module.ts`; const modulePath = `${path}/app/app.module.ts`;
const moduleSource = host.read(modulePath) !.toString('utf-8'); const moduleSource = host.read(modulePath)!.toString('utf-8');
const sourceFile = ts.createSourceFile(modulePath, moduleSource, ts.ScriptTarget.Latest, true); const sourceFile = ts.createSourceFile(modulePath, moduleSource, ts.ScriptTarget.Latest, true);
const importChanges = addImportToModule(sourceFile, modulePath, 'BrowserModule', '@angular/platform-browser'); const importChanges = addImportToModule(sourceFile, modulePath, 'BrowserModule', '@angular/platform-browser');
const bootstrapChanges = addBootstrapToModule(sourceFile, modulePath, 'AppComponent', './app.component'); const bootstrapChanges = addBootstrapToModule(sourceFile, modulePath, 'AppComponent', './app.component');

View File

@ -3,6 +3,7 @@ exports_files(["tsconfig.json"])
load("@build_bazel_rules_angular//:defs.bzl", "ng_external_libraries") load("@build_bazel_rules_angular//:defs.bzl", "ng_external_libraries")
# change to glob(["node_modules/**/*.js", "node_modules/**/*.json", "node_modules/**/*.d.ts"])
filegroup(name = "node_modules", srcs = glob([ filegroup(name = "node_modules", srcs = glob([
# should not be whitelisted # should not be whitelisted
"node_modules/@angular/**", "node_modules/@angular/**",
@ -20,14 +21,13 @@ filegroup(name = "node_modules", srcs = glob([
"node_modules/webpack/**" "node_modules/webpack/**"
], exclude=["node_modules/@angular/cli/**"])) ], exclude=["node_modules/@angular/cli/**"]))
# this should go away soon when we do bootstrap codegen in the scripts.postinstall
ng_external_libraries(name = "ng_libs", srcs = glob([ ng_external_libraries(name = "ng_libs", srcs = glob([
"node_modules/@angular/**" "node_modules/@angular/**"
], exclude = [ ], exclude = [
"node_modules/@angular/cli/**", "node_modules/@angular/cli/**",
"node_modules/@angular/platform-browser/animations*", "node_modules/@angular/platform-browser/animations*",
"node_modules/@angular/platform-browser/animations/**", "node_modules/@angular/platform-browser/animations/**",
# Alex E?
"node_modules/@angular/router*", "node_modules/@angular/router*",
"node_modules/@angular/router/**", "node_modules/@angular/router/**",
])) ]))

View File

@ -1,6 +1,6 @@
# <%= className %> # <%= className %>
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version <%= version %>. This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version EXPERIMENTAL BAZEL.
## Development server ## Development server