Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
build(ivy): create hello world rollup (#22004)
This is a customization of the rollup_bundle rule from rules_nodejs which adds the build-optimizer as a plugin. Add a functional test with fast round-trip that asserts the minified app still works. Publish the min.js artifact on circleCI so we can track its size. PR Close #22004
- Loading branch information
Showing
16 changed files
with
439 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,19 @@ | ||
# Empty marker file, indicating this directory is a Bazel package. | ||
load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary") | ||
|
||
nodejs_binary( | ||
name = "rollup_with_build_optimizer", | ||
data = ["@angular_devkit//packages/angular_devkit/build_optimizer:lib"], | ||
# Since our rule extends the one in rules_nodejs, we use the same runtime | ||
# dependency @build_bazel_rules_nodejs_rollup_deps. We don't need any | ||
# additional npm dependencies when we run rollup or uglify. | ||
entry_point = "build_bazel_rules_nodejs_rollup_deps/node_modules/rollup/bin/rollup", | ||
node_modules = "@build_bazel_rules_nodejs_rollup_deps//:node_modules", | ||
visibility = ["//visibility:public"], | ||
) | ||
|
||
nodejs_binary( | ||
name = "modify_tsconfig", | ||
data = ["modify_tsconfig.js"], | ||
entry_point = "angular/packages/bazel/src/modify_tsconfig.js", | ||
visibility = ["//visibility:public"], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
# 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 | ||
|
||
"""Provides ES5 syntax with ESModule import/exports. | ||
This exposes another flavor of output JavaScript, which is ES5 syntax | ||
with ES2015 module syntax (import/export). | ||
All Bazel rules should consume the standard dev or prod mode. | ||
However we need to publish this flavor on NPM, so it's necessary to be able | ||
to produce it. | ||
""" | ||
|
||
# The provider downstream rules use to access the outputs | ||
ESM5Info = provider( | ||
doc = "Typescript compilation outputs in ES5 syntax with ES Modules", | ||
fields = { | ||
"transitive_output": """Dict of [rootDir, .js depset] entries. | ||
The value is a depset of the .js output files. | ||
The key is the prefix that should be stripped off the files | ||
when resolving modules, eg. for file | ||
bazel-bin/[external/wkspc/]path/to/package/label.esm5/path/to/package/file.js | ||
the rootdir would be | ||
bazel-bin/[external/wkspc/]path/to/package/label.esm5""", | ||
}, | ||
) | ||
|
||
def _map_closure_path(file): | ||
result = file.short_path[:-len(".closure.js")] | ||
# short_path is meant to be used when accessing runfiles in a binary, where | ||
# the CWD is inside the current repo. Therefore files in external repo have a | ||
# short_path of ../external/wkspc/path/to/package | ||
# We want to strip the first two segments from such paths. | ||
if (result.startswith("../")): | ||
result = "/".join(result.split("/")[2:]) | ||
return result + ".js" | ||
|
||
def _join(array): | ||
return "/".join([p for p in array if p]) | ||
|
||
def _esm5_outputs_aspect(target, ctx): | ||
if not hasattr(target, "typescript"): | ||
return [] | ||
|
||
# We create a new tsconfig.json file that will have our compilation settings | ||
tsconfig = ctx.actions.declare_file("%s_esm5.tsconfig.json" % target.label.name) | ||
|
||
workspace = target.label.workspace_root if target.label.workspace_root else "" | ||
|
||
# re-root the outputs under a ".esm5" directory so the path don't collide | ||
out_dir = ctx.label.name + ".esm5" | ||
if workspace: | ||
out_dir = out_dir + "/" + workspace | ||
|
||
outputs = [ctx.actions.declare_file(_join([out_dir, _map_closure_path(f)])) | ||
for f in target.typescript.replay_params.outputs | ||
if not f.short_path.endswith(".externs.js")] | ||
|
||
ctx.actions.run( | ||
executable = ctx.executable._modify_tsconfig, | ||
inputs = [target.typescript.replay_params.tsconfig], | ||
outputs = [tsconfig], | ||
arguments = [ | ||
target.typescript.replay_params.tsconfig.path, | ||
tsconfig.path, | ||
_join([workspace, target.label.package, ctx.label.name + ".esm5"]), | ||
ctx.bin_dir.path | ||
], | ||
) | ||
|
||
ctx.action( | ||
progress_message = "Compiling TypeScript (ES5 with ES Modules) %s" % target.label, | ||
inputs = target.typescript.replay_params.inputs + [tsconfig], | ||
outputs = outputs, | ||
arguments = [tsconfig.path], | ||
executable = target.typescript.replay_params.compiler, | ||
execution_requirements = { | ||
"supports-workers": "0", | ||
}, | ||
) | ||
|
||
root_dir = _join([ | ||
ctx.bin_dir.path, | ||
workspace, | ||
target.label.package, | ||
ctx.label.name + ".esm5", | ||
]) | ||
|
||
transitive_output={root_dir: depset(outputs)} | ||
for dep in ctx.rule.attr.deps: | ||
if ESM5Info in dep: | ||
transitive_output.update(dep[ESM5Info].transitive_output) | ||
|
||
return [ESM5Info( | ||
transitive_output = transitive_output, | ||
)] | ||
|
||
# Downstream rules can use this aspect to access the ESM5 output flavor. | ||
# Only terminal rules (those which expect never to be used in deps[]) should do | ||
# this. | ||
esm5_outputs_aspect = aspect( | ||
implementation = _esm5_outputs_aspect, | ||
# Recurse to the deps of any target we visit | ||
attr_aspects = ['deps'], | ||
attrs = { | ||
"_modify_tsconfig": attr.label( | ||
default = Label("//packages/bazel/src:modify_tsconfig"), | ||
executable = True, | ||
cfg = "host"), | ||
# We must list tsc_wrapped here to ensure it's built before the action runs | ||
# For some reason, having the compiler output as an input to the action above | ||
# is not sufficient. | ||
"_tsc_wrapped": attr.label( | ||
default = Label("@build_bazel_rules_typescript//internal/tsc_wrapped:tsc_wrapped_bin"), | ||
executable = True, | ||
cfg = "host", | ||
), | ||
# Same comment as for tsc_wrapped above. | ||
"_ngc_wrapped": attr.label( | ||
default = Label("//packages/bazel/src/ngc-wrapped"), | ||
executable = True, | ||
cfg = "host", | ||
), | ||
}, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/** | ||
* @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 | ||
*/ | ||
|
||
/** | ||
* @fileoverview Read a tsconfig.json file intended to produce production mode | ||
* JS output, modify it to produce esm5 output instead, and write the result | ||
* to disk. | ||
*/ | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
|
||
function main(args) { | ||
if (args.length < 3) { | ||
console.error('Usage: $0 input.tsconfig.json output.tsconfig.json newRoot binDir'); | ||
} | ||
[input, output, newRoot, binDir] = args; | ||
|
||
const data = JSON.parse(fs.readFileSync(input, {encoding: 'utf-8'})); | ||
data['compilerOptions']['target'] = 'es5'; | ||
data['bazelOptions']['es5Mode'] = true; | ||
data['bazelOptions']['tsickle'] = false; | ||
data['compilerOptions']['outDir'] = path.join(data['compilerOptions']['outDir'], newRoot); | ||
if (data['angularCompilerOptions']) { | ||
data['angularCompilerOptions']['expectedOut'] = | ||
data['angularCompilerOptions']['expectedOut'].map( | ||
f => f.replace(/\.closure\.js$/, '.js').replace(binDir, path.join(binDir, newRoot))); | ||
} | ||
fs.writeFileSync(output, JSON.stringify(data)); | ||
} | ||
|
||
if (require.main === module) { | ||
process.exitCode = main(process.argv.slice(2)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# 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 | ||
|
||
"""This provides a variant of rollup_bundle 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. | ||
""" | ||
|
||
load("@build_bazel_rules_nodejs//internal/rollup:rollup_bundle.bzl", | ||
"rollup_module_mappings_aspect", | ||
"ROLLUP_ATTRS", | ||
"ROLLUP_OUTPUTS", | ||
"write_rollup_config", | ||
"run_rollup", | ||
"run_uglify") | ||
load("@build_bazel_rules_nodejs//internal:collect_es6_sources.bzl", collect_es2015_sources = "collect_es6_sources") | ||
load(":esm5.bzl", "esm5_outputs_aspect", "ESM5Info") | ||
|
||
PACKAGES=["core", "common"] | ||
PLUGIN_CONFIG="{sideEffectFreeModules: [\n%s]}" % ",\n".join( | ||
[" 'packages/{0}/{0}.esm5'".format(p) for p in PACKAGES]) | ||
BO_ROLLUP="angular_devkit/packages/angular_devkit/build_optimizer/src/build-optimizer/rollup-plugin.js" | ||
BO_PLUGIN="require('%s').default(%s)" % (BO_ROLLUP, PLUGIN_CONFIG) | ||
|
||
def _ng_rollup_bundle(ctx): | ||
# We don't expect anyone to make use of this bundle yet, but it makes this rule | ||
# compatible with rollup_bundle which allows them to be easily swapped back and | ||
# forth. | ||
esm2015_rollup_config = write_rollup_config(ctx, filename = "_%s.rollup_es6.conf.js") | ||
run_rollup(ctx, collect_es2015_sources(ctx), esm2015_rollup_config, ctx.outputs.build_es6) | ||
|
||
esm5_sources = [] | ||
root_dirs = [] | ||
|
||
for dep in ctx.attr.deps: | ||
if ESM5Info in dep: | ||
# TODO(alexeagle): we could make the module resolution in the rollup plugin | ||
# faster if we kept the files grouped with their root dir. This approach just | ||
# passes in both lists and requires multiple lookups (with expensive exception | ||
# handling) to locate the files again. | ||
transitive_output = dep[ESM5Info].transitive_output | ||
root_dirs.extend(transitive_output.keys()) | ||
esm5_sources.extend(transitive_output.values()) | ||
|
||
rollup_config = write_rollup_config(ctx, [BO_PLUGIN], root_dirs) | ||
run_rollup(ctx, depset(transitive = esm5_sources).to_list(), rollup_config, ctx.outputs.build_es5) | ||
|
||
run_uglify(ctx, ctx.outputs.build_es5, ctx.outputs.build_es5_min) | ||
run_uglify(ctx, ctx.outputs.build_es5, ctx.outputs.build_es5_min_debug, debug = True) | ||
|
||
return DefaultInfo(files=depset([ctx.outputs.build_es5_min])) | ||
|
||
ng_rollup_bundle = rule( | ||
implementation = _ng_rollup_bundle, | ||
attrs = dict(ROLLUP_ATTRS, **{ | ||
"deps": attr.label_list(aspects = [ | ||
rollup_module_mappings_aspect, | ||
esm5_outputs_aspect, | ||
]), | ||
"_rollup": attr.label( | ||
executable = True, | ||
cfg="host", | ||
default = Label("@angular//packages/bazel/src:rollup_with_build_optimizer")), | ||
}), | ||
outputs = ROLLUP_OUTPUTS, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package(default_visibility = ["//visibility:public"]) | ||
|
||
load("//tools:defaults.bzl", "ts_library") | ||
load("//packages/bazel/src:ng_rollup_bundle.bzl", "ng_rollup_bundle") | ||
load("@build_bazel_rules_nodejs//:defs.bzl", "jasmine_node_test") | ||
|
||
ts_library( | ||
name = "hello_world", | ||
srcs = ["index.ts"], | ||
deps = [ | ||
"//packages/core", | ||
], | ||
) | ||
|
||
ng_rollup_bundle( | ||
name = "bundle", | ||
# TODO(alexeagle): This is inconsistent. | ||
# We try to teach users to always have their workspace at the start of a | ||
# path, to disambiguate from other workspaces. | ||
# Here, the rule implementation is looking in an execroot where the layout | ||
# has an "external" directory for external dependencies. | ||
# This should probably start with "angular/" and let the rule deal with it. | ||
entry_point = "packages/core/test/bundling/hello_world/index.js", | ||
deps = [ | ||
":hello_world", | ||
"//packages/core", | ||
], | ||
) | ||
|
||
ts_library( | ||
name = "test_lib", | ||
testonly = 1, | ||
srcs = ["domino_typings.d.ts"] + glob(["*_spec.ts"]), | ||
deps = ["//packages:types"], | ||
) | ||
|
||
jasmine_node_test( | ||
name = "test", | ||
data = [ | ||
":bundle", | ||
":bundle.js", | ||
":bundle.min_debug.js", | ||
], | ||
deps = [":test_lib"], | ||
) |
12 changes: 12 additions & 0 deletions
12
packages/core/test/bundling/hello_world/domino_typings.d.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/** | ||
* @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 | ||
*/ | ||
|
||
declare module 'domino' { | ||
function createWindow(html: string, url: string): Window; | ||
const impl: {Element: any}; | ||
} |
Oops, something went wrong.