The legacy nodejs rules rollup_bundle is now deprecated and will be removed in the nodejs rules 1.0 release due in mid-November. This PR brings in the rules_nodejs internal API deps that ng_rollup_bundle, ng_package and ls_rollup_bundle depend on into this repo to break the dependency. In the future these rules should switch to use the new rollup_bundle via a macro as done in https://github.com/angular/angular/pull/33329 but this is not possible right now due to the complication of having esm5 re-rooted ts_library dependencies. The es6 sources now have .mjs extensions so they no longer need to be re-rooted to `{package}.es6`. This eliminates the need for the collect_es6_sources() function. Note: repo has been updated to the newest working version of rollup which is 1.25.2. There is some regression in 1.26.0 which causes the following bundling failure: ``` ERROR: /Users/greg/google/angular/packages/localize/BUILD.bazel:20:1: Optimizing JavaScript packages/localize/localize.umd.min.js [terser] failed (Exit 1) terser.sh failed: error executing command bazel-out/host/bin/external/npm/terser/bin/terser.sh bazel-out/darwin-fastbuild/bin/packages/localize/localize.umd.js --output bazel-out/darwin-fastbuild/bin/packages/localize/localize.umd.min.js ... (remaining 5 argument(s) skipped) Use --sandbox_debug to see verbose messages from the sandbox Parse error at packages/localize/localize.umd.js:491,4 export * from './src/constants'; ^ ERROR: Export statement may only appear at top level at js_error (/private/var/tmp/_bazel_greg/5e8f8a9cd1c6fbc1afd11e37ee1fe2e5/sandbox/darwin-sandbox/79/execroot/angular/bazel-out/host/bin/external/npm/terser/bin/terser.sh.runfiles/npm/node_modules/terser/lib/parse.js:357:11) at Dn.visit (/private/var/tmp/_bazel_greg/5e8f8a9cd1c6fbc1afd11e37ee1fe2e5/sandbox/darwin-sandbox/79/execroot/angular/bazel-out/host/bin/external/npm/terser/bin/terser.sh.runfiles/npm/node_modules/terser/lib/scope.js:347:13) at Dn._visit (/private/var/tmp/_bazel_greg/5e8f8a9cd1c6fbc1afd11e37ee1fe2e5/sandbox/darwin-sandbox/79/execroot/angular/bazel-out/host/bin/external/npm/terser/bin/terser.sh.runfiles/npm/node_modules/terser/lib/ast.js:1207:24) at AST_Export._walk (/private/var/tmp/_bazel_greg/5e8f8a9cd1c6fbc1afd11e37ee1fe2e5/sandbox/darwin-sandbox/79/execroot/angular/bazel-out/host/bin/external/npm/terser/bin/terser.sh.runfiles/npm/node_modules/terser/lib/ast.js:718:17) at walk_body (/private/var/tmp/_bazel_greg/5e8f8a9cd1c6fbc1afd11e37ee1fe2e5/sandbox/darwin-sandbox/79/execroot/angular/bazel-out/host/bin/external/npm/terser/bin/terser.sh.runfiles/npm/node_modules/terser/lib/ast.js:168:17) at AST_Function.call (/private/var/tmp/_bazel_greg/5e8f8a9cd1c6fbc1afd11e37ee1fe2e5/sandbox/darwin-sandbox/79/execroot/angular/bazel-out/host/bin/external/npm/terser/bin/terser.sh.runfiles/npm/node_modules/terser/lib/ast.js:430:13) at descend (/private/var/tmp/_bazel_greg/5e8f8a9cd1c6fbc1afd11e37ee1fe2e5/sandbox/darwin-sandbox/79/execroot/angular/bazel-out/host/bin/external/npm/terser/bin/terser.sh.runfiles/npm/node_modules/terser/lib/ast.js:1208:21) at Dn.visit (/private/var/tmp/_bazel_greg/5e8f8a9cd1c6fbc1afd11e37ee1fe2e5/sandbox/darwin-sandbox/79/execroot/angular/bazel-out/host/bin/external/npm/terser/bin/terser.sh.runfiles/npm/node_modules/terser/lib/scope.js:256:13) at Dn._visit (/private/var/tmp/_bazel_greg/5e8f8a9cd1c6fbc1afd11e37ee1fe2e5/sandbox/darwin-sandbox/79/execroot/angular/bazel-out/host/bin/external/npm/terser/bin/terser.sh.runfiles/npm/node_modules/terser/lib/ast.js:1207:24) at AST_Function._walk (/private/var/tmp/_bazel_greg/5e8f8a9cd1c6fbc1afd11e37ee1fe2e5/sandbox/darwin-sandbox/79/execroot/angular/bazel-out/host/bin/external/npm/terser/bin/terser.sh.runfiles/npm/node_modules/terser/lib/ast.js:424:24) [Function] Target //packages/localize:npm_package failed to build Use --verbose_failures to see the command lines of failed build steps. ERROR: /Users/greg/google/angular/packages/localize/BUILD.bazel:20:1 Optimizing JavaScript packages/localize/localize.umd.min.js [terser] failed (Exit 1) terser.sh failed: error executing command bazel-out/host/bin/external/npm/terser/bin/terser.sh bazel-out/darwin-fastbuild/bin/packages/localize/localize.umd.js --output bazel-out/darwin-fastbuild/bin/packages/localize/localize.umd.min.js ... (remaining 5 argument(s) skipped) ``` Will leave that for another day. Terser also updated to 4.3.3. Updating to 4.3.4 (https://github.com/terser/terser/blob/master/CHANGELOG.md) turns comments preservation on by default which increases the size of the //packages/core/test/bundling/todo:bundle.min.js in CI. After bazelbuild/rules_nodejs#1317 terser can be updated to the latest as passing —comments /a^/ to args can turn off all comments for the //packages/core/test/bundling/todo:bundle.min.js size test. PR Close #33201 PR Close #33607
This commit is contained in:
@ -5,14 +5,17 @@ load("@npm_bazel_jasmine//:index.bzl", _jasmine_node_test = "jasmine_node_test")
|
||||
load("@npm_bazel_karma//:index.bzl", _karma_web_test = "karma_web_test", _karma_web_test_suite = "karma_web_test_suite", _ts_web_test = "ts_web_test", _ts_web_test_suite = "ts_web_test_suite")
|
||||
load("@npm_bazel_typescript//:index.bzl", _ts_library = "ts_library")
|
||||
load("//packages/bazel:index.bzl", _ng_module = "ng_module", _ng_package = "ng_package")
|
||||
load("//packages/bazel/src:ng_rollup_bundle.bzl", _ng_rollup_bundle = "ng_rollup_bundle")
|
||||
load("//tools/ng_rollup_bundle:ng_rollup_bundle.bzl", _ng_rollup_bundle = "ng_rollup_bundle")
|
||||
load("//tools:ng_benchmark.bzl", _ng_benchmark = "ng_benchmark")
|
||||
|
||||
_DEFAULT_TSCONFIG_TEST = "//packages:tsconfig-test"
|
||||
_INTERNAL_NG_MODULE_API_EXTRACTOR = "//packages/bazel/src/api-extractor:api_extractor"
|
||||
_INTERNAL_NG_MODULE_COMPILER = "//packages/bazel/src/ngc-wrapped"
|
||||
_INTERNAL_NG_MODULE_XI18N = "//packages/bazel/src/ngc-wrapped:xi18n"
|
||||
_INTERNAL_NG_PACKAGER_PACKAGER = "//packages/bazel/src/ng_package:packager"
|
||||
_INTERNAL_NG_PACKAGE_PACKAGER = "//packages/bazel/src/ng_package:packager"
|
||||
_INTERNAL_NG_PACKAGE_DEFALUT_TERSER_CONFIG_FILE = "//packages/bazel/src/ng_package:terser_config.default.json"
|
||||
_INTERNAL_NG_PACKAGE_DEFAULT_ROLLUP_CONFIG_TMPL = "//packages/bazel/src/ng_package:rollup.config.js"
|
||||
_INTERNAL_NG_PACKAGE_DEFAULT_ROLLUP = "//packages/bazel/src/ng_package:rollup_for_ng_package"
|
||||
|
||||
# Packages which are versioned together on npm
|
||||
ANGULAR_SCOPED_PACKAGES = ["@angular/%s" % p for p in [
|
||||
@ -145,7 +148,10 @@ def ng_package(name, readme_md = None, license_banner = None, deps = [], **kwarg
|
||||
readme_md = readme_md,
|
||||
license_banner = license_banner,
|
||||
replacements = PKG_GROUP_REPLACEMENTS,
|
||||
ng_packager = _INTERNAL_NG_PACKAGER_PACKAGER,
|
||||
ng_packager = _INTERNAL_NG_PACKAGE_PACKAGER,
|
||||
terser_config_file = _INTERNAL_NG_PACKAGE_DEFALUT_TERSER_CONFIG_FILE,
|
||||
rollup_config_tmpl = _INTERNAL_NG_PACKAGE_DEFAULT_ROLLUP_CONFIG_TMPL,
|
||||
rollup = _INTERNAL_NG_PACKAGE_DEFAULT_ROLLUP,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
18
tools/ng_rollup_bundle/BUILD.bazel
Normal file
18
tools/ng_rollup_bundle/BUILD.bazel
Normal file
@ -0,0 +1,18 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
|
||||
|
||||
exports_files(["rollup.config.js"])
|
||||
|
||||
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",
|
||||
install_source_map_support = False,
|
||||
)
|
457
tools/ng_rollup_bundle/ng_rollup_bundle.bzl
Normal file
457
tools/ng_rollup_bundle/ng_rollup_bundle.bzl
Normal file
@ -0,0 +1,457 @@
|
||||
# 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
|
||||
|
||||
"""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("//tools/ng_rollup_bundle:rollup_with_build_optimizer"),
|
||||
),
|
||||
"_rollup_config_tmpl": attr.label(
|
||||
default = Label("//tools/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
|
||||
|
||||
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,
|
||||
},
|
||||
)
|
||||
|
||||
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")
|
||||
|
||||
if ctx.attr.globals:
|
||||
args.add("--external")
|
||||
args.add_joined(ctx.attr.globals.keys(), join_with = ",")
|
||||
args.add("--globals")
|
||||
args.add_joined(["%s:%s" % g for g in ctx.attr.globals.items()], join_with = ",")
|
||||
|
||||
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)
|
||||
|
||||
# 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 + "", sourcemap = False, visibility = visibility)
|
||||
native.filegroup(name = name + ".min.js", srcs = [name + ".min"], visibility = visibility)
|
||||
terser_minified(name = name + ".min_debug", src = name + "", sourcemap = False, debug = True, visibility = visibility)
|
||||
native.filegroup(name = name + ".min_debug.js", srcs = [name + ".min_debug"], visibility = visibility)
|
||||
npm_package_bin(
|
||||
name = "_%s_brotli" % name,
|
||||
tool = "//tools/brotli-cli",
|
||||
data = [name + ".min.js"],
|
||||
outs = [name + ".min.js.br"],
|
||||
args = [
|
||||
"--output=$(location %s.min.js.br)" % name,
|
||||
"$(location %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 + "", sourcemap = False, visibility = visibility)
|
||||
native.filegroup(name = name + ".min.es2015.js", srcs = [name + ".min.es2015"], visibility = visibility)
|
||||
terser_minified(name = name + ".min_debug.es2015", src = name + "", sourcemap = False, debug = True, visibility = visibility)
|
||||
native.filegroup(name = name + ".min_debug.es2015.js", srcs = [name + ".min_debug.es2015"], visibility = visibility)
|
||||
npm_package_bin(
|
||||
name = "_%s_es2015_brotli" % name,
|
||||
tool = "//tools/brotli-cli",
|
||||
data = [name + ".min.es2015.js"],
|
||||
outs = [name + ".min.es2015.js.br"],
|
||||
args = [
|
||||
"--output=$(location %s.min.es2015.js.br)" % name,
|
||||
"$(location %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,
|
||||
)
|
182
tools/ng_rollup_bundle/rollup.config.js
Normal file
182
tools/ng_rollup_bundle/rollup.config.js
Normal file
@ -0,0 +1,182 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
// 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';
|
||||
|
||||
log_verbose(`running with
|
||||
cwd: ${process.cwd()}
|
||||
workspaceName: ${workspaceName}
|
||||
useBuildOptimzier: ${useBuildOptimzier}
|
||||
rootDir: ${rootDir}
|
||||
bannerFile: ${bannerFile}
|
||||
stampData: ${stampData}
|
||||
moduleMappings: ${JSON.stringify(moduleMappings)}
|
||||
nodeModulesRoot: ${nodeModulesRoot}
|
||||
`);
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
let plugins = [
|
||||
{
|
||||
name: 'resolveBazel',
|
||||
resolveId: resolveBazel,
|
||||
},
|
||||
nodeResolve({
|
||||
// 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.
|
||||
mainFields: ['module', 'main'],
|
||||
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,
|
||||
output: {
|
||||
banner,
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = config;
|
Reference in New Issue
Block a user