build: add esm5 build (#22258)

This is a partial cherry-pick of 370ab66c4f
which included this along with a new feature for ivy.

PR Close #22258
This commit is contained in:
Alex Eagle
2018-02-02 15:25:33 -08:00
committed by Victor Berchet
parent 6b457843b9
commit 185a6ab562
15 changed files with 190 additions and 27 deletions

View File

@ -2,16 +2,6 @@ package(default_visibility = ["//visibility:public"])
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",
)
nodejs_binary(
name = "modify_tsconfig",
data = ["modify_tsconfig.js"],

127
packages/bazel/src/esm5.bzl Normal file
View File

@ -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",
),
},
)

View File

@ -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));
}

View File

@ -180,6 +180,7 @@ def ngc_compile_action(ctx, label, inputs, outputs, messages_out, tsconfig_file,
tsconfig = tsconfig_file,
inputs = inputs,
outputs = outputs,
compiler = ctx.executable.compiler,
)
return None