build(bazel): use fine-grained npm deps (#26111) (#26488)

PR Close #26488
This commit is contained in:
Greg Magolan
2018-10-04 13:14:14 -07:00
committed by Igor Minar
parent b6c9678f21
commit 1f3331f5e6
104 changed files with 6687 additions and 1820 deletions

View File

@ -80,12 +80,20 @@ def _esm5_outputs_aspect(target, ctx):
],
)
replay_compiler = target.typescript.replay_params.compiler.path.split("/")[-1]
if replay_compiler == "tsc_wrapped":
compiler = ctx.executable._tsc_wrapped
elif replay_compiler == "ngc-wrapped":
compiler = ctx.executable._ngc_wrapped
else:
fail("Unknown replay compiler", target.typescript.replay_params.compiler.path)
ctx.actions.run(
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,
executable = compiler,
execution_requirements = {
# TODO(alexeagle): enable worker mode for these compilations
"supports-workers": "0",
@ -122,15 +130,11 @@ esm5_outputs_aspect = aspect(
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_bin"),
default = Label("@build_bazel_rules_typescript//:@bazel/typescript/tsc_wrapped"),
executable = True,
cfg = "host",
),
# Same comment as for tsc_wrapped above.
"_ngc_wrapped": attr.label(
default = Label("//packages/bazel/src/ngc-wrapped"),
executable = True,

View File

@ -14,6 +14,7 @@ load(
"ts_providers_dict_to_struct",
"tsc_wrapped_tsconfig",
)
load("@build_bazel_rules_nodejs//internal/common:node_module_info.bzl", "NodeModuleInfo", "collect_node_modules_aspect")
def compile_strategy(ctx):
"""Detect which strategy should be used to implement ng_module.
@ -379,23 +380,31 @@ def ngc_compile_action(
return None
def _filter_ts_inputs(all_inputs):
# The compiler only needs to see TypeScript sources from the npm dependencies,
# but may need to look at package.json and ngsummary.json files as well.
return [
f
for f in all_inputs
if f.path.endswith(".js") or f.path.endswith(".ts") or f.path.endswith(".json")
]
def _compile_action(ctx, inputs, outputs, messages_out, tsconfig_file, node_opts):
# Give the Angular compiler all the user-listed assets
file_inputs = list(ctx.files.assets)
# The compiler only needs to see TypeScript sources from the npm dependencies,
# but may need to look at package.json and ngsummary.json files as well.
if hasattr(ctx.attr, "node_modules"):
file_inputs += [
f
for f in ctx.files.node_modules
if f.path.endswith(".ts") or f.path.endswith(".json")
]
file_inputs.extend(_filter_ts_inputs(ctx.files.node_modules))
# If the user supplies a tsconfig.json file, the Angular compiler needs to read it
if hasattr(ctx.attr, "tsconfig") and ctx.file.tsconfig:
file_inputs.append(ctx.file.tsconfig)
# Also include files from npm fine grained deps as action_inputs.
# These deps are identified by the NodeModuleInfo provider.
for d in ctx.attr.deps:
if NodeModuleInfo in d:
file_inputs.extend(_filter_ts_inputs(d.files))
# Collect the inputs and summary files from our deps
action_inputs = depset(
file_inputs,
@ -443,6 +452,13 @@ def ng_module_impl(ctx, ts_compile_actions):
providers = ts_compile_actions(
ctx,
is_library = True,
# Filter out the node_modules from deps passed to TypeScript compiler
# since they don't have the required providers.
# They were added to the action inputs for tsc_wrapped already.
# strict_deps checking currently skips node_modules.
# TODO(alexeagle): turn on strict deps checking when we have a real
# provider for JS/DTS inputs to ts_library.
deps = [d for d in ctx.attr.deps if not NodeModuleInfo in d],
compile_action = _prodmode_compile_action,
devmode_compile_action = _devmode_compile_action,
tsc_wrapped_tsconfig = _ngc_tsconfig,
@ -474,6 +490,11 @@ def ng_module_impl(ctx, ts_compile_actions):
def _ng_module_impl(ctx):
return ts_providers_dict_to_struct(ng_module_impl(ctx, compile_ts))
local_deps_aspects = [collect_node_modules_aspect, _collect_summaries_aspect]
# Workaround skydoc bug which assumes DEPS_ASPECTS is a str type
[local_deps_aspects.append(a) for a in DEPS_ASPECTS]
NG_MODULE_ATTRIBUTES = {
"srcs": attr.label_list(allow_files = [".ts"]),
@ -481,7 +502,7 @@ NG_MODULE_ATTRIBUTES = {
# https://github.com/bazelbuild/skydoc/issues/21
"deps": attr.label_list(
doc = "Targets that are imported by this target",
aspects = list(DEPS_ASPECTS) + [_collect_summaries_aspect],
aspects = local_deps_aspects,
),
"assets": attr.label_list(
doc = ".html and .css files needed by the Angular compiler",
@ -514,12 +535,68 @@ NG_MODULE_ATTRIBUTES = {
NG_MODULE_RULE_ATTRS = dict(dict(COMMON_ATTRIBUTES, **NG_MODULE_ATTRIBUTES), **{
"tsconfig": attr.label(allow_files = True, single_file = True),
# @// is special syntax for the "main" repository
# The default assumes the user specified a target "node_modules" in their
# root BUILD file.
"node_modules": attr.label(
default = Label("@//:node_modules"),
doc = """The npm packages which should be available during the compile.
The default value is `@npm//typescript:typescript__typings` is setup
for projects that use bazel managed npm deps that. It is recommended
that you use the workspace name `@npm` for bazel managed deps so the
default node_modules works out of the box. Otherwise, you'll have to
override the node_modules attribute manually. This default is in place
since ng_module will always depend on at least the typescript
default libs which are provided by `@npm//typescript:typescript__typings`.
This attribute is DEPRECATED. As of version 0.18.0 the recommended
approach to npm dependencies is to use fine grained npm dependencies
which are setup with the `yarn_install` or `npm_install` rules.
For example, in targets that used a `//:node_modules` filegroup,
```
ng_module(
name = "my_lib",
...
node_modules = "@npm//node_modules",
)
```
which specifies all files within the `//:node_modules` filegroup
to be inputs to the `my_lib`. Using fine grained npm dependencies,
`my_lib` is defined with only the npm dependencies that are
needed:
```
ng_module(
name = "my_lib",
...
deps = [
"@npm//@types/foo",
"@npm//@types/bar",
"@npm//foo",
"@npm//bar",
...
],
)
```
In this case, only the listed npm packages and their
transitive deps are includes as inputs to the `my_lib` target
which reduces the time required to setup the runfiles for this
target (see https://github.com/bazelbuild/bazel/issues/5153).
The default typescript libs are also available via the node_modules
default in this case.
The @npm external repository and the fine grained npm package
targets are setup using the `yarn_install` or `npm_install` rule
in your WORKSPACE file:
yarn_install(
name = "npm",
package_json = "//:package.json",
yarn_lock = "//:yarn.lock",
)
""",
default = Label("@npm//typescript:typescript__typings"),
),
"entry_point": attr.string(),

View File

@ -14,6 +14,7 @@ exports_files(["rollup.config.js"])
ts_library(
name = "lib",
srcs = glob(["*.ts"]),
compiler = "//:@bazel/typescript/tsc_wrapped",
node_modules = "@angular_packager_deps//:node_modules",
tsconfig = ":tsconfig.json",
)

View File

@ -17,7 +17,7 @@ load("@build_bazel_rules_nodejs//:internal/collect_es6_sources.bzl", "collect_es
load(
"@build_bazel_rules_nodejs//:internal/rollup/rollup_bundle.bzl",
"ROLLUP_ATTRS",
"rollup_module_mappings_aspect",
"ROLLUP_DEPS_ASPECTS",
"run_uglify",
"write_rollup_config",
)
@ -28,6 +28,7 @@ load(
"create_package",
)
load("@build_bazel_rules_nodejs//:internal/node.bzl", "sources_aspect")
load("@build_bazel_rules_nodejs//internal/common:node_module_info.bzl", "NodeModuleInfo")
load("//packages/bazel/src:esm5.bzl", "esm5_outputs_aspect", "esm5_root_dir", "flatten_esm5")
# Convert from some-dash-case to someCamelCase
@ -160,6 +161,13 @@ def _filter_out_generated_files(files):
def _esm2015_root_dir(ctx):
return ctx.label.name + ".es6"
def _filter_js_inputs(all_inputs):
return [
f
for f in all_inputs
if f.path.endswith(".js") or f.path.endswith(".json")
]
# ng_package produces package that is npm-ready.
def _ng_package_impl(ctx):
npm_package_directory = ctx.actions.declare_directory("%s.ng_pkg" % ctx.label.name)
@ -235,11 +243,19 @@ def _ng_package_impl(ctx):
umd_output = ctx.outputs.umd
min_output = ctx.outputs.umd_min
node_modules_files = _filter_js_inputs(ctx.files.node_modules)
# Also include files from npm fine grained deps as inputs.
# These deps are identified by the NodeModuleInfo provider.
for d in ctx.attr.deps:
if NodeModuleInfo in d:
node_modules_files += _filter_js_inputs(d.files)
esm2015_config = write_rollup_config(ctx, [], "/".join([ctx.bin_dir.path, ctx.label.package, _esm2015_root_dir(ctx)]), filename = "_%s.rollup_esm2015.conf.js")
esm5_config = write_rollup_config(ctx, [], "/".join([ctx.bin_dir.path, ctx.label.package, esm5_root_dir(ctx)]), filename = "_%s.rollup_esm5.conf.js")
fesm2015.append(_rollup(ctx, "fesm2015", esm2015_config, es2015_entry_point, esm_2015_files + ctx.files.node_modules, fesm2015_output))
fesm5.append(_rollup(ctx, "fesm5", esm5_config, es5_entry_point, esm5_sources + ctx.files.node_modules, fesm5_output))
fesm2015.append(_rollup(ctx, "fesm2015", esm2015_config, es2015_entry_point, esm_2015_files + node_modules_files, fesm2015_output))
fesm5.append(_rollup(ctx, "fesm5", esm5_config, es5_entry_point, esm5_sources + node_modules_files, fesm5_output))
bundles.append(
_rollup(
@ -247,7 +263,7 @@ def _ng_package_impl(ctx):
"umd",
esm5_config,
es5_entry_point,
esm5_sources + ctx.files.node_modules,
esm5_sources + node_modules_files,
umd_output,
format = "umd",
package_name = package_name,
@ -332,7 +348,8 @@ def _ng_package_impl(ctx):
devfiles = depset()
if ctx.attr.include_devmode_srcs:
for d in ctx.attr.deps:
devfiles = depset(transitive = [devfiles, d.files, d.node_sources])
if not NodeModuleInfo in d:
devfiles = depset(transitive = [devfiles, d.files, d.node_sources])
# Re-use the create_package function from the nodejs npm_package rule.
package_dir = create_package(
@ -344,13 +361,14 @@ def _ng_package_impl(ctx):
files = depset([package_dir]),
)]
DEPS_ASPECTS = [esm5_outputs_aspect, sources_aspect]
# Workaround skydoc bug which assumes ROLLUP_DEPS_ASPECTS is a str type
[DEPS_ASPECTS.append(a) for a in ROLLUP_DEPS_ASPECTS]
NG_PACKAGE_ATTRS = dict(NPM_PACKAGE_ATTRS, **dict(ROLLUP_ATTRS, **{
"srcs": attr.label_list(allow_files = True),
"deps": attr.label_list(aspects = [
rollup_module_mappings_aspect,
esm5_outputs_aspect,
sources_aspect,
]),
"deps": attr.label_list(aspects = DEPS_ASPECTS),
"data": attr.label_list(
doc = "Additional, non-Angular files to be added to the package, e.g. global CSS assets.",
allow_files = True,

View File

@ -17,8 +17,8 @@
load(
"@build_bazel_rules_nodejs//internal/rollup:rollup_bundle.bzl",
"ROLLUP_ATTRS",
"ROLLUP_DEPS_ASPECTS",
"ROLLUP_OUTPUTS",
"rollup_module_mappings_aspect",
"run_rollup",
"run_sourcemapexplorer",
"run_uglify",
@ -141,16 +141,18 @@ def _ng_rollup_bundle(ctx):
return DefaultInfo(files = depset([ctx.outputs.build_es5_min, sourcemap]))
DEPS_ASPECTS = [esm5_outputs_aspect]
# Workaround skydoc bug which assumes ROLLUP_DEPS_ASPECTS is a str type
[DEPS_ASPECTS.append(a) for a in ROLLUP_DEPS_ASPECTS]
ng_rollup_bundle = rule(
implementation = _ng_rollup_bundle,
attrs = dict(ROLLUP_ATTRS, **{
"deps": attr.label_list(
doc = """Other targets that provide JavaScript files.
Typically this will be `ts_library` or `ng_module` targets.""",
aspects = [
rollup_module_mappings_aspect,
esm5_outputs_aspect,
],
aspects = DEPS_ASPECTS,
),
"_rollup": attr.label(
executable = True,

View File

@ -13,7 +13,9 @@ ts_library(
"extract_i18n.ts",
"index.ts",
],
compiler = "//:@bazel/typescript/tsc_wrapped",
module_name = "@angular/bazel",
node_modules = "@ngdeps//typescript:typescript__typings",
tsconfig = ":tsconfig.json",
visibility = ["//packages/bazel/test/ngc-wrapped:__subpackages__"],
deps = [
@ -22,7 +24,10 @@ ts_library(
# Users will get this dependency from node_modules.
"//packages/compiler-cli",
# END-INTERNAL
"@build_bazel_rules_typescript//internal:tsc_wrapped",
"@ngdeps//@bazel/typescript",
"@ngdeps//@types/node",
"@ngdeps//tsickle",
"@ngdeps//typescript",
],
)
@ -31,9 +36,9 @@ nodejs_binary(
data = [
":ngc_lib",
"@build_bazel_rules_typescript//third_party/github.com/bazelbuild/bazel/src/main/protobuf:worker_protocol.proto",
"@ngdeps//tslib",
],
entry_point = "angular/packages/bazel/src/ngc-wrapped/index.js",
node_modules = "@//:node_modules",
visibility = ["//visibility:public"],
)
@ -43,6 +48,5 @@ nodejs_binary(
":ngc_lib",
],
entry_point = "angular/packages/bazel/src/ngc-wrapped/index.js/extract_i18n.js",
node_modules = "@//:node_modules",
visibility = ["//visibility:public"],
)

View File

@ -1,5 +1,6 @@
{
"compilerOptions": {
"lib": ["es5", "es2015.collection", "es2015.core"]
"lib": ["es5", "es2015.collection", "es2015.core"],
"types": ["node"]
}
}

View File

@ -202,7 +202,6 @@ def protractor_web_test(
name = protractor_bin_name,
entry_point = "protractor/bin/protractor",
data = srcs + deps + data,
node_modules = "@//:node_modules",
testonly = 1,
visibility = ["//visibility:private"],
)
@ -303,7 +302,6 @@ def protractor_web_test_suite(
name = protractor_bin_name,
entry_point = "protractor/bin/protractor",
data = srcs + deps + data,
node_modules = "@//:node_modules",
testonly = 1,
visibility = ["//visibility:private"],
)

View File

@ -11,6 +11,9 @@ load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")
ts_library(
name = "utils",
srcs = ["index.ts"],
compiler = "//:@bazel/typescript/tsc_wrapped",
module_name = "@angular/bazel/protractor-utils",
node_modules = "@ngdeps//typescript:typescript__typings",
tsconfig = ":tsconfig.json",
deps = ["@ngdeps//@types/node"],
)

View File

@ -1,6 +1,7 @@
{
"compilerOptions": {
"noImplicitAny": true,
"lib": ["es2015"]
"lib": ["es2015"],
"types": ["node"]
}
}