revert: "revert: "feat(dev-infra): exposed new rule 'component_benchmark' via dev_infra (#36434)" (#36798)" (#36800)

This reverts commit ad8c4cdd75.

PR Close #36800
This commit is contained in:
Wagner Maciel
2020-04-24 13:14:28 -07:00
committed by atscott
parent c7c0c1f626
commit 7be8bb1489
96 changed files with 191 additions and 125 deletions

View File

@ -0,0 +1,12 @@
package(default_visibility = ["//visibility:public"])
# Make source files available for distribution via pkg_npm
filegroup(
name = "files",
srcs = glob(["*"]) + [
"//dev-infra/benchmark/brotli-cli:files",
"//dev-infra/benchmark/browsers:files",
"//dev-infra/benchmark/component_benchmark:files",
"//dev-infra/benchmark/ng_rollup_bundle:files",
],
)

View File

@ -0,0 +1,19 @@
package(default_visibility = ["//visibility:public"])
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
nodejs_binary(
name = "brotli-cli",
data = [
"cli.js",
"@npm//brotli",
],
entry_point = ":cli.js",
visibility = ["//visibility:public"],
)
# Make source files available for distribution via pkg_npm
filegroup(
name = "files",
srcs = glob(["*"]),
)

View File

@ -0,0 +1,21 @@
/**
* @license
* Copyright Google LLC 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
*/
const fs = require('fs');
const compress = require('brotli/compress');
function main(args) {
const output = args[0].substring('--output='.length);
const input = args[1];
const buffer = fs.readFileSync(input);
fs.writeFileSync(output, compress(buffer, {mode: 0, quality: 11}));
}
if (require.main === module) {
main(process.argv.slice(2));
}

View File

@ -0,0 +1,78 @@
# Copyright Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
################################################################################
#
package(default_visibility = ["//visibility:public"])
load("@io_bazel_rules_webtesting//web:web.bzl", "browser", "web_test_archive")
# Override of chromium web_test_archive so that the archive is selected based on platform
web_test_archive(
name = "chromium_archive",
testonly = True,
archive = select({
"@io_bazel_rules_webtesting//common/conditions:linux": "@org_chromium_chromium_amd64//file",
"@io_bazel_rules_webtesting//common/conditions:mac": "@org_chromium_chromium_macos//file",
"@io_bazel_rules_webtesting//common/conditions:windows": "@org_chromium_chromium_windows//file",
}),
extract = "build",
named_files = select({
"@io_bazel_rules_webtesting//common/conditions:linux": {"CHROMIUM": "chrome-linux/chrome"},
"@io_bazel_rules_webtesting//common/conditions:mac": {"CHROMIUM": "chrome-mac/Chromium.app/Contents/MacOS/chromium"},
"@io_bazel_rules_webtesting//common/conditions:windows": {"CHROMIUM": "chrome-win/chrome.exe"},
}),
visibility = ["//dev-infra/benchmark/browsers:__subpackages__"],
)
# Override of chromedriver web_test_archive so that the archive is selected based on platform
web_test_archive(
name = "chromedriver_archive",
testonly = True,
archive = select({
"@io_bazel_rules_webtesting//common/conditions:linux": "@org_chromium_chromedriver_amd64//file",
"@io_bazel_rules_webtesting//common/conditions:mac": "@org_chromium_chromedriver_macos//file",
"@io_bazel_rules_webtesting//common/conditions:windows": "@org_chromium_chromedriver_windows//file",
}),
extract = "build",
named_files = select({
"@io_bazel_rules_webtesting//common/conditions:linux": {
"CHROMEDRIVER": "chromedriver_linux64/chromedriver",
},
"@io_bazel_rules_webtesting//common/conditions:mac": {
"CHROMEDRIVER": "chromedriver_mac64/chromedriver",
},
"@io_bazel_rules_webtesting//common/conditions:windows": {
"CHROMEDRIVER": "chromedriver_win32/chromedriver.exe",
},
}),
visibility = ["//dev-infra/benchmark/browsers:__subpackages__"],
)
browser(
name = "chromium",
metadata = "chromium.json",
visibility = ["//visibility:public"],
deps = [
":chromedriver_archive",
":chromium_archive",
"@io_bazel_rules_webtesting//go/wsl",
],
)
# Make source files available for distribution via pkg_npm
filegroup(
name = "files",
srcs = glob(["*"]),
)

View File

@ -0,0 +1,87 @@
# Copyright 2018 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Pinned browser versions.
This function is here to make browser repositories work with cross-platform RBE.
Unlike the rules_webtesting browser_repositories, this function defines
separate repositories for each platform
"""
load("@io_bazel_rules_webtesting//web/internal:platform_http_file.bzl", _platform_http_file = "platform_http_file")
def platform_http_file(name, licenses, sha256, urls):
"""Platform spepcific browser repository.
This works around a dificiency in io_bazel_rules_webtesting platform_http_file in that
it selects the platform when the repository rule is executed. This limits browsers
tests to run on the local user platform only. For cross-platform RBE we want a repository
to be defined per platform so the correct one can be selected.
"""
_platform_http_file(
name = name,
amd64_sha256 = sha256,
amd64_urls = urls,
licenses = licenses,
macos_sha256 = sha256,
macos_urls = urls,
windows_sha256 = sha256,
windows_urls = urls,
)
def browser_repositories():
"""Load pinned rules_webtesting browser versions."""
platform_http_file(
name = "org_chromium_chromium_amd64",
licenses = ["notice"], # BSD 3-clause (maybe more?)
sha256 = "b1e30c4dec8a451f8fe10d1f2d3c71e491d0333425f32247fe5c80a0a354303d",
urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/664981/chrome-linux.zip"],
)
platform_http_file(
name = "org_chromium_chromium_macos",
licenses = ["notice"], # BSD 3-clause (maybe more?)
sha256 = "7c0ba93616f44a421330b1c1262e8899fbdf7916bed8b04c775e0426f6f35ec6",
urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Mac/665002/chrome-mac.zip"],
)
platform_http_file(
name = "org_chromium_chromium_windows",
licenses = ["notice"], # BSD 3-clause (maybe more?)
sha256 = "f2facd0066270078d0e8999e684595274c359cac3946299a1ceedba2a5de1c63",
urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Win/664999/chrome-win.zip"],
)
platform_http_file(
name = "org_chromium_chromedriver_amd64",
licenses = ["reciprocal"], # BSD 3-clause, ICU, MPL 1.1, libpng (BSD/MIT-like), Academic Free License v. 2.0, BSD 2-clause, MIT
sha256 = "0ead02145854b60a3317b59031205b362fb4cfdb680fef20e95c89582e6e38be",
urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/664981/chromedriver_linux64.zip"],
)
platform_http_file(
name = "org_chromium_chromedriver_macos",
licenses = ["reciprocal"], # BSD 3-clause, ICU, MPL 1.1, libpng (BSD/MIT-like), Academic Free License v. 2.0, BSD 2-clause, MIT
sha256 = "8dd159e27b13b16262afa6993b15321e736c3b484da363c0e03bb050d72522c9",
urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Mac/665002/chromedriver_mac64.zip"],
)
platform_http_file(
name = "org_chromium_chromedriver_windows",
licenses = ["reciprocal"], # BSD 3-clause, ICU, MPL 1.1, libpng (BSD/MIT-like), Academic Free License v. 2.0, BSD 2-clause, MIT
sha256 = "1cc881364974102182257a5c5c2b9cfed513689dee28924ca44df082bdf9fd60",
urls = ["https://commondatastorage.googleapis.com/chromium-browser-snapshots/Win/664999/chromedriver_win32.zip"],
)

View File

@ -0,0 +1,20 @@
{
"environment" : "local",
"capabilities" : {
"browserName" : "chrome",
"goog:chromeOptions" : {
"binary" : "%FILE:CHROMIUM%",
"args" : [
"--headless",
"--use-gl=swiftshader-webgl"
]
},
"google:wslConfig": {
"binary": "%FILE:CHROMEDRIVER%",
"port":"%WSLPORT:WSL%",
"args": ["--port=%WSLPORT:WSL%"],
"status": true,
"shutdown": true
}
}
}

View File

@ -0,0 +1,12 @@
package(default_visibility = ["//visibility:public"])
exports_files([
"protractor-perf.conf.js",
"start-server.js",
])
# Make source files available for distribution via pkg_npm
filegroup(
name = "files",
srcs = glob(["*"]) + ["//dev-infra/benchmark/component_benchmark/defaults:files"],
)

View File

@ -0,0 +1,27 @@
load("@npm_bazel_protractor//:index.bzl", "protractor_web_test_suite")
"""
Macro that can be used to define a benchmark test. This differentiates from
a normal Protractor test suite because we specify a custom "perf" configuration
that sets up "@angular/benchpress". Benchmark test targets will not run on CI
unless explicitly requested.
"""
def benchmark_test(name, server, tags = [], **kwargs):
protractor_web_test_suite(
name = name,
browsers = ["//dev-infra/benchmark/browsers:chromium"],
configuration = "//dev-infra/benchmark/component_benchmark:protractor-perf.conf.js",
on_prepare = "//dev-infra/benchmark/component_benchmark:start-server.js",
server = server,
# Benchmark targets should not run on CI by default.
tags = tags + [
"manual",
"no-remote-exec",
],
test_suite_tags = [
"manual",
"no-remote-exec",
],
**kwargs
)

View File

@ -0,0 +1,145 @@
load("//dev-infra/benchmark/ng_rollup_bundle:ng_rollup_bundle.bzl", "ng_rollup_bundle")
load("//tools:defaults.bzl", "ng_module")
load("@npm_bazel_typescript//:index.bzl", "ts_devserver", "ts_library")
load(":benchmark_test.bzl", "benchmark_test")
def copy_default_file(origin, destination):
"""
Copies a file from /defaults to the destination.
Args:
origin: The name of a file in benchpress/defaults to be copied.
destination: Where the original file will be clopied to.
"""
native.genrule(
name = "copy_default_" + origin + "_file_genrule",
srcs = ["//dev-infra/benchmark/component_benchmark/defaults:" + origin],
outs = [destination],
cmd = "cat $(SRCS) >> $@",
)
def component_benchmark(
name,
prefix,
driver,
driver_deps,
ng_srcs,
ng_deps,
assets = None,
styles = None,
entry_point = None,
entry_point_deps = [
"//packages/core",
"//packages/platform-browser",
]):
"""
Runs a benchmark test against the given angular app using the given driver.
This rule was created with the intention of reducing the amount of
duplicate/boilderplate code, while also allowing you to be as verbose with
your app as you'd like. The goal being that if you just want to test a
simple component, the only thing you'd need to provide are the component
(via ng_srcs) and driver.
** USAGE NOTES **
(assets/styles): The default index.html imports a stylesheet named
"styles.css". This allows the use of the default index.html with a custom
stylesheet through the styles arg by providing either a styles.css in the
prefix directory or by providing a css binary named styles.css.
(assets): The default index.html expects that the root selector for
the benchmark app is "app-root".
(entry_point): The default entry_point expects a file named "app.module" to export
the root NgModule for the benchmark application. It also expects that the
root NgModule is named "AppModule".
TIP: The server is named `name + "_server"` so that you can view/debug the
app.
Args:
name: The name of the benchmark_test to be run
prefix: The relative path to the root directory of the benchmark app
driver: The ts driver for running the benchmark
driver_deps: Driver's dependencies
ng_srcs: All of the ts srcs for the angular app
ng_deps: Dependencies for the angular app
assets: Static files
styles: Stylesheets
entry_point: Main entry point for the angular app
entry_point_deps: Entry point's dependencies
"""
app_lib = name + "_app_lib"
app_main = name + "_app_main"
benchmark_driver = name + "_driver"
server = name + "_server"
# If the user doesn't provide assets, entry_point, or styles, we use a
# default version.
# Note that we copy the default files to the same directory as what is used
# by the app for three reasons:
# 1. To avoid having the entry point be defined in a different package from
# where this macro is called.
# 2. So that we can use relative paths for imports in entry point.
# 3. To make using default static files as seamless as possible.
if not entry_point:
entry_point = prefix + "default_index.ts"
ng_srcs.append(entry_point)
copy_default_file("index.ts", entry_point)
if not assets:
html = prefix + "index.html"
assets = [html]
copy_default_file("index.html", html)
if not styles:
css = prefix + "styles.css"
styles = [css]
copy_default_file("styles.css", css)
# Bootstraps the application and creates
# additional files to be imported by the entry_point file.
ng_module(
name = app_lib,
srcs = ng_srcs,
# Creates ngFactory and ngSummary to be imported by the app's entry point.
generate_ve_shims = True,
deps = ng_deps,
tsconfig = "//dev-infra/benchmark/component_benchmark:tsconfig-e2e.json",
)
# Bundle the application (needed by ts_devserver).
ng_rollup_bundle(
name = app_main,
entry_point = entry_point,
deps = [":" + app_lib] + entry_point_deps,
)
# The ts_library for the driver that runs tests against the benchmark app.
ts_library(
name = benchmark_driver,
tsconfig = "//dev-infra/benchmark/component_benchmark:tsconfig-e2e.json",
testonly = True,
srcs = [driver],
deps = driver_deps,
)
# The server for our application.
ts_devserver(
name = server,
bootstrap = ["//packages/zone.js/dist:zone.js"],
port = 4200,
static_files = assets + styles,
deps = [":" + app_main + ".min_debug.es2015.js"],
additional_root_paths = ["//dev-infra/benchmark/component_benchmark/defaults"],
serving_path = "/app_bundle.js",
)
# Runs a protractor test that's set up to use @angular/benchpress.
benchmark_test(
name = name,
server = ":" + server,
deps = [":" + benchmark_driver],
)

View File

@ -0,0 +1,13 @@
package(default_visibility = ["//visibility:public"])
# Make source files available for distribution via pkg_npm
filegroup(
name = "files",
srcs = glob(["*"]),
)
exports_files([
"index.html",
"index.ts",
"styles.css",
])

View File

@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Benchmark Test</title>
<!-- Prevent favicon.ico requests -->
<link rel="icon" href="data:,">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<app-root id="root">Loading...</app-root>
<script src="/app_bundle.js"></script>
</body>
</html>

View File

@ -0,0 +1,19 @@
/**
* @license
* Copyright Google LLC 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
*/
// @ts-ignore Cannot find module
import {enableProdMode} from '@angular/core';
// @ts-ignore Cannot find module
import {platformBrowser} from '@angular/platform-browser';
// @ts-ignore Cannot find module
import {AppModuleNgFactory} from './app.module.ngfactory';
enableProdMode();
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);

View File

@ -0,0 +1,7 @@
/*
* This file exists so that if the default index.html is used a 404 will not
* throw.
*
* We leave an import for "styles.css" in the default index.html for the case
* where someone wants to use index.html and provide their own styles.
*/

View File

@ -0,0 +1,43 @@
/**
* @license
* Copyright Google LLC 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
*/
const CHROME_OPTIONS = {
'args': ['--js-flags=--expose-gc', '--no-sandbox', '--headless', '--disable-dev-shm-usage'],
'perfLoggingPrefs': {
'traceCategories':
'v8,blink.console,devtools.timeline,disabled-by-default-devtools.timeline,blink.user_timing'
}
};
exports.config = {
onPrepare: function() {
beforeEach(function() {
browser.ignoreSynchronization = false;
});
},
restartBrowserBetweenTests: true,
allScriptsTimeout: 11000,
capabilities: {
'browserName': 'chrome',
chromeOptions: CHROME_OPTIONS,
loggingPrefs: {
performance: 'ALL',
browser: 'ALL',
}
},
directConnect: true,
framework: 'jasmine2',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 90000,
print: function(msg) {
console.log(msg);
},
},
useAllAngular2AppRoots: true
};

View File

@ -0,0 +1,18 @@
/**
* @license
* Copyright Google LLC 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
*/
const protractorUtils = require('@bazel/protractor/protractor-utils');
const protractor = require('protractor');
module.exports = async function(config) {
const {port} = await protractorUtils.runServer(config.workspace, config.server, '-port', []);
const processedConfig = await protractor.browser.getProcessedConfig();
const serverUrl = `http://localhost:${port}`;
return processedConfig.baseUrl = protractor.browser.baseUrl = serverUrl;
};

View File

@ -0,0 +1,6 @@
{
"compilerOptions": {
"lib": ["es2015", "dom"],
"types": ["node", "jasmine"]
}
}

View File

@ -0,0 +1,20 @@
package(default_visibility = ["//visibility:public"])
load("@npm_bazel_typescript//:index.bzl", "ts_library")
ts_library(
name = "driver-utilities",
srcs = glob(["*.ts"]),
module_name = "@angular/dev-infra/benchmark/driver-utilities",
tsconfig = "//dev-infra/benchmark/component_benchmark:tsconfig-e2e.json",
deps = [
"//packages/benchpress",
"@npm//@types/fs-extra",
"@npm//@types/node",
"@npm//@types/selenium-webdriver",
"@npm//fs-extra",
"@npm//node-uuid",
"@npm//protractor",
"@npm//selenium-webdriver",
],
)

View File

@ -0,0 +1,50 @@
/**
* @license
* Copyright Google LLC 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
*/
/* tslint:disable:no-console */
import {browser} from 'protractor';
import * as webdriver from 'selenium-webdriver';
declare var expect: any;
export function openBrowser(config: {
url: string,
params?: {name: string, value: any}[],
ignoreBrowserSynchronization?: boolean
}) {
if (config.ignoreBrowserSynchronization) {
browser.ignoreSynchronization = true;
}
const urlParams: string[] = [];
if (config.params) {
config.params.forEach((param) => urlParams.push(param.name + '=' + param.value));
}
const url = encodeURI(config.url + '?' + urlParams.join('&'));
browser.get(url);
if (config.ignoreBrowserSynchronization) {
browser.sleep(2000);
}
}
/**
* @experimental This API will be moved to Protractor.
*/
export function verifyNoBrowserErrors() {
// TODO(tbosch): Bug in ChromeDriver: Need to execute at least one command
// so that the browser logs can be read out!
browser.executeScript('1+1');
browser.manage().logs().get('browser').then(function(browserLog: any) {
const filteredLog = browserLog.filter(function(logEntry: any) {
if (logEntry.level.value >= webdriver.logging.Level.INFO.value) {
console.log('>> ' + logEntry.message);
}
return logEntry.level.value > webdriver.logging.Level.WARNING.value;
});
expect(filteredLog).toEqual([]);
});
}

View File

@ -0,0 +1,9 @@
/**
* @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
*/
export {openBrowser, verifyNoBrowserErrors} from './e2e_util';
export {runBenchmark} from './perf_util';

View File

@ -0,0 +1,75 @@
/**
* @license
* Copyright Google LLC 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
*/
export {verifyNoBrowserErrors} from './e2e_util';
const nodeUuid = require('node-uuid');
import * as fs from 'fs-extra';
import {SeleniumWebDriverAdapter, Options, JsonFileReporter, Validator, RegressionSlopeValidator, ConsoleReporter, SizeValidator, MultiReporter, MultiMetric, Runner, StaticProvider} from '@angular/benchpress';
import {openBrowser} from './e2e_util';
// Note: Keep the `modules/benchmarks/README.md` file in sync with the supported options.
const globalOptions = {
sampleSize: process.env.PERF_SAMPLE_SIZE || 20,
forceGc: process.env.PERF_FORCE_GC === 'true',
dryRun: process.env.PERF_DRYRUN === 'true',
};
const runner = createBenchpressRunner();
export function runBenchmark(config: {
id: string,
url: string,
params: {name: string, value: any}[],
ignoreBrowserSynchronization?: boolean,
microMetrics?: {[key: string]: string},
work?: () => void,
prepare?: () => void,
setup?: () => void
}): Promise<any> {
openBrowser(config);
if (config.setup) {
config.setup();
}
const description: {[key: string]: any} = {};
config.params.forEach((param) => description[param.name] = param.value);
return runner.sample({
id: config.id,
execute: config.work,
prepare: config.prepare,
microMetrics: config.microMetrics,
providers: [{provide: Options.SAMPLE_DESCRIPTION, useValue: {}}]
});
}
function createBenchpressRunner(): Runner {
let runId = nodeUuid.v1();
if (process.env.GIT_SHA) {
runId = process.env.GIT_SHA + ' ' + runId;
}
const resultsFolder = './dist/benchmark_results';
fs.ensureDirSync(resultsFolder);
const providers: StaticProvider[] = [
SeleniumWebDriverAdapter.PROTRACTOR_PROVIDERS,
{provide: Options.FORCE_GC, useValue: globalOptions.forceGc},
{provide: Options.DEFAULT_DESCRIPTION, useValue: {'runId': runId}}, JsonFileReporter.PROVIDERS,
{provide: JsonFileReporter.PATH, useValue: resultsFolder}
];
if (!globalOptions.dryRun) {
providers.push({provide: Validator, useExisting: RegressionSlopeValidator});
providers.push(
{provide: RegressionSlopeValidator.SAMPLE_SIZE, useValue: globalOptions.sampleSize});
providers.push(MultiReporter.provideWith([ConsoleReporter, JsonFileReporter]));
} else {
providers.push({provide: Validator, useExisting: SizeValidator});
providers.push({provide: SizeValidator.SAMPLE_SIZE, useValue: 1});
providers.push(MultiReporter.provideWith([]));
providers.push(MultiMetric.provideWith([]));
}
return new Runner(providers);
}

View File

@ -0,0 +1,26 @@
package(default_visibility = ["//visibility:public"])
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
exports_files([
"rollup.config.js",
"terser_config.json",
])
nodejs_binary(
name = "rollup_with_build_optimizer",
data = [
"@npm//@angular-devkit/build-optimizer",
"@npm//rollup",
"@npm//rollup-plugin-commonjs",
"@npm//rollup-plugin-node-resolve",
"@npm//rollup-plugin-sourcemaps",
],
entry_point = "@npm//:node_modules/rollup/dist/bin/rollup",
)
# Make source files available for distribution via pkg_npm
filegroup(
name = "files",
srcs = glob(["*"]),
)

View File

@ -0,0 +1,471 @@
# Copyright Google LLC 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
"""Rollup with Build Optimizer
This provides a variant of the [rollup_bundle] rule that works better for Angular apps.
It registers `@angular-devkit/build-optimizer` as a rollup plugin, to get
better optimization. It also uses ESM5 format inputs, as this is what
build-optimizer is hard-coded to look for and transform.
[rollup_bundle]: https://bazelbuild.github.io/rules_nodejs/rollup/rollup_bundle.html
"""
load("@build_bazel_rules_nodejs//:index.bzl", "npm_package_bin")
load("@build_bazel_rules_nodejs//:providers.bzl", "JSEcmaScriptModuleInfo", "NpmPackageInfo", "node_modules_aspect")
load("//packages/bazel/src:esm5.bzl", "esm5_outputs_aspect", "esm5_root_dir", "flatten_esm5")
load("@npm_bazel_terser//:index.bzl", "terser_minified")
_NG_ROLLUP_BUNDLE_OUTPUTS = {
"bundle": "%{name}.js",
"sourcemap": "%{name}.js.map",
}
_NG_ROLLUP_MODULE_MAPPINGS_ATTR = "ng_rollup_module_mappings"
def _ng_rollup_module_mappings_aspect_impl(target, ctx):
mappings = dict()
for dep in ctx.rule.attr.deps:
if hasattr(dep, _NG_ROLLUP_MODULE_MAPPINGS_ATTR):
for k, v in getattr(dep, _NG_ROLLUP_MODULE_MAPPINGS_ATTR).items():
if k in mappings and mappings[k] != v:
fail(("duplicate module mapping at %s: %s maps to both %s and %s" %
(target.label, k, mappings[k], v)), "deps")
mappings[k] = v
if ((hasattr(ctx.rule.attr, "module_name") and ctx.rule.attr.module_name) or
(hasattr(ctx.rule.attr, "module_root") and ctx.rule.attr.module_root)):
mn = ctx.rule.attr.module_name
if not mn:
mn = target.label.name
mr = target.label.package
if target.label.workspace_root:
mr = "%s/%s" % (target.label.workspace_root, mr)
if ctx.rule.attr.module_root and ctx.rule.attr.module_root != ".":
if ctx.rule.attr.module_root.endswith(".ts"):
# This is the type-checking module mapping. Strip the trailing .d.ts
# as it doesn't belong in TypeScript's path mapping.
mr = "%s/%s" % (mr, ctx.rule.attr.module_root.replace(".d.ts", ""))
else:
mr = "%s/%s" % (mr, ctx.rule.attr.module_root)
if mn in mappings and mappings[mn] != mr:
fail(("duplicate module mapping at %s: %s maps to both %s and %s" %
(target.label, mn, mappings[mn], mr)), "deps")
mappings[mn] = mr
return struct(ng_rollup_module_mappings = mappings)
ng_rollup_module_mappings_aspect = aspect(
_ng_rollup_module_mappings_aspect_impl,
attr_aspects = ["deps"],
)
_NG_ROLLUP_BUNDLE_DEPS_ASPECTS = [esm5_outputs_aspect, ng_rollup_module_mappings_aspect, node_modules_aspect]
_NG_ROLLUP_BUNDLE_ATTRS = {
"build_optimizer": attr.bool(
doc = """Use build optimizer plugin
Only used if sources are esm5 which depends on value of esm5_sources.""",
default = True,
),
"esm5_sources": attr.bool(
doc = """Use esm5 input sources""",
default = True,
),
"srcs": attr.label_list(
doc = """JavaScript source files from the workspace.
These can use ES2015 syntax and ES Modules (import/export)""",
allow_files = True,
),
"entry_point": attr.label(
doc = """The starting point of the application, passed as the `--input` flag to rollup.
If the entry JavaScript file belongs to the same package (as the BUILD file),
you can simply reference it by its relative name to the package directory:
```
ng_rollup_bundle(
name = "bundle",
entry_point = ":main.js",
)
```
You can specify the entry point as a typescript file so long as you also include
the ts_library target in deps:
```
ts_library(
name = "main",
srcs = ["main.ts"],
)
ng_rollup_bundle(
name = "bundle",
deps = [":main"]
entry_point = ":main.ts",
)
```
The rule will use the corresponding `.js` output of the ts_library rule as the entry point.
If the entry point target is a rule, it should produce a single JavaScript entry file that will be passed to the nodejs_binary rule.
For example:
```
filegroup(
name = "entry_file",
srcs = ["main.js"],
)
ng_rollup_bundle(
name = "bundle",
entry_point = ":entry_file",
)
```
""",
mandatory = True,
allow_single_file = True,
),
"deps": attr.label_list(
doc = """Other targets that provide JavaScript files.
Typically this will be `ts_library` or `ng_module` targets.""",
aspects = _NG_ROLLUP_BUNDLE_DEPS_ASPECTS,
),
"format": attr.string(
doc = """"Specifies the format of the generated bundle. One of the following:
- `amd`: Asynchronous Module Definition, used with module loaders like RequireJS
- `cjs`: CommonJS, suitable for Node and other bundlers
- `esm`: Keep the bundle as an ES module file, suitable for other bundlers and inclusion as a `<script type=module>` tag in modern browsers
- `iife`: A self-executing function, suitable for inclusion as a `<script>` tag. (If you want to create a bundle for your application, you probably want to use this.)
- `umd`: Universal Module Definition, works as amd, cjs and iife all in one
- `system`: Native format of the SystemJS loader
""",
values = ["amd", "cjs", "esm", "iife", "umd", "system"],
default = "esm",
),
"global_name": attr.string(
doc = """A name given to this package when referenced as a global variable.
This name appears in the bundle module incantation at the beginning of the file,
and governs the global symbol added to the global context (e.g. `window`) as a side-
effect of loading the UMD/IIFE JS bundle.
Rollup doc: "The variable name, representing your iife/umd bundle, by which other scripts on the same page can access it."
This is passed to the `output.name` setting in Rollup.""",
),
"globals": attr.string_dict(
doc = """A dict of symbols that reference external scripts.
The keys are variable names that appear in the program,
and the values are the symbol to reference at runtime in a global context (UMD bundles).
For example, a program referencing @angular/core should use ng.core
as the global reference, so Angular users should include the mapping
`"@angular/core":"ng.core"` in the globals.""",
default = {},
),
"license_banner": attr.label(
doc = """A .txt file passed to the `banner` config option of rollup.
The contents of the file will be copied to the top of the resulting bundles.
Note that you can replace a version placeholder in the license file, by using
the special version `0.0.0-PLACEHOLDER`. See the section on stamping in the README.""",
allow_single_file = [".txt"],
),
"_rollup": attr.label(
executable = True,
cfg = "host",
default = Label("//dev-infra/benchmark/ng_rollup_bundle:rollup_with_build_optimizer"),
),
"_rollup_config_tmpl": attr.label(
default = Label("//dev-infra/benchmark/ng_rollup_bundle:rollup.config.js"),
allow_single_file = True,
),
}
def _compute_node_modules_root(ctx):
"""Computes the node_modules root from the node_modules and deps attributes.
Args:
ctx: the skylark execution context
Returns:
The node_modules root as a string
"""
node_modules_root = None
for d in ctx.attr.deps:
if NpmPackageInfo in d:
possible_root = "/".join(["external", d[NpmPackageInfo].workspace, "node_modules"])
if not node_modules_root:
node_modules_root = possible_root
elif node_modules_root != possible_root:
fail("All npm dependencies need to come from a single workspace. Found '%s' and '%s'." % (node_modules_root, possible_root))
if not node_modules_root:
# there are no fine grained deps but we still need a node_modules_root even if its empty
node_modules_root = "external/npm/node_modules"
return node_modules_root
# Avoid using non-normalized paths (workspace/../other_workspace/path)
def _to_manifest_path(ctx, file):
if file.short_path.startswith("../"):
return file.short_path[3:]
else:
return ctx.workspace_name + "/" + file.short_path
# Expand entry_point into runfiles and strip the file extension
def _esm5_entry_point_path(ctx):
return _to_manifest_path(ctx, ctx.file.entry_point)[:-(len(ctx.file.entry_point.extension) + 1)]
def _no_ext(f):
return f.short_path[:-len(f.extension) - 1]
def _resolve_js_input(f, inputs):
if f.extension == "js" or f.extension == "mjs":
return f
# look for corresponding js file in inputs
no_ext = _no_ext(f)
for i in inputs:
if i.extension == "js" or i.extension == "mjs":
if _no_ext(i) == no_ext:
return i
fail("Could not find corresponding javascript entry point for %s. Add the %s.js to your deps." % (f.path, no_ext))
def _write_rollup_config(ctx, root_dir, build_optimizer, filename = "_%s.rollup.conf.js"):
"""Generate a rollup config file.
Args:
ctx: Bazel rule execution context
root_dir: root directory for module resolution
build_optimizer: whether to enable Build Optimizer plugin
filename: output filename pattern (defaults to `_%s.rollup.conf.js`)
Returns:
The rollup config file. See https://rollupjs.org/guide/en#configuration-files
"""
config = ctx.actions.declare_file(filename % ctx.label.name)
mappings = dict()
all_deps = ctx.attr.deps + ctx.attr.srcs
for dep in all_deps:
if hasattr(dep, _NG_ROLLUP_MODULE_MAPPINGS_ATTR):
for k, v in getattr(dep, _NG_ROLLUP_MODULE_MAPPINGS_ATTR).items():
if k in mappings and mappings[k] != v:
fail(("duplicate module mapping at %s: %s maps to both %s and %s" %
(dep.label, k, mappings[k], v)), "deps")
mappings[k] = v
globals = {}
external = []
if ctx.attr.globals:
globals = ctx.attr.globals.items()
external = ctx.attr.globals.keys()
ctx.actions.expand_template(
output = config,
template = ctx.file._rollup_config_tmpl,
substitutions = {
"TMPL_banner_file": "\"%s\"" % ctx.file.license_banner.path if ctx.file.license_banner else "undefined",
"TMPL_build_optimizer": "true" if build_optimizer else "false",
"TMPL_module_mappings": str(mappings),
"TMPL_node_modules_root": _compute_node_modules_root(ctx),
"TMPL_root_dir": root_dir,
"TMPL_stamp_data": "\"%s\"" % ctx.version_file.path if ctx.version_file else "undefined",
"TMPL_workspace_name": ctx.workspace_name,
"TMPL_external": ", ".join(["'%s'" % e for e in external]),
"TMPL_globals": ", ".join(["'%s': '%s'" % g for g in globals]),
"TMPL_ivy_enabled": "true" if ctx.var.get("angular_ivy_enabled", None) == "True" else "false",
},
)
return config
def _filter_js_inputs(all_inputs):
all_inputs_list = all_inputs.to_list() if type(all_inputs) == type(depset()) else all_inputs
return [
f
for f in all_inputs_list
if f.path.endswith(".js") or f.path.endswith(".mjs") or f.path.endswith(".json")
]
def _run_rollup(ctx, entry_point_path, sources, config):
args = ctx.actions.args()
args.add("--config", config.path)
args.add("--input", entry_point_path)
args.add("--output.file", ctx.outputs.bundle)
args.add("--output.name", ctx.attr.global_name if ctx.attr.global_name else ctx.label.name)
args.add("--output.format", ctx.attr.format)
args.add("--output.sourcemap")
args.add("--output.sourcemapFile", ctx.outputs.sourcemap)
# We will produce errors as needed. Anything else is spammy: a well-behaved
# bazel rule prints nothing on success.
args.add("--silent")
args.add("--preserveSymlinks")
direct_inputs = [config]
# Also include files from npm fine grained deps as inputs.
# These deps are identified by the NpmPackageInfo provider.
for d in ctx.attr.deps:
if NpmPackageInfo in d:
# Note: we can't avoid calling .to_list() on sources
direct_inputs.extend(_filter_js_inputs(d[NpmPackageInfo].sources.to_list()))
if ctx.file.license_banner:
direct_inputs.append(ctx.file.license_banner)
if ctx.version_file:
direct_inputs.append(ctx.version_file)
ctx.actions.run(
progress_message = "Bundling JavaScript %s [rollup]" % ctx.outputs.bundle.short_path,
executable = ctx.executable._rollup,
inputs = depset(direct_inputs, transitive = [sources]),
outputs = [ctx.outputs.bundle, ctx.outputs.sourcemap],
arguments = [args],
)
def _ng_rollup_bundle_impl(ctx):
if ctx.attr.esm5_sources:
# Use esm5 sources and build optimzier if ctx.attr.build_optimizer is set
rollup_config = _write_rollup_config(ctx, build_optimizer = ctx.attr.build_optimizer, root_dir = "/".join([ctx.bin_dir.path, ctx.label.package, esm5_root_dir(ctx)]))
_run_rollup(ctx, _esm5_entry_point_path(ctx), flatten_esm5(ctx), rollup_config)
else:
# Use esm2015 sources and no build optimzier
rollup_config = _write_rollup_config(ctx, build_optimizer = False, root_dir = ctx.bin_dir.path)
esm2015_files_depsets = []
for dep in ctx.attr.deps:
if JSEcmaScriptModuleInfo in dep:
esm2015_files_depsets.append(dep[JSEcmaScriptModuleInfo].sources)
esm2015_files = depset(transitive = esm2015_files_depsets)
entry_point_path = _to_manifest_path(ctx, _resolve_js_input(ctx.file.entry_point, esm2015_files.to_list()))
_run_rollup(ctx, entry_point_path, esm2015_files, rollup_config)
return DefaultInfo(files = depset([ctx.outputs.bundle, ctx.outputs.sourcemap]))
_ng_rollup_bundle = rule(
implementation = _ng_rollup_bundle_impl,
attrs = _NG_ROLLUP_BUNDLE_ATTRS,
outputs = _NG_ROLLUP_BUNDLE_OUTPUTS,
)
"""
Run [Rollup] with the [Build Optimizer] plugin and use esm5 inputs.
[Rollup]: https://rollupjs.org/
[Build Optimizer]: https://www.npmjs.com/package/@angular-devkit/build-optimizer
"""
def ng_rollup_bundle(name, **kwargs):
"""Rollup with Build Optimizer on esm5 inputs.
This provides a variant of the [legacy rollup_bundle] rule that works better for Angular apps.
Runs [rollup], [terser_minified] and [brotli] to produce a number of output bundles.
es5 : "%{name}.js"
es5 minified : "%{name}.min.js"
es5 minified (compressed) : "%{name}.min.js.br",
es5 minified (debug) : "%{name}.min_debug.js"
es2015 : "%{name}.es2015.js"
es2015 minified : "%{name}.min.es2015.js"
es2015 minified (compressed) : "%{name}.min.js.es2015.br",
es2015 minified (debug) : "%{name}.min_debug.es2015.js"
It registers `@angular-devkit/build-optimizer` as a rollup plugin, to get
better optimization. It also uses ESM5 format inputs, as this is what
build-optimizer is hard-coded to look for and transform.
[legacy rollup_bundle]: https://github.com/bazelbuild/rules_nodejs/blob/0.38.3/internal/rollup/rollup_bundle.bzl
[rollup]: https://rollupjs.org/guide/en/
[terser_minified]: https://bazelbuild.github.io/rules_nodejs/Terser.html
[brotli]: https://brotli.org/
"""
format = kwargs.pop("format", "iife")
build_optimizer = kwargs.pop("build_optimizer", True)
visibility = kwargs.pop("visibility", None)
# Common arguments for all terser_minified targets
common_terser_args = {
# As of terser 4.3.4 license comments are preserved by default. See
# https://github.com/terser/terser/blob/master/CHANGELOG.md. We want to
# maintain the comments off behavior. We pass the --comments flag with
# a regex that always evaluates to false to do this.
"args": ["--comments", "/bogus_string_to_suppress_all_comments^/"],
"config_file": "//dev-infra/benchmark/ng_rollup_bundle:terser_config.json",
"sourcemap": False,
}
# TODO(gregmagolan): reduce this macro to just use the new @bazel/rollup rollup_bundle
# once esm5 inputs are no longer needed. _ng_rollup_bundle is just here for esm5 support
# and once that requirement is removed for Angular 10 then there is nothing that rule is doing
# that the new @bazel/rollup rollup_bundle rule can't do.
_ng_rollup_bundle(
name = name,
build_optimizer = build_optimizer,
format = format,
visibility = visibility,
**kwargs
)
terser_minified(name = name + ".min", src = name, visibility = visibility, **common_terser_args)
native.filegroup(name = name + ".min.js", srcs = [name + ".min"], visibility = visibility)
terser_minified(name = name + ".min_debug", src = name, debug = True, visibility = visibility, **common_terser_args)
native.filegroup(name = name + ".min_debug.js", srcs = [name + ".min_debug"], visibility = visibility)
npm_package_bin(
name = "_%s_brotli" % name,
tool = "//dev-infra/benchmark/brotli-cli",
data = [name + ".min.js"],
outs = [name + ".min.js.br"],
args = [
"--output=$(execpath %s.min.js.br)" % name,
"$(execpath %s.min.js)" % name,
],
visibility = visibility,
)
_ng_rollup_bundle(
name = name + ".es2015",
esm5_sources = False,
format = format,
visibility = visibility,
**kwargs
)
terser_minified(name = name + ".min.es2015", src = name + ".es2015", visibility = visibility, **common_terser_args)
native.filegroup(name = name + ".min.es2015.js", srcs = [name + ".min.es2015"], visibility = visibility)
terser_minified(name = name + ".min_debug.es2015", src = name + ".es2015", debug = True, visibility = visibility, **common_terser_args)
native.filegroup(name = name + ".min_debug.es2015.js", srcs = [name + ".min_debug.es2015"], visibility = visibility)
npm_package_bin(
name = "_%s_es2015_brotli" % name,
tool = "//dev-infra/benchmark/brotli-cli",
data = [name + ".min.es2015.js"],
outs = [name + ".min.es2015.js.br"],
args = [
"--output=$(execpath %s.min.es2015.js.br)" % name,
"$(execpath %s.min.es2015.js)" % name,
],
visibility = visibility,
)
def ls_rollup_bundle(name, **kwargs):
"""A variant of ng_rollup_bundle for the language-service bundle
ls_rollup_bundle uses esm5 inputs, outputs AMD and does not use the build optimizer.
"""
visibility = kwargs.pop("visibility", None)
# Note: the output file is called "umd.js" because of historical reasons.
# The format is actually AMD and not UMD, but we are afraid to rename
# the file because that would likely break the IDE and other integrations that
# have the path hardcoded in them.
ng_rollup_bundle(
name = name + ".umd",
build_optimizer = False,
format = "amd",
visibility = visibility,
**kwargs
)
native.alias(
name = name,
actual = name + ".umd",
visibility = visibility,
)

View File

@ -0,0 +1,212 @@
/**
* @license
* Copyright Google LLC 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
*/
// Rollup configuration
// GENERATED BY Bazel
const buildOptimizer = require(
'npm/node_modules/@angular-devkit/build-optimizer/src/build-optimizer/rollup-plugin.js');
const nodeResolve = require('rollup-plugin-node-resolve');
const sourcemaps = require('rollup-plugin-sourcemaps');
const commonjs = require('rollup-plugin-commonjs');
const path = require('path');
const fs = require('fs');
function log_verbose(...m) {
// This is a template file so we use __filename to output the actual filename
if (!!process.env['VERBOSE_LOGS']) console.error(`[${path.basename(__filename)}]`, ...m);
}
const workspaceName = 'TMPL_workspace_name';
const useBuildOptimzier = TMPL_build_optimizer;
const rootDir = 'TMPL_root_dir';
const bannerFile = TMPL_banner_file;
const stampData = TMPL_stamp_data;
const moduleMappings = TMPL_module_mappings;
const nodeModulesRoot = 'TMPL_node_modules_root';
const ivyEnabled = TMPL_ivy_enabled;
log_verbose(`running with
cwd: ${process.cwd()}
workspaceName: ${workspaceName}
useBuildOptimzier: ${useBuildOptimzier}
rootDir: ${rootDir}
bannerFile: ${bannerFile}
stampData: ${stampData}
moduleMappings: ${JSON.stringify(moduleMappings)}
nodeModulesRoot: ${nodeModulesRoot}
ivyEnabled: ${ivyEnabled}
`);
function fileExists(filePath) {
try {
return fs.statSync(filePath).isFile();
} catch (e) {
return false;
}
}
// This resolver mimics the TypeScript Path Mapping feature, which lets us resolve
// modules based on a mapping of short names to paths.
function resolveBazel(
importee, importer, baseDir = process.cwd(), resolve = require.resolve, root = rootDir) {
log_verbose(`resolving '${importee}' from ${importer}`);
function resolveInRootDir(importee) {
function tryImportee(importee) {
var candidate = path.join(baseDir, root, importee);
log_verbose(`try to resolve '${importee}' at '${candidate}'`);
try {
var result = resolve(candidate);
return result;
} catch (e) {
return undefined;
}
}
// Attempt in the following order {importee}.mjs, {importee}/index.mjs,
// {importee}, {importee}.js, {importee}/index.js. If an .mjs file is
// available it should be resolved as rollup cannot handle the .js files
// generated by ts_library as they are not esm. When rolling up esm5 files
// these are re-rooted so it is not an issue.
// TODO(gregmagolan): clean this up in the future as the .mjs es2015 outputs
// along side the .js es5 outputs from ts_library creates this unusual situation for
// which we can't rely on standard node module resolution to do the right thing.
// In the future ts_library (or equivalent) should only produce a single flavor of
// output and ng_rollup_bundle should also just use the use the vanilla rollup_bundle
// rule without the need for a custom bazel resolver.
return tryImportee(`${importee}.mjs`) || tryImportee(`${importee}/index.mjs`) ||
tryImportee(importee) || tryImportee(`${importee}.js`) ||
tryImportee(`${importee}/index.js`);
}
// Since mappings are always in POSIX paths, when comparing the importee to mappings
// we should normalize the importee.
// Having it normalized is also useful to determine relative paths.
const normalizedImportee = importee.replace(/\\/g, '/');
// If import is fully qualified then resolve it directly
if (fileExists(importee)) {
log_verbose(`resolved fully qualified '${importee}'`);
return importee;
}
var resolved;
if (normalizedImportee.startsWith('./') || normalizedImportee.startsWith('../')) {
// relative import
if (importer) {
let importerRootRelative = path.dirname(importer);
const relative = path.relative(path.join(baseDir, root), importerRootRelative);
if (!relative.startsWith('.')) {
importerRootRelative = relative;
}
resolved = path.join(importerRootRelative, importee);
} else {
throw new Error('cannot resolve relative paths without an importer');
}
if (resolved) resolved = resolveInRootDir(resolved);
}
if (!resolved) {
// possible workspace import or external import if importee matches a module
// mapping
for (const k in moduleMappings) {
if (normalizedImportee == k || normalizedImportee.startsWith(k + '/')) {
// replace the root module name on a mappings match
// note that the module_root attribute is intended to be used for type-checking
// so it uses eg. "index.d.ts". At runtime, we have only index.js, so we strip the
// .d.ts suffix and let node require.resolve do its thing.
var v = moduleMappings[k].replace(/\.d\.ts$/, '');
const mappedImportee = path.join(v, normalizedImportee.slice(k.length + 1));
log_verbose(`module mapped '${importee}' to '${mappedImportee}'`);
resolved = resolveInRootDir(mappedImportee);
if (resolved) break;
}
}
}
if (!resolved) {
// workspace import
const userWorkspacePath = path.relative(workspaceName, importee);
resolved = resolveInRootDir(userWorkspacePath.startsWith('..') ? importee : userWorkspacePath);
}
if (resolved) {
log_verbose(`resolved to ${resolved}`);
} else {
log_verbose(`allowing rollup to resolve '${importee}' with node module resolution`);
}
return resolved;
}
// We make mainFields match what was the default for plugin-node-resolve <4.2.0
// when mainFields was introduced. Resolving to 'browser' or 'es2015' first breaks
// some of the benchmarks such as `//modules/benchmarks/src/expanding_rows:perf_chromium-local`.
// See https://app.circleci.com/jobs/github/angular/angular/507444 &&
// https://app.circleci.com/jobs/github/angular/angular/507442 for affected tests.
const mainFields = ['module', 'main'];
const ngccMainFields = mainFields.map(f => `${f}_ivy_ngcc`);
let plugins = [
{
name: 'resolveBazel',
resolveId: resolveBazel,
},
nodeResolve({
// If Ivy is enabled, we need to make sure that the module resolution prioritizes ngcc
// processed entry-point fields. Ngcc adds special fields to `package.json` files of
// modules that have been processed. Prioritizing these fields matches the Angular CLIs
// behavior for supporting Ivy. We need to support ngcc because `ng_rollup_bundle` rule is
// shared with other repositories that consume Angular from NPM (w/ ngcc).
// https://github.com/angular/angular-cli/blob/1a1ceb609b9a87c4021cce3a6f0fc6d167cd09d2/packages/ngtools/webpack/src/angular_compiler_plugin.ts#L918-L920
mainFields: ivyEnabled ? [...ngccMainFields, ...mainFields] : mainFields,
jail: process.cwd(),
customResolveOptions: {moduleDirectory: nodeModulesRoot}
}),
commonjs({ignoreGlobal: true}),
sourcemaps(),
];
if (useBuildOptimzier) {
plugins = [
buildOptimizer.default({
sideEffectFreeModules: [
'.esm5/packages/core/src',
'.esm5/packages/common/src',
'.esm5/packages/compiler/src',
'.esm5/packages/platform-browser/src',
]
}),
].concat(plugins);
}
let banner = '';
if (bannerFile) {
banner = fs.readFileSync(bannerFile, {encoding: 'utf-8'});
if (stampData) {
const versionTag = fs.readFileSync(stampData, {encoding: 'utf-8'})
.split('\n')
.find(s => s.startsWith('BUILD_SCM_VERSION'));
// Don't assume BUILD_SCM_VERSION exists
if (versionTag) {
const version = versionTag.split(' ')[1].trim();
banner = banner.replace(/0.0.0-PLACEHOLDER/, version);
}
}
}
const config = {
plugins,
external: [TMPL_external],
output: {
globals: {TMPL_globals},
banner,
}
};
module.exports = config;

View File

@ -0,0 +1,12 @@
{
"compress": {
"global_defs": {"ngDevMode": false, "ngI18nClosureMode": false, "ngJitMode": false},
"keep_fnames": "bazel_no_debug",
"passes": 3,
"pure_getters": true,
"reduce_funcs": "bazel_no_debug",
"reduce_vars": "bazel_no_debug",
"sequences": "bazel_no_debug"
},
"mangle": "bazel_no_debug"
}