style: reformat bzl files on patch branch to match master

This commit is contained in:
Alex Eagle 2018-09-18 14:43:05 -07:00
parent af785f9e91
commit e7c72ab556
15 changed files with 1253 additions and 1168 deletions

View File

@ -12,11 +12,13 @@ from source downstream. Alternately, this API is available from the
used in a downstream project. used in a downstream project.
""" """
load("//packages/bazel:index.bzl", load(
"//packages/bazel:index.bzl",
_ng_module = "ng_module", _ng_module = "ng_module",
_ng_package = "ng_package", _ng_package = "ng_package",
_protractor_web_test = "protractor_web_test", _protractor_web_test = "protractor_web_test",
_protractor_web_test_suite = "protractor_web_test_suite") _protractor_web_test_suite = "protractor_web_test_suite",
)
load("//tools:ng_setup_workspace.bzl", _ng_setup_workspace = "ng_setup_workspace") load("//tools:ng_setup_workspace.bzl", _ng_setup_workspace = "ng_setup_workspace")
ng_module = _ng_module ng_module = _ng_module

View File

@ -10,9 +10,11 @@ Users should not load files under "/src"
load("//packages/bazel/src:ng_module.bzl", _ng_module = "ng_module") load("//packages/bazel/src:ng_module.bzl", _ng_module = "ng_module")
load("//packages/bazel/src:ng_setup_workspace.bzl", _ng_setup_workspace = "ng_setup_workspace") load("//packages/bazel/src:ng_setup_workspace.bzl", _ng_setup_workspace = "ng_setup_workspace")
load("//packages/bazel/src/ng_package:ng_package.bzl", _ng_package = "ng_package") load("//packages/bazel/src/ng_package:ng_package.bzl", _ng_package = "ng_package")
load("//packages/bazel/src/protractor:protractor_web_test.bzl", load(
_protractor_web_test = "protractor_web_test", "//packages/bazel/src/protractor:protractor_web_test.bzl",
_protractor_web_test_suite = "protractor_web_test_suite") _protractor_web_test = "protractor_web_test",
_protractor_web_test_suite = "protractor_web_test_suite",
)
ng_module = _ng_module ng_module = _ng_module
ng_package = _ng_package ng_package = _ng_package

View File

@ -28,82 +28,85 @@ ESM5Info = provider(
) )
def _map_closure_path(file): def _map_closure_path(file):
result = file.short_path[:-len(".closure.js")] 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 is meant to be used when accessing runfiles in a binary, where
# short_path of ../external/wkspc/path/to/package # the CWD is inside the current repo. Therefore files in external repo have a
# We want to strip the first two segments from such paths. # short_path of ../external/wkspc/path/to/package
if (result.startswith("../")): # We want to strip the first two segments from such paths.
result = "/".join(result.split("/")[2:]) if (result.startswith("../")):
return result + ".js" result = "/".join(result.split("/")[2:])
return result + ".js"
def _join(array): def _join(array):
return "/".join([p for p in array if p]) return "/".join([p for p in array if p])
def _esm5_outputs_aspect(target, ctx): def _esm5_outputs_aspect(target, ctx):
if not hasattr(target, "typescript"): if not hasattr(target, "typescript"):
return [] return []
# Workaround for https://github.com/bazelbuild/rules_typescript/issues/211 # Workaround for https://github.com/bazelbuild/rules_typescript/issues/211
# TODO(gmagolan): generate esm5 output from ts_proto_library and have that # TODO(gmagolan): generate esm5 output from ts_proto_library and have that
# output work with esm5_outputs_aspect # output work with esm5_outputs_aspect
if not hasattr(target.typescript, "replay_params"): if not hasattr(target.typescript, "replay_params"):
print("WARNING: no esm5 output from target %s//%s:%s available" % (target.label.workspace_root, target.label.package, target.label.name)) print("WARNING: no esm5 output from target %s//%s:%s available" % (target.label.workspace_root, target.label.package, target.label.name))
return [] return []
# We create a new tsconfig.json file that will have our compilation settings # 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) tsconfig = ctx.actions.declare_file("%s_esm5.tsconfig.json" % target.label.name)
workspace = target.label.workspace_root if target.label.workspace_root else "" 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 # re-root the outputs under a ".esm5" directory so the path don't collide
out_dir = ctx.label.name + ".esm5" out_dir = ctx.label.name + ".esm5"
if workspace: if workspace:
out_dir = out_dir + "/" + workspace out_dir = out_dir + "/" + workspace
outputs = [ctx.actions.declare_file(_join([out_dir, _map_closure_path(f)])) outputs = [
for f in target.typescript.replay_params.outputs ctx.actions.declare_file(_join([out_dir, _map_closure_path(f)]))
if not f.short_path.endswith(".externs.js")] for f in target.typescript.replay_params.outputs
if not f.short_path.endswith(".externs.js")
]
ctx.actions.run( ctx.actions.run(
executable = ctx.executable._modify_tsconfig, executable = ctx.executable._modify_tsconfig,
inputs = [target.typescript.replay_params.tsconfig], inputs = [target.typescript.replay_params.tsconfig],
outputs = [tsconfig], outputs = [tsconfig],
arguments = [ arguments = [
target.typescript.replay_params.tsconfig.path, target.typescript.replay_params.tsconfig.path,
tsconfig.path, tsconfig.path,
_join([workspace, target.label.package, ctx.label.name + ".esm5"]), _join([workspace, target.label.package, ctx.label.name + ".esm5"]),
ctx.bin_dir.path ctx.bin_dir.path,
], ],
) )
ctx.actions.run( ctx.actions.run(
progress_message = "Compiling TypeScript (ES5 with ES Modules) %s" % target.label, progress_message = "Compiling TypeScript (ES5 with ES Modules) %s" % target.label,
inputs = target.typescript.replay_params.inputs + [tsconfig], inputs = target.typescript.replay_params.inputs + [tsconfig],
outputs = outputs, outputs = outputs,
arguments = [tsconfig.path], arguments = [tsconfig.path],
executable = target.typescript.replay_params.compiler, executable = target.typescript.replay_params.compiler,
execution_requirements = { execution_requirements = {
# TODO(alexeagle): enable worker mode for these compilations # TODO(alexeagle): enable worker mode for these compilations
"supports-workers": "0", "supports-workers": "0",
}, },
) )
root_dir = _join([ root_dir = _join([
ctx.bin_dir.path, ctx.bin_dir.path,
workspace, workspace,
target.label.package, target.label.package,
ctx.label.name + ".esm5", ctx.label.name + ".esm5",
]) ])
transitive_output={root_dir: depset(outputs)} transitive_output = {root_dir: depset(outputs)}
for dep in ctx.rule.attr.deps: for dep in ctx.rule.attr.deps:
if ESM5Info in dep: if ESM5Info in dep:
transitive_output.update(dep[ESM5Info].transitive_output) transitive_output.update(dep[ESM5Info].transitive_output)
return [ESM5Info( return [ESM5Info(
transitive_output = transitive_output, transitive_output = transitive_output,
)] )]
# Downstream rules can use this aspect to access the ESM5 output flavor. # 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 # Only terminal rules (those which expect never to be used in deps[]) should do
@ -111,12 +114,13 @@ def _esm5_outputs_aspect(target, ctx):
esm5_outputs_aspect = aspect( esm5_outputs_aspect = aspect(
implementation = _esm5_outputs_aspect, implementation = _esm5_outputs_aspect,
# Recurse to the deps of any target we visit # Recurse to the deps of any target we visit
attr_aspects = ['deps'], attr_aspects = ["deps"],
attrs = { attrs = {
"_modify_tsconfig": attr.label( "_modify_tsconfig": attr.label(
default = Label("//packages/bazel/src:modify_tsconfig"), default = Label("//packages/bazel/src:modify_tsconfig"),
executable = True, executable = True,
cfg = "host"), cfg = "host",
),
# We must list tsc_wrapped here to ensure it's built before the action runs # 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 # For some reason, having the compiler output as an input to the action above
# is not sufficient. # is not sufficient.
@ -135,43 +139,44 @@ esm5_outputs_aspect = aspect(
) )
def esm5_root_dir(ctx): def esm5_root_dir(ctx):
return ctx.label.name + ".esm5" return ctx.label.name + ".esm5"
def flatten_esm5(ctx): def flatten_esm5(ctx):
"""Merge together the .esm5 folders from the dependencies. """Merge together the .esm5 folders from the dependencies.
Two different dependencies A and B may have outputs like Two different dependencies A and B may have outputs like
`bazel-bin/path/to/A.esm5/path/to/lib.js` `bazel-bin/path/to/A.esm5/path/to/lib.js`
`bazel-bin/path/to/B.esm5/path/to/main.js` `bazel-bin/path/to/B.esm5/path/to/main.js`
In order to run rollup on this app, in case main.js contains `import from './lib'` In order to run rollup on this app, in case main.js contains `import from './lib'`
they need to be together in the same root directory, so if we depend on both A and B they need to be together in the same root directory, so if we depend on both A and B
we need the outputs to be we need the outputs to be
`bazel-bin/path/to/my_rule.esm5/path/to/lib.js` `bazel-bin/path/to/my_rule.esm5/path/to/lib.js`
`bazel-bin/path/to/my_rule.esm5/path/to/main.js` `bazel-bin/path/to/my_rule.esm5/path/to/main.js`
Args: Args:
ctx: the skylark rule execution context ctx: the skylark rule execution context
Returns: Returns:
list of flattened files list of flattened files
""" """
esm5_sources = [] esm5_sources = []
result = [] result = []
for dep in ctx.attr.deps: for dep in ctx.attr.deps:
if ESM5Info in dep: if ESM5Info in dep:
transitive_output = dep[ESM5Info].transitive_output transitive_output = dep[ESM5Info].transitive_output
esm5_sources.extend(transitive_output.values()) esm5_sources.extend(transitive_output.values())
for f in depset(transitive = esm5_sources).to_list(): for f in depset(transitive = esm5_sources).to_list():
path = f.short_path[f.short_path.find(".esm5") + len(".esm5"):] path = f.short_path[f.short_path.find(".esm5") + len(".esm5"):]
if (path.startswith("../")): if (path.startswith("../")):
path = "external/" + path[3:] path = "external/" + path[3:]
rerooted_file = ctx.actions.declare_file("/".join([esm5_root_dir(ctx), path])) rerooted_file = ctx.actions.declare_file("/".join([esm5_root_dir(ctx), path]))
result.append(rerooted_file) result.append(rerooted_file)
# print("copy", f.short_path, "to", rerooted_file.short_path)
ctx.actions.expand_template( # print("copy", f.short_path, "to", rerooted_file.short_path)
output = rerooted_file, ctx.actions.expand_template(
template = f, output = rerooted_file,
substitutions = {}, template = f,
) substitutions = {},
return result )
return result

View File

@ -5,270 +5,272 @@
"""Implementation of the ng_module rule. """Implementation of the ng_module rule.
""" """
load(":rules_typescript.bzl", load(
"tsc_wrapped_tsconfig", ":rules_typescript.bzl",
"COMMON_ATTRIBUTES", "COMMON_ATTRIBUTES",
"COMMON_OUTPUTS", "COMMON_OUTPUTS",
"compile_ts",
"DEPS_ASPECTS", "DEPS_ASPECTS",
"compile_ts",
"ts_providers_dict_to_struct", "ts_providers_dict_to_struct",
"tsc_wrapped_tsconfig",
) )
def _compile_strategy(ctx): def _compile_strategy(ctx):
"""Detect which strategy should be used to implement ng_module. """Detect which strategy should be used to implement ng_module.
Depending on the value of the 'compile' define flag or the '_global_mode' attribute, ng_module Depending on the value of the 'compile' define flag or the '_global_mode' attribute, ng_module
can be implemented in various ways. This function reads the configuration passed by the user and can be implemented in various ways. This function reads the configuration passed by the user and
determines which mode is active. determines which mode is active.
Args: Args:
ctx: skylark rule execution context ctx: skylark rule execution context
Returns: Returns:
one of 'legacy', 'local', 'jit', or 'global' depending on the configuration in ctx one of 'legacy', 'local', 'jit', or 'global' depending on the configuration in ctx
""" """
strategy = 'legacy' strategy = "legacy"
if 'compile' in ctx.var: if "compile" in ctx.var:
strategy = ctx.var['compile'] strategy = ctx.var["compile"]
if strategy not in ['legacy', 'local', 'jit']: if strategy not in ["legacy", "local", "jit"]:
fail("Unknown --define=compile value '%s'" % strategy) fail("Unknown --define=compile value '%s'" % strategy)
if strategy == 'legacy' and hasattr(ctx.attr, '_global_mode') and ctx.attr._global_mode: if strategy == "legacy" and hasattr(ctx.attr, "_global_mode") and ctx.attr._global_mode:
strategy = 'global' strategy = "global"
return strategy return strategy
def _compiler_name(ctx): def _compiler_name(ctx):
"""Selects a user-visible name depending on the current compilation strategy. """Selects a user-visible name depending on the current compilation strategy.
Args: Args:
ctx: skylark rule execution context ctx: skylark rule execution context
Returns: Returns:
the name of the current compiler to be displayed in build output the name of the current compiler to be displayed in build output
""" """
strategy = _compile_strategy(ctx) strategy = _compile_strategy(ctx)
if strategy == 'legacy': if strategy == "legacy":
return 'ngc' return "ngc"
elif strategy == 'global': elif strategy == "global":
return 'ngc.ivy' return "ngc.ivy"
elif strategy == 'local': elif strategy == "local":
return 'ngtsc' return "ngtsc"
elif strategy == 'jit': elif strategy == "jit":
return 'tsc' return "tsc"
else: else:
fail('unreachable') fail("unreachable")
def _enable_ivy_value(ctx): def _enable_ivy_value(ctx):
"""Determines the value of the enableIvy option in the generated tsconfig. """Determines the value of the enableIvy option in the generated tsconfig.
Args: Args:
ctx: skylark rule execution context ctx: skylark rule execution context
Returns: Returns:
the value of enableIvy that needs to be set in angularCompilerOptions in the generated tsconfig the value of enableIvy that needs to be set in angularCompilerOptions in the generated tsconfig
""" """
strategy = _compile_strategy(ctx) strategy = _compile_strategy(ctx)
if strategy == 'legacy': if strategy == "legacy":
return False return False
elif strategy == 'global': elif strategy == "global":
return True return True
elif strategy == 'local': elif strategy == "local":
return 'ngtsc' return "ngtsc"
elif strategy == 'jit': elif strategy == "jit":
return 'tsc' return "tsc"
else: else:
fail('unreachable') fail("unreachable")
def _include_ng_files(ctx): def _include_ng_files(ctx):
"""Determines whether Angular outputs will be produced by the current compilation strategy. """Determines whether Angular outputs will be produced by the current compilation strategy.
Args: Args:
ctx: skylark rule execution context ctx: skylark rule execution context
Returns: Returns:
true iff the current compilation strategy will produce View Engine compilation outputs (such as true iff the current compilation strategy will produce View Engine compilation outputs (such as
factory files), false otherwise factory files), false otherwise
""" """
strategy = _compile_strategy(ctx) strategy = _compile_strategy(ctx)
return strategy == 'legacy' or strategy == 'global' return strategy == "legacy" or strategy == "global"
def _basename_of(ctx, file): def _basename_of(ctx, file):
ext_len = len(".ts") ext_len = len(".ts")
if file.short_path.endswith(".ng.html"): if file.short_path.endswith(".ng.html"):
ext_len = len(".ng.html") ext_len = len(".ng.html")
elif file.short_path.endswith(".html"): elif file.short_path.endswith(".html"):
ext_len = len(".html") ext_len = len(".html")
return file.short_path[len(ctx.label.package) + 1:-ext_len] return file.short_path[len(ctx.label.package) + 1:-ext_len]
# Return true if run with bazel (the open-sourced version of blaze), false if # Return true if run with bazel (the open-sourced version of blaze), false if
# run with blaze. # run with blaze.
def _is_bazel(): def _is_bazel():
return not hasattr(native, "genmpm") return not hasattr(native, "genmpm")
def _flat_module_out_file(ctx): def _flat_module_out_file(ctx):
"""Provide a default for the flat_module_out_file attribute. """Provide a default for the flat_module_out_file attribute.
We cannot use the default="" parameter of ctx.attr because the value is calculated We cannot use the default="" parameter of ctx.attr because the value is calculated
from other attributes (name) from other attributes (name)
Args: Args:
ctx: skylark rule execution context ctx: skylark rule execution context
Returns: Returns:
a basename used for the flat module out (no extension) a basename used for the flat module out (no extension)
""" """
if hasattr(ctx.attr, "flat_module_out_file") and ctx.attr.flat_module_out_file: if hasattr(ctx.attr, "flat_module_out_file") and ctx.attr.flat_module_out_file:
return ctx.attr.flat_module_out_file return ctx.attr.flat_module_out_file
return "%s_public_index" % ctx.label.name return "%s_public_index" % ctx.label.name
def _should_produce_flat_module_outs(ctx): def _should_produce_flat_module_outs(ctx):
"""Should we produce flat module outputs. """Should we produce flat module outputs.
We only produce flat module outs when we expect the ng_module is meant to be published, We only produce flat module outs when we expect the ng_module is meant to be published,
based on the presence of the module_name attribute. based on the presence of the module_name attribute.
Args: Args:
ctx: skylark rule execution context ctx: skylark rule execution context
Returns: Returns:
true iff we should run the bundle_index_host to produce flat module metadata and bundle index true iff we should run the bundle_index_host to produce flat module metadata and bundle index
""" """
return _is_bazel() and ctx.attr.module_name return _is_bazel() and ctx.attr.module_name
# Calculate the expected output of the template compiler for every source in # Calculate the expected output of the template compiler for every source in
# in the library. Most of these will be produced as empty files but it is # in the library. Most of these will be produced as empty files but it is
# unknown, without parsing, which will be empty. # unknown, without parsing, which will be empty.
def _expected_outs(ctx): def _expected_outs(ctx):
include_ng_files = _include_ng_files(ctx) include_ng_files = _include_ng_files(ctx)
devmode_js_files = [] devmode_js_files = []
closure_js_files = [] closure_js_files = []
declaration_files = [] declaration_files = []
summary_files = [] summary_files = []
metadata_files = [] metadata_files = []
factory_basename_set = depset([_basename_of(ctx, src) for src in ctx.files.factories]) factory_basename_set = depset([_basename_of(ctx, src) for src in ctx.files.factories])
for src in ctx.files.srcs + ctx.files.assets: for src in ctx.files.srcs + ctx.files.assets:
package_prefix = ctx.label.package + "/" if ctx.label.package else "" package_prefix = ctx.label.package + "/" if ctx.label.package else ""
# Strip external repository name from path if src is from external repository # Strip external repository name from path if src is from external repository
# If src is from external repository, it's short_path will be ../<external_repo_name>/... # If src is from external repository, it's short_path will be ../<external_repo_name>/...
short_path = src.short_path if src.short_path[0:2] != ".." else "/".join(src.short_path.split("/")[2:]) short_path = src.short_path if src.short_path[0:2] != ".." else "/".join(src.short_path.split("/")[2:])
if short_path.endswith(".ts") and not short_path.endswith(".d.ts"): if short_path.endswith(".ts") and not short_path.endswith(".d.ts"):
basename = short_path[len(package_prefix):-len(".ts")] basename = short_path[len(package_prefix):-len(".ts")]
if include_ng_files and (len(factory_basename_set) == 0 or basename in factory_basename_set): if include_ng_files and (len(factory_basename_set) == 0 or basename in factory_basename_set):
devmode_js = [ devmode_js = [
".ngfactory.js", ".ngfactory.js",
".ngsummary.js", ".ngsummary.js",
".js", ".js",
] ]
summaries = [".ngsummary.json"] summaries = [".ngsummary.json"]
metadata = [".metadata.json"] metadata = [".metadata.json"]
else: else:
devmode_js = [".js"] devmode_js = [".js"]
summaries = [] summaries = []
metadata = [] metadata = []
elif include_ng_files and short_path.endswith(".css"): elif include_ng_files and short_path.endswith(".css"):
basename = short_path[len(package_prefix):-len(".css")] basename = short_path[len(package_prefix):-len(".css")]
devmode_js = [ devmode_js = [
".css.shim.ngstyle.js", ".css.shim.ngstyle.js",
".css.ngstyle.js", ".css.ngstyle.js",
] ]
summaries = [] summaries = []
metadata = [] metadata = []
else:
continue
filter_summaries = ctx.attr.filter_summaries
closure_js = [f.replace(".js", ".closure.js") for f in devmode_js if not filter_summaries or not f.endswith(".ngsummary.js")]
declarations = [f.replace(".js", ".d.ts") for f in devmode_js]
devmode_js_files += [ctx.actions.declare_file(basename + ext) for ext in devmode_js]
closure_js_files += [ctx.actions.declare_file(basename + ext) for ext in closure_js]
declaration_files += [ctx.actions.declare_file(basename + ext) for ext in declarations]
summary_files += [ctx.actions.declare_file(basename + ext) for ext in summaries]
if not _is_bazel():
metadata_files += [ctx.actions.declare_file(basename + ext) for ext in metadata]
# We do this just when producing a flat module index for a publishable ng_module
if include_ng_files and _should_produce_flat_module_outs(ctx):
flat_module_out = _flat_module_out_file(ctx)
devmode_js_files.append(ctx.actions.declare_file("%s.js" % flat_module_out))
closure_js_files.append(ctx.actions.declare_file("%s.closure.js" % flat_module_out))
bundle_index_typings = ctx.actions.declare_file("%s.d.ts" % flat_module_out)
declaration_files.append(bundle_index_typings)
metadata_files.append(ctx.actions.declare_file("%s.metadata.json" % flat_module_out))
else: else:
continue bundle_index_typings = None
filter_summaries = ctx.attr.filter_summaries # TODO(alxhub): i18n is only produced by the legacy compiler currently. This should be re-enabled
closure_js = [f.replace(".js", ".closure.js") for f in devmode_js if not filter_summaries or not f.endswith(".ngsummary.js")] # when ngtsc can extract messages
declarations = [f.replace(".js", ".d.ts") for f in devmode_js] if include_ng_files:
i18n_messages_files = [ctx.new_file(ctx.genfiles_dir, ctx.label.name + "_ngc_messages.xmb")]
else:
i18n_messages_files = []
devmode_js_files += [ctx.actions.declare_file(basename + ext) for ext in devmode_js] return struct(
closure_js_files += [ctx.actions.declare_file(basename + ext) for ext in closure_js] closure_js = closure_js_files,
declaration_files += [ctx.actions.declare_file(basename + ext) for ext in declarations] devmode_js = devmode_js_files,
summary_files += [ctx.actions.declare_file(basename + ext) for ext in summaries] declarations = declaration_files,
if not _is_bazel(): summaries = summary_files,
metadata_files += [ctx.actions.declare_file(basename + ext) for ext in metadata] metadata = metadata_files,
bundle_index_typings = bundle_index_typings,
# We do this just when producing a flat module index for a publishable ng_module i18n_messages = i18n_messages_files,
if include_ng_files and _should_produce_flat_module_outs(ctx): )
flat_module_out = _flat_module_out_file(ctx)
devmode_js_files.append(ctx.actions.declare_file("%s.js" % flat_module_out))
closure_js_files.append(ctx.actions.declare_file("%s.closure.js" % flat_module_out))
bundle_index_typings = ctx.actions.declare_file("%s.d.ts" % flat_module_out)
declaration_files.append(bundle_index_typings)
metadata_files.append(ctx.actions.declare_file("%s.metadata.json" % flat_module_out))
else:
bundle_index_typings = None
# TODO(alxhub): i18n is only produced by the legacy compiler currently. This should be re-enabled
# when ngtsc can extract messages
if include_ng_files:
i18n_messages_files = [ctx.new_file(ctx.genfiles_dir, ctx.label.name + "_ngc_messages.xmb")]
else:
i18n_messages_files = []
return struct(
closure_js = closure_js_files,
devmode_js = devmode_js_files,
declarations = declaration_files,
summaries = summary_files,
metadata = metadata_files,
bundle_index_typings = bundle_index_typings,
i18n_messages = i18n_messages_files,
)
def _ngc_tsconfig(ctx, files, srcs, **kwargs): def _ngc_tsconfig(ctx, files, srcs, **kwargs):
outs = _expected_outs(ctx) outs = _expected_outs(ctx)
include_ng_files = _include_ng_files(ctx) include_ng_files = _include_ng_files(ctx)
if "devmode_manifest" in kwargs: if "devmode_manifest" in kwargs:
expected_outs = outs.devmode_js + outs.declarations + outs.summaries + outs.metadata expected_outs = outs.devmode_js + outs.declarations + outs.summaries + outs.metadata
else: else:
expected_outs = outs.closure_js expected_outs = outs.closure_js
angular_compiler_options = { angular_compiler_options = {
"enableResourceInlining": ctx.attr.inline_resources, "enableResourceInlining": ctx.attr.inline_resources,
"generateCodeForLibraries": False, "generateCodeForLibraries": False,
"allowEmptyCodegenFiles": True, "allowEmptyCodegenFiles": True,
# Summaries are only enabled if Angular outputs are to be produced. # Summaries are only enabled if Angular outputs are to be produced.
"enableSummariesForJit": include_ng_files, "enableSummariesForJit": include_ng_files,
"enableIvy": _enable_ivy_value(ctx), "enableIvy": _enable_ivy_value(ctx),
"fullTemplateTypeCheck": ctx.attr.type_check, "fullTemplateTypeCheck": ctx.attr.type_check,
# FIXME: wrong place to de-dupe # FIXME: wrong place to de-dupe
"expectedOut": depset([o.path for o in expected_outs]).to_list() "expectedOut": depset([o.path for o in expected_outs]).to_list(),
} }
if _should_produce_flat_module_outs(ctx): if _should_produce_flat_module_outs(ctx):
angular_compiler_options["flatModuleId"] = ctx.attr.module_name angular_compiler_options["flatModuleId"] = ctx.attr.module_name
angular_compiler_options["flatModuleOutFile"] = _flat_module_out_file(ctx) angular_compiler_options["flatModuleOutFile"] = _flat_module_out_file(ctx)
angular_compiler_options["flatModulePrivateSymbolPrefix"] = "_".join( angular_compiler_options["flatModulePrivateSymbolPrefix"] = "_".join(
[ctx.workspace_name] + ctx.label.package.split("/") + [ctx.label.name, ""]) [ctx.workspace_name] + ctx.label.package.split("/") + [ctx.label.name, ""],
)
return dict(tsc_wrapped_tsconfig(ctx, files, srcs, **kwargs), **{ return dict(tsc_wrapped_tsconfig(ctx, files, srcs, **kwargs), **{
"angularCompilerOptions": angular_compiler_options "angularCompilerOptions": angular_compiler_options,
}) })
def _collect_summaries_aspect_impl(target, ctx): def _collect_summaries_aspect_impl(target, ctx):
results = depset(target.angular.summaries if hasattr(target, "angular") else []) results = depset(target.angular.summaries if hasattr(target, "angular") else [])
# If we are visiting empty-srcs ts_library, this is a re-export # If we are visiting empty-srcs ts_library, this is a re-export
srcs = ctx.rule.attr.srcs if hasattr(ctx.rule.attr, "srcs") else [] srcs = ctx.rule.attr.srcs if hasattr(ctx.rule.attr, "srcs") else []
# "re-export" rules should expose all the files of their deps # "re-export" rules should expose all the files of their deps
if not srcs and hasattr(ctx.rule.attr, "deps"): if not srcs and hasattr(ctx.rule.attr, "deps"):
for dep in ctx.rule.attr.deps: for dep in ctx.rule.attr.deps:
if (hasattr(dep, "angular")): if (hasattr(dep, "angular")):
results = depset(dep.angular.summaries, transitive = [results]) results = depset(dep.angular.summaries, transitive = [results])
return struct(collect_summaries_aspect_result = results) return struct(collect_summaries_aspect_result = results)
_collect_summaries_aspect = aspect( _collect_summaries_aspect = aspect(
implementation = _collect_summaries_aspect_impl, implementation = _collect_summaries_aspect_impl,
@ -278,213 +280,224 @@ _collect_summaries_aspect = aspect(
# Extra options passed to Node when running ngc. # Extra options passed to Node when running ngc.
_EXTRA_NODE_OPTIONS_FLAGS = [ _EXTRA_NODE_OPTIONS_FLAGS = [
# Expose the v8 garbage collection API to JS. # Expose the v8 garbage collection API to JS.
"--node_options=--expose-gc" "--node_options=--expose-gc",
] ]
def ngc_compile_action(ctx, label, inputs, outputs, messages_out, tsconfig_file, def ngc_compile_action(
node_opts, locale=None, i18n_args=[]): ctx,
"""Helper function to create the ngc action. label,
inputs,
outputs,
messages_out,
tsconfig_file,
node_opts,
locale = None,
i18n_args = []):
"""Helper function to create the ngc action.
This is exposed for google3 to wire up i18n replay rules, and is not intended This is exposed for google3 to wire up i18n replay rules, and is not intended
as part of the public API. as part of the public API.
Args: Args:
ctx: skylark context ctx: skylark context
label: the label of the ng_module being compiled label: the label of the ng_module being compiled
inputs: passed to the ngc action's inputs inputs: passed to the ngc action's inputs
outputs: passed to the ngc action's outputs outputs: passed to the ngc action's outputs
messages_out: produced xmb files messages_out: produced xmb files
tsconfig_file: tsconfig file with settings used for the compilation tsconfig_file: tsconfig file with settings used for the compilation
node_opts: list of strings, extra nodejs options. node_opts: list of strings, extra nodejs options.
locale: i18n locale, or None locale: i18n locale, or None
i18n_args: additional command-line arguments to ngc i18n_args: additional command-line arguments to ngc
Returns: Returns:
the parameters of the compilation which will be used to replay the ngc action for i18N. the parameters of the compilation which will be used to replay the ngc action for i18N.
""" """
include_ng_files = _include_ng_files(ctx) include_ng_files = _include_ng_files(ctx)
mnemonic = "AngularTemplateCompile" mnemonic = "AngularTemplateCompile"
progress_message = "Compiling Angular templates (%s) %s" % (_compiler_name(ctx), label) progress_message = "Compiling Angular templates (%s) %s" % (_compiler_name(ctx), label)
if locale: if locale:
mnemonic = "AngularI18NMerging" mnemonic = "AngularI18NMerging"
supports_workers = "0" supports_workers = "0"
progress_message = ("Recompiling Angular templates (ngc) %s for locale %s" % progress_message = ("Recompiling Angular templates (ngc) %s for locale %s" %
(label, locale)) (label, locale))
else: else:
supports_workers = str(int(ctx.attr._supports_workers)) supports_workers = str(int(ctx.attr._supports_workers))
arguments = (list(_EXTRA_NODE_OPTIONS_FLAGS) + arguments = (list(_EXTRA_NODE_OPTIONS_FLAGS) +
["--node_options=%s" % opt for opt in node_opts]) ["--node_options=%s" % opt for opt in node_opts])
# One at-sign makes this a params-file, enabling the worker strategy.
# Two at-signs escapes the argument so it's passed through to ngc
# rather than the contents getting expanded.
if supports_workers == "1":
arguments += ["@@" + tsconfig_file.path]
else:
arguments += ["-p", tsconfig_file.path]
arguments += i18n_args # One at-sign makes this a params-file, enabling the worker strategy.
# Two at-signs escapes the argument so it's passed through to ngc
# rather than the contents getting expanded.
if supports_workers == "1":
arguments += ["@@" + tsconfig_file.path]
else:
arguments += ["-p", tsconfig_file.path]
ctx.actions.run( arguments += i18n_args
progress_message = progress_message,
mnemonic = mnemonic,
inputs = inputs,
outputs = outputs,
arguments = arguments,
executable = ctx.executable.compiler,
execution_requirements = {
"supports-workers": supports_workers,
},
)
if include_ng_files and messages_out != None:
ctx.actions.run( ctx.actions.run(
inputs = list(inputs), progress_message = progress_message,
outputs = messages_out, mnemonic = mnemonic,
executable = ctx.executable._ng_xi18n,
arguments = (_EXTRA_NODE_OPTIONS_FLAGS +
[tsconfig_file.path] +
# The base path is bin_dir because of the way the ngc
# compiler host is configured. So we need to explicitly
# point to genfiles/ to redirect the output.
["../genfiles/" + messages_out[0].short_path]),
progress_message = "Extracting Angular 2 messages (ng_xi18n)",
mnemonic = "Angular2MessageExtractor")
if not locale and not ctx.attr.no_i18n:
return struct(
label = label,
tsconfig = tsconfig_file,
inputs = inputs, inputs = inputs,
outputs = outputs, outputs = outputs,
compiler = ctx.executable.compiler, arguments = arguments,
executable = ctx.executable.compiler,
execution_requirements = {
"supports-workers": supports_workers,
},
) )
return None if include_ng_files and messages_out != None:
ctx.actions.run(
inputs = list(inputs),
outputs = messages_out,
executable = ctx.executable._ng_xi18n,
arguments = (_EXTRA_NODE_OPTIONS_FLAGS +
[tsconfig_file.path] +
# The base path is bin_dir because of the way the ngc
# compiler host is configured. So we need to explicitly
# point to genfiles/ to redirect the output.
["../genfiles/" + messages_out[0].short_path]),
progress_message = "Extracting Angular 2 messages (ng_xi18n)",
mnemonic = "Angular2MessageExtractor",
)
if not locale and not ctx.attr.no_i18n:
return struct(
label = label,
tsconfig = tsconfig_file,
inputs = inputs,
outputs = outputs,
compiler = ctx.executable.compiler,
)
return None
def _compile_action(ctx, inputs, outputs, messages_out, tsconfig_file, node_opts): def _compile_action(ctx, inputs, outputs, messages_out, tsconfig_file, node_opts):
# Give the Angular compiler all the user-listed assets # Give the Angular compiler all the user-listed assets
file_inputs = list(ctx.files.assets) file_inputs = list(ctx.files.assets)
# The compiler only needs to see TypeScript sources from the npm dependencies, # 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. # but may need to look at package.json and ngsummary.json files as well.
if hasattr(ctx.attr, "node_modules"): if hasattr(ctx.attr, "node_modules"):
file_inputs += [f for f in ctx.files.node_modules file_inputs += [
if f.path.endswith(".ts") or f.path.endswith(".json")] f
for f in ctx.files.node_modules
if f.path.endswith(".ts") or f.path.endswith(".json")
]
# If the user supplies a tsconfig.json file, the Angular compiler needs to read it # If the user supplies a tsconfig.json file, the Angular compiler needs to read it
if hasattr(ctx.attr, "tsconfig") and ctx.file.tsconfig: if hasattr(ctx.attr, "tsconfig") and ctx.file.tsconfig:
file_inputs.append(ctx.file.tsconfig) file_inputs.append(ctx.file.tsconfig)
# Collect the inputs and summary files from our deps # Collect the inputs and summary files from our deps
action_inputs = depset(file_inputs, action_inputs = depset(
transitive = [inputs] + [dep.collect_summaries_aspect_result for dep in ctx.attr.deps file_inputs,
if hasattr(dep, "collect_summaries_aspect_result")]) transitive = [inputs] + [
dep.collect_summaries_aspect_result
return ngc_compile_action(ctx, ctx.label, action_inputs, outputs, messages_out, tsconfig_file, node_opts) for dep in ctx.attr.deps
if hasattr(dep, "collect_summaries_aspect_result")
],
def _prodmode_compile_action(ctx, inputs, outputs, tsconfig_file, node_opts):
outs = _expected_outs(ctx)
return _compile_action(ctx, inputs, outputs + outs.closure_js, outs.i18n_messages, tsconfig_file, node_opts)
def _devmode_compile_action(ctx, inputs, outputs, tsconfig_file, node_opts):
outs = _expected_outs(ctx)
compile_action_outputs = outputs + outs.devmode_js + outs.declarations + outs.summaries + outs.metadata
_compile_action(ctx, inputs, compile_action_outputs, None, tsconfig_file, node_opts)
def _ts_expected_outs(ctx, label, srcs_files = []):
# rules_typescript expects a function with two or more arguments, but our
# implementation doesn't use the label(and **kwargs).
_ignored = [label, srcs_files]
return _expected_outs(ctx)
def ng_module_impl(ctx, ts_compile_actions):
"""Implementation function for the ng_module rule.
This is exposed so that google3 can have its own entry point that re-uses this
and is not meant as a public API.
Args:
ctx: the skylark rule context
ts_compile_actions: generates all the actions to run an ngc compilation
Returns:
the result of the ng_module rule as a dict, suitable for
conversion by ts_providers_dict_to_struct
"""
include_ng_files = _include_ng_files(ctx)
providers = ts_compile_actions(
ctx, is_library=True, compile_action=_prodmode_compile_action,
devmode_compile_action=_devmode_compile_action,
tsc_wrapped_tsconfig=_ngc_tsconfig,
outputs = _ts_expected_outs)
outs = _expected_outs(ctx)
if include_ng_files:
providers["angular"] = {
"summaries": outs.summaries,
"metadata": outs.metadata
}
providers["ngc_messages"] = outs.i18n_messages
if include_ng_files and _should_produce_flat_module_outs(ctx):
if len(outs.metadata) > 1:
fail("expecting exactly one metadata output for " + str(ctx.label))
providers["angular"]["flat_module_metadata"] = struct(
module_name = ctx.attr.module_name,
metadata_file = outs.metadata[0],
typings_file = outs.bundle_index_typings,
flat_module_out_file = _flat_module_out_file(ctx),
) )
return providers return ngc_compile_action(ctx, ctx.label, action_inputs, outputs, messages_out, tsconfig_file, node_opts)
def _prodmode_compile_action(ctx, inputs, outputs, tsconfig_file, node_opts):
outs = _expected_outs(ctx)
return _compile_action(ctx, inputs, outputs + outs.closure_js, outs.i18n_messages, tsconfig_file, node_opts)
def _devmode_compile_action(ctx, inputs, outputs, tsconfig_file, node_opts):
outs = _expected_outs(ctx)
compile_action_outputs = outputs + outs.devmode_js + outs.declarations + outs.summaries + outs.metadata
_compile_action(ctx, inputs, compile_action_outputs, None, tsconfig_file, node_opts)
def _ts_expected_outs(ctx, label, srcs_files = []):
# rules_typescript expects a function with two or more arguments, but our
# implementation doesn't use the label(and **kwargs).
_ignored = [label, srcs_files]
return _expected_outs(ctx)
def ng_module_impl(ctx, ts_compile_actions):
"""Implementation function for the ng_module rule.
This is exposed so that google3 can have its own entry point that re-uses this
and is not meant as a public API.
Args:
ctx: the skylark rule context
ts_compile_actions: generates all the actions to run an ngc compilation
Returns:
the result of the ng_module rule as a dict, suitable for
conversion by ts_providers_dict_to_struct
"""
include_ng_files = _include_ng_files(ctx)
providers = ts_compile_actions(
ctx,
is_library = True,
compile_action = _prodmode_compile_action,
devmode_compile_action = _devmode_compile_action,
tsc_wrapped_tsconfig = _ngc_tsconfig,
outputs = _ts_expected_outs,
)
outs = _expected_outs(ctx)
if include_ng_files:
providers["angular"] = {
"summaries": outs.summaries,
"metadata": outs.metadata,
}
providers["ngc_messages"] = outs.i18n_messages
if include_ng_files and _should_produce_flat_module_outs(ctx):
if len(outs.metadata) > 1:
fail("expecting exactly one metadata output for " + str(ctx.label))
providers["angular"]["flat_module_metadata"] = struct(
module_name = ctx.attr.module_name,
metadata_file = outs.metadata[0],
typings_file = outs.bundle_index_typings,
flat_module_out_file = _flat_module_out_file(ctx),
)
return providers
def _ng_module_impl(ctx): def _ng_module_impl(ctx):
return ts_providers_dict_to_struct(ng_module_impl(ctx, compile_ts)) return ts_providers_dict_to_struct(ng_module_impl(ctx, compile_ts))
NG_MODULE_ATTRIBUTES = { NG_MODULE_ATTRIBUTES = {
"srcs": attr.label_list(allow_files = [".ts"]), "srcs": attr.label_list(allow_files = [".ts"]),
"deps": attr.label_list(aspects = DEPS_ASPECTS + [_collect_summaries_aspect]), "deps": attr.label_list(aspects = DEPS_ASPECTS + [_collect_summaries_aspect]),
"assets": attr.label_list(allow_files = [ "assets": attr.label_list(allow_files = [
".css", ".css",
# TODO(alexeagle): change this to ".ng.html" when usages updated # TODO(alexeagle): change this to ".ng.html" when usages updated
".html", ".html",
]), ]),
"factories": attr.label_list( "factories": attr.label_list(
allow_files = [".ts", ".html"], allow_files = [".ts", ".html"],
mandatory = False), mandatory = False,
),
"filter_summaries": attr.bool(default = False), "filter_summaries": attr.bool(default = False),
"type_check": attr.bool(default = True), "type_check": attr.bool(default = True),
"inline_resources": attr.bool(default = True), "inline_resources": attr.bool(default = True),
"no_i18n": attr.bool(default = False), "no_i18n": attr.bool(default = False),
"compiler": attr.label( "compiler": attr.label(
default = Label("//packages/bazel/src/ngc-wrapped"), default = Label("//packages/bazel/src/ngc-wrapped"),
executable = True, executable = True,
cfg = "host", cfg = "host",
), ),
"_ng_xi18n": attr.label( "_ng_xi18n": attr.label(
default = Label("//packages/bazel/src/ngc-wrapped:xi18n"), default = Label("//packages/bazel/src/ngc-wrapped:xi18n"),
executable = True, executable = True,
cfg = "host", cfg = "host",
), ),
"_supports_workers": attr.bool(default = True), "_supports_workers": attr.bool(default = True),
} }
@ -495,9 +508,8 @@ NG_MODULE_RULE_ATTRS = dict(dict(COMMON_ATTRIBUTES, **NG_MODULE_ATTRIBUTES), **{
# The default assumes the user specified a target "node_modules" in their # The default assumes the user specified a target "node_modules" in their
# root BUILD file. # root BUILD file.
"node_modules": attr.label( "node_modules": attr.label(
default = Label("@//:node_modules") default = Label("@//:node_modules"),
), ),
"entry_point": attr.string(), "entry_point": attr.string(),
# Default is %{name}_public_index # Default is %{name}_public_index
@ -516,7 +528,6 @@ ng_module = rule(
outputs = COMMON_OUTPUTS, outputs = COMMON_OUTPUTS,
) )
# TODO(alxhub): this rule causes legacy ngc to produce Ivy outputs from global analysis information. # TODO(alxhub): this rule causes legacy ngc to produce Ivy outputs from global analysis information.
# It exists to facilitate testing of the Ivy runtime until ngtsc is mature enough to be used # It exists to facilitate testing of the Ivy runtime until ngtsc is mature enough to be used
# instead, and should be removed once ngtsc is capable of fulfilling the same requirements. # instead, and should be removed once ngtsc is capable of fulfilling the same requirements.

View File

@ -6,41 +6,46 @@
""" """
load("@build_bazel_rules_nodejs//:internal/collect_es6_sources.bzl", "collect_es6_sources") load("@build_bazel_rules_nodejs//:internal/collect_es6_sources.bzl", "collect_es6_sources")
load("@build_bazel_rules_nodejs//:internal/rollup/rollup_bundle.bzl", load(
"write_rollup_config", "@build_bazel_rules_nodejs//:internal/rollup/rollup_bundle.bzl",
"rollup_module_mappings_aspect", "ROLLUP_ATTRS",
"run_uglify", "rollup_module_mappings_aspect",
"ROLLUP_ATTRS") "run_uglify",
load("@build_bazel_rules_nodejs//:internal/npm_package/npm_package.bzl", "write_rollup_config",
"NPM_PACKAGE_ATTRS", )
"NPM_PACKAGE_OUTPUTS", load(
"create_package") "@build_bazel_rules_nodejs//:internal/npm_package/npm_package.bzl",
"NPM_PACKAGE_ATTRS",
"NPM_PACKAGE_OUTPUTS",
"create_package",
)
load("@build_bazel_rules_nodejs//:internal/node.bzl", "sources_aspect") load("@build_bazel_rules_nodejs//:internal/node.bzl", "sources_aspect")
load("//packages/bazel/src:esm5.bzl", "esm5_outputs_aspect", "flatten_esm5", "esm5_root_dir") load("//packages/bazel/src:esm5.bzl", "esm5_outputs_aspect", "esm5_root_dir", "flatten_esm5")
# Convert from some-dash-case to someCamelCase # Convert from some-dash-case to someCamelCase
def _convert_dash_case_to_camel_case(s): def _convert_dash_case_to_camel_case(s):
parts = s.split("-") parts = s.split("-")
# First letter in the result is always unchanged
return s[0] + "".join([p.title() for p in parts])[1:] # First letter in the result is always unchanged
return s[0] + "".join([p.title() for p in parts])[1:]
# Convert from a package name on npm to an identifier that's a legal global symbol # Convert from a package name on npm to an identifier that's a legal global symbol
# @angular/core -> ng.core # @angular/core -> ng.core
# @angular/platform-browser-dynamic/testing -> ng.platformBrowserDynamic.testing # @angular/platform-browser-dynamic/testing -> ng.platformBrowserDynamic.testing
def _global_name(package_name): def _global_name(package_name):
# strip npm scoped package qualifier # strip npm scoped package qualifier
start = 1 if package_name.startswith("@") else 0 start = 1 if package_name.startswith("@") else 0
parts = package_name[start:].split("/") parts = package_name[start:].split("/")
result_parts = [] result_parts = []
for p in parts: for p in parts:
# Special case for angular's short name # Special case for angular's short name
if p == "angular": if p == "angular":
result_parts.append("ng") result_parts.append("ng")
else: else:
result_parts.append(_convert_dash_case_to_camel_case(p)) result_parts.append(_convert_dash_case_to_camel_case(p))
return ".".join(result_parts) return ".".join(result_parts)
WELL_KNOWN_GLOBALS = { p: _global_name(p) for p in [ WELL_KNOWN_GLOBALS = {p: _global_name(p) for p in [
"@angular/upgrade", "@angular/upgrade",
"@angular/upgrade/static", "@angular/upgrade/static",
"@angular/forms", "@angular/forms",
@ -77,242 +82,259 @@ WELL_KNOWN_GLOBALS = { p: _global_name(p) for p in [
]} ]}
def _rollup(ctx, bundle_name, rollup_config, entry_point, inputs, js_output, format = "es", package_name = "", include_tslib = False): def _rollup(ctx, bundle_name, rollup_config, entry_point, inputs, js_output, format = "es", package_name = "", include_tslib = False):
map_output = ctx.actions.declare_file(js_output.basename + ".map", sibling = js_output) map_output = ctx.actions.declare_file(js_output.basename + ".map", sibling = js_output)
args = ctx.actions.args() args = ctx.actions.args()
args.add("--config", rollup_config) args.add("--config", rollup_config)
args.add("--input", entry_point) args.add("--input", entry_point)
args.add("--output.file", js_output) args.add("--output.file", js_output)
args.add("--output.format", format) args.add("--output.format", format)
if package_name: if package_name:
args.add("--output.name", _global_name(package_name)) args.add("--output.name", _global_name(package_name))
args.add("--amd.id", package_name) args.add("--amd.id", package_name)
# Note: if the input has external source maps then we need to also install and use # Note: if the input has external source maps then we need to also install and use
# `rollup-plugin-sourcemaps`, which will require us to use rollup.config.js file instead # `rollup-plugin-sourcemaps`, which will require us to use rollup.config.js file instead
# of command line args # of command line args
args.add("--sourcemap") args.add("--sourcemap")
globals = dict(WELL_KNOWN_GLOBALS, **ctx.attr.globals) globals = dict(WELL_KNOWN_GLOBALS, **ctx.attr.globals)
external = globals.keys() external = globals.keys()
if not include_tslib: if not include_tslib:
external.append("tslib") external.append("tslib")
args.add_joined("--external", external, join_with=",") args.add_joined("--external", external, join_with = ",")
args.add_joined( args.add_joined(
"--globals", "--globals",
["%s:%s" % g for g in globals.items()], ["%s:%s" % g for g in globals.items()],
join_with=",") join_with = ",",
)
args.add("--silent") args.add("--silent")
other_inputs = [ctx.executable._rollup, rollup_config] other_inputs = [ctx.executable._rollup, rollup_config]
if ctx.file.license_banner: if ctx.file.license_banner:
other_inputs.append(ctx.file.license_banner) other_inputs.append(ctx.file.license_banner)
if ctx.version_file: if ctx.version_file:
other_inputs.append(ctx.version_file) other_inputs.append(ctx.version_file)
ctx.actions.run( ctx.actions.run(
progress_message = "ng_package: Rollup %s %s" % (bundle_name, ctx.label), progress_message = "ng_package: Rollup %s %s" % (bundle_name, ctx.label),
mnemonic = "AngularPackageRollup", mnemonic = "AngularPackageRollup",
inputs = inputs.to_list() + other_inputs, inputs = inputs.to_list() + other_inputs,
outputs = [js_output, map_output], outputs = [js_output, map_output],
executable = ctx.executable._rollup, executable = ctx.executable._rollup,
arguments = [args], arguments = [args],
) )
return struct( return struct(
js = js_output, js = js_output,
map = map_output, map = map_output,
) )
# convert from [{js: js_file1, map: map_file1}, ...] to # convert from [{js: js_file1, map: map_file1}, ...] to
# [js_filepath1, map_filepath1, ...] # [js_filepath1, map_filepath1, ...]
def _flatten_paths(directory): def _flatten_paths(directory):
result = [] result = []
for f in directory: for f in directory:
result.append(f.js.path) result.append(f.js.path)
if f.map: if f.map:
result.append(f.map.path) result.append(f.map.path)
return result return result
# takes an depset of files and returns an array that doesn't contain any generated files by ngc # takes an depset of files and returns an array that doesn't contain any generated files by ngc
def _filter_out_generated_files(files): def _filter_out_generated_files(files):
result = [] result = []
for file in files: for file in files:
if (not(file.path.endswith(".ngfactory.js") or file.path.endswith(".ngsummary.js") or file.path.endswith(".ngstyle.js"))): if (not (file.path.endswith(".ngfactory.js") or file.path.endswith(".ngsummary.js") or file.path.endswith(".ngstyle.js"))):
result.append(file) result.append(file)
return depset(result) return depset(result)
def _esm2015_root_dir(ctx): def _esm2015_root_dir(ctx):
return ctx.label.name + ".es6" return ctx.label.name + ".es6"
# ng_package produces package that is npm-ready. # ng_package produces package that is npm-ready.
def _ng_package_impl(ctx): def _ng_package_impl(ctx):
npm_package_directory = ctx.actions.declare_directory("%s.ng_pkg" % ctx.label.name) npm_package_directory = ctx.actions.declare_directory("%s.ng_pkg" % ctx.label.name)
esm_2015_files = _filter_out_generated_files(collect_es6_sources(ctx)) esm_2015_files = _filter_out_generated_files(collect_es6_sources(ctx))
esm5_sources = _filter_out_generated_files(flatten_esm5(ctx)) esm5_sources = _filter_out_generated_files(flatten_esm5(ctx))
# These accumulators match the directory names where the files live in the # These accumulators match the directory names where the files live in the
# Angular package format. # Angular package format.
fesm2015 = [] fesm2015 = []
fesm5 = [] fesm5 = []
esm2015 = [] esm2015 = []
esm5 = [] esm5 = []
bundles = [] bundles = []
# For Angular Package Format v6, we put all the individual .js files in the # For Angular Package Format v6, we put all the individual .js files in the
# esm5/ and esm2015/ folders. # esm5/ and esm2015/ folders.
for f in esm5_sources.to_list(): for f in esm5_sources.to_list():
if f.path.endswith(".js"): if f.path.endswith(".js"):
esm5.append(struct(js = f, map = None)) esm5.append(struct(js = f, map = None))
for f in esm_2015_files.to_list(): for f in esm_2015_files.to_list():
if f.path.endswith(".js"): if f.path.endswith(".js"):
esm2015.append(struct(js = f, map = None)) esm2015.append(struct(js = f, map = None))
# We infer the entry points to be: # We infer the entry points to be:
# - ng_module rules in the deps (they have an "angular" provider) # - ng_module rules in the deps (they have an "angular" provider)
# - in this package or a subpackage # - in this package or a subpackage
# - those that have a module_name attribute (they produce flat module metadata) # - those that have a module_name attribute (they produce flat module metadata)
flat_module_metadata = [] flat_module_metadata = []
# Name given in the package.json name field, eg. @angular/core/testing
package_name = "" # Name given in the package.json name field, eg. @angular/core/testing
deps_in_package = [d for d in ctx.attr.deps if d.label.package.startswith(ctx.label.package)] package_name = ""
for dep in deps_in_package: deps_in_package = [d for d in ctx.attr.deps if d.label.package.startswith(ctx.label.package)]
# Intentionally evaluates to empty string for the main entry point for dep in deps_in_package:
entry_point = dep.label.package[len(ctx.label.package) + 1:] # Intentionally evaluates to empty string for the main entry point
if hasattr(dep, "module_name"): entry_point = dep.label.package[len(ctx.label.package) + 1:]
package_name = dep.module_name if hasattr(dep, "module_name"):
if hasattr(dep, "angular") and hasattr(dep.angular, "flat_module_metadata"): package_name = dep.module_name
flat_module_metadata.append(dep.angular.flat_module_metadata) if hasattr(dep, "angular") and hasattr(dep.angular, "flat_module_metadata"):
flat_module_out_file = dep.angular.flat_module_metadata.flat_module_out_file + ".js" flat_module_metadata.append(dep.angular.flat_module_metadata)
flat_module_out_file = dep.angular.flat_module_metadata.flat_module_out_file + ".js"
else:
# fallback to a reasonable default
flat_module_out_file = "index.js"
es2015_entry_point = "/".join([p for p in [
ctx.bin_dir.path,
ctx.label.package,
_esm2015_root_dir(ctx),
ctx.label.package,
entry_point,
flat_module_out_file,
] if p])
es5_entry_point = "/".join([p for p in [
ctx.label.package,
entry_point,
flat_module_out_file,
] if p])
if entry_point:
# TODO jasonaden says there is no particular reason these filenames differ
prefix = primary_entry_point_name(ctx.attr.name, ctx.attr.entry_point, ctx.attr.entry_point_name)
umd_output_filename = "-".join([prefix] + entry_point.split("/"))
fesm_output_filename = entry_point.replace("/", "__")
fesm2015_output = ctx.actions.declare_file("fesm2015/%s.js" % fesm_output_filename)
fesm5_output = ctx.actions.declare_file("%s.js" % fesm_output_filename)
umd_output = ctx.actions.declare_file("%s.umd.js" % umd_output_filename)
min_output = ctx.actions.declare_file("%s.umd.min.js" % umd_output_filename)
else:
fesm2015_output = ctx.outputs.fesm2015
fesm5_output = ctx.outputs.fesm5
umd_output = ctx.outputs.umd
min_output = ctx.outputs.umd_min
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))
bundles.append(
_rollup(
ctx,
"umd",
esm5_config,
es5_entry_point,
esm5_sources + ctx.files.node_modules,
umd_output,
format = "umd",
package_name = package_name,
include_tslib = True,
),
)
uglify_sourcemap = run_uglify(
ctx,
umd_output,
min_output,
config_name = entry_point.replace("/", "_"),
)
bundles.append(struct(js = min_output, map = uglify_sourcemap))
packager_inputs = (
ctx.files.srcs +
ctx.files.data +
esm5_sources.to_list() +
depset(transitive = [
d.typescript.transitive_declarations
for d in ctx.attr.deps
if hasattr(d, "typescript")
]).to_list() +
[f.js for f in fesm2015 + fesm5 + esm2015 + esm5 + bundles] +
[f.map for f in fesm2015 + fesm5 + esm2015 + esm5 + bundles if f.map]
)
packager_args = ctx.actions.args()
packager_args.use_param_file("%s", use_always = True)
# The order of arguments matters here, as they are read in order in packager.ts.
packager_args.add(npm_package_directory.path)
packager_args.add(ctx.label.package)
packager_args.add_joined([ctx.bin_dir.path, ctx.label.package], join_with = "/")
packager_args.add_joined([ctx.genfiles_dir.path, ctx.label.package], join_with = "/")
# Marshal the metadata into a JSON string so we can parse the data structure
# in the TypeScript program easily.
metadata_arg = {}
for m in flat_module_metadata:
packager_inputs.extend([m.metadata_file])
metadata_arg[m.module_name] = {
"index": m.typings_file.path.replace(".d.ts", ".js"),
"typings": m.typings_file.path,
"metadata": m.metadata_file.path,
}
packager_args.add(str(metadata_arg))
if ctx.file.readme_md:
packager_inputs.append(ctx.file.readme_md)
packager_args.add(ctx.file.readme_md.path)
else: else:
# fallback to a reasonable default # placeholder
flat_module_out_file = "index.js" packager_args.add("")
es2015_entry_point = "/".join([p for p in [ packager_args.add_joined(_flatten_paths(fesm2015), join_with = ",")
ctx.bin_dir.path, packager_args.add_joined(_flatten_paths(fesm5), join_with = ",")
ctx.label.package, packager_args.add_joined(_flatten_paths(esm2015), join_with = ",")
_esm2015_root_dir(ctx), packager_args.add_joined(_flatten_paths(esm5), join_with = ",")
ctx.label.package, packager_args.add_joined(_flatten_paths(bundles), join_with = ",")
entry_point, packager_args.add_joined([s.path for s in ctx.files.srcs], join_with = ",")
flat_module_out_file,
] if p])
es5_entry_point = "/".join([p for p in [ # TODO: figure out a better way to gather runfiles providers from the transitive closure.
ctx.label.package, packager_args.add_joined([d.path for d in ctx.files.data], join_with = ",")
entry_point,
flat_module_out_file,
] if p])
if entry_point: if ctx.file.license_banner:
# TODO jasonaden says there is no particular reason these filenames differ packager_inputs.append(ctx.file.license_banner)
prefix = primary_entry_point_name(ctx.attr.name, ctx.attr.entry_point, ctx.attr.entry_point_name) packager_args.add(ctx.file.license_banner)
umd_output_filename = "-".join([prefix] + entry_point.split("/"))
fesm_output_filename = entry_point.replace("/", "__")
fesm2015_output = ctx.actions.declare_file("fesm2015/%s.js" % fesm_output_filename)
fesm5_output = ctx.actions.declare_file("%s.js" % fesm_output_filename)
umd_output = ctx.actions.declare_file("%s.umd.js" % umd_output_filename)
min_output = ctx.actions.declare_file("%s.umd.min.js" % umd_output_filename)
else: else:
fesm2015_output = ctx.outputs.fesm2015 # placeholder
fesm5_output = ctx.outputs.fesm5 packager_args.add("")
umd_output = ctx.outputs.umd
min_output = ctx.outputs.umd_min
esm2015_config = write_rollup_config(ctx, [], "/".join([ctx.bin_dir.path, ctx.label.package, _esm2015_root_dir(ctx)]), filename="_%s.rollup_esm2015.conf.js") ctx.actions.run(
esm5_config = write_rollup_config(ctx, [], "/".join([ctx.bin_dir.path, ctx.label.package, esm5_root_dir(ctx)]), filename="_%s.rollup_esm5.conf.js") progress_message = "Angular Packaging: building npm package %s" % str(ctx.label),
mnemonic = "AngularPackage",
inputs = packager_inputs,
outputs = [npm_package_directory],
executable = ctx.executable._ng_packager,
arguments = [packager_args],
)
fesm2015.append(_rollup(ctx, "fesm2015", esm2015_config, es2015_entry_point, esm_2015_files + ctx.files.node_modules, fesm2015_output)) devfiles = depset()
fesm5.append(_rollup(ctx, "fesm5", esm5_config, es5_entry_point, esm5_sources + ctx.files.node_modules, fesm5_output)) if ctx.attr.include_devmode_srcs:
for d in ctx.attr.deps:
devfiles = depset(transitive = [devfiles, d.files, d.node_sources])
bundles.append( # Re-use the create_package function from the nodejs npm_package rule.
_rollup(ctx, "umd", esm5_config, es5_entry_point, esm5_sources + ctx.files.node_modules, umd_output, package_dir = create_package(
format = "umd", package_name = package_name, include_tslib = True)) ctx,
uglify_sourcemap = run_uglify(ctx, umd_output, min_output, devfiles.to_list(),
config_name = entry_point.replace("/", "_")) [npm_package_directory] + ctx.files.packages,
bundles.append(struct(js = min_output, map = uglify_sourcemap)) )
return [DefaultInfo(
packager_inputs = ( files = depset([package_dir]),
ctx.files.srcs + )]
ctx.files.data +
esm5_sources.to_list() +
depset(transitive = [d.typescript.transitive_declarations
for d in ctx.attr.deps
if hasattr(d, "typescript")]).to_list() +
[f.js for f in fesm2015 + fesm5 + esm2015 + esm5 + bundles] +
[f.map for f in fesm2015 + fesm5 + esm2015 + esm5 + bundles if f.map])
packager_args = ctx.actions.args()
packager_args.use_param_file("%s", use_always = True)
# The order of arguments matters here, as they are read in order in packager.ts.
packager_args.add(npm_package_directory.path)
packager_args.add(ctx.label.package)
packager_args.add_joined([ctx.bin_dir.path, ctx.label.package], join_with="/")
packager_args.add_joined([ctx.genfiles_dir.path, ctx.label.package], join_with="/")
# Marshal the metadata into a JSON string so we can parse the data structure
# in the TypeScript program easily.
metadata_arg = {}
for m in flat_module_metadata:
packager_inputs.extend([m.metadata_file])
metadata_arg[m.module_name] = {
"index": m.typings_file.path.replace(".d.ts", ".js"),
"typings": m.typings_file.path,
"metadata": m.metadata_file.path,
}
packager_args.add(str(metadata_arg))
if ctx.file.readme_md:
packager_inputs.append(ctx.file.readme_md)
packager_args.add(ctx.file.readme_md.path)
else:
# placeholder
packager_args.add("")
packager_args.add_joined(_flatten_paths(fesm2015), join_with=",")
packager_args.add_joined(_flatten_paths(fesm5), join_with=",")
packager_args.add_joined(_flatten_paths(esm2015), join_with=",")
packager_args.add_joined(_flatten_paths(esm5), join_with=",")
packager_args.add_joined(_flatten_paths(bundles), join_with=",")
packager_args.add_joined([s.path for s in ctx.files.srcs], join_with=",")
# TODO: figure out a better way to gather runfiles providers from the transitive closure.
packager_args.add_joined([d.path for d in ctx.files.data], join_with=",")
if ctx.file.license_banner:
packager_inputs.append(ctx.file.license_banner)
packager_args.add(ctx.file.license_banner)
else:
# placeholder
packager_args.add("")
ctx.actions.run(
progress_message = "Angular Packaging: building npm package %s" % str(ctx.label),
mnemonic = "AngularPackage",
inputs = packager_inputs,
outputs = [npm_package_directory],
executable = ctx.executable._ng_packager,
arguments = [packager_args],
)
devfiles = depset()
if ctx.attr.include_devmode_srcs:
for d in ctx.attr.deps:
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(
ctx,
devfiles.to_list(),
[npm_package_directory] + ctx.files.packages)
return [DefaultInfo(
files = depset([package_dir])
)]
NG_PACKAGE_ATTRS = dict(NPM_PACKAGE_ATTRS, **dict(ROLLUP_ATTRS, **{ NG_PACKAGE_ATTRS = dict(NPM_PACKAGE_ATTRS, **dict(ROLLUP_ATTRS, **{
"srcs": attr.label_list(allow_files = True), "srcs": attr.label_list(allow_files = True),
@ -327,22 +349,29 @@ NG_PACKAGE_ATTRS = dict(NPM_PACKAGE_ATTRS, **dict(ROLLUP_ATTRS, **{
), ),
"include_devmode_srcs": attr.bool(default = False), "include_devmode_srcs": attr.bool(default = False),
"readme_md": attr.label(allow_single_file = FileType([".md"])), "readme_md": attr.label(allow_single_file = FileType([".md"])),
"globals": attr.string_dict(default={}), "globals": attr.string_dict(default = {}),
"entry_point_name": attr.string( "entry_point_name": attr.string(
doc = "Name to use when generating bundle files for the primary entry-point.", doc = "Name to use when generating bundle files for the primary entry-point.",
), ),
"_ng_packager": attr.label( "_ng_packager": attr.label(
default=Label("//packages/bazel/src/ng_package:packager"), default = Label("//packages/bazel/src/ng_package:packager"),
executable=True, cfg="host"), executable = True,
cfg = "host",
),
"_rollup": attr.label( "_rollup": attr.label(
default=Label("@build_bazel_rules_nodejs//internal/rollup"), default = Label("@build_bazel_rules_nodejs//internal/rollup"),
executable=True, cfg="host"), executable = True,
cfg = "host",
),
"_rollup_config_tmpl": attr.label( "_rollup_config_tmpl": attr.label(
default=Label("@build_bazel_rules_nodejs//internal/rollup:rollup.config.js"), default = Label("@build_bazel_rules_nodejs//internal/rollup:rollup.config.js"),
allow_single_file=True), allow_single_file = True,
),
"_uglify": attr.label( "_uglify": attr.label(
default=Label("@build_bazel_rules_nodejs//internal/rollup:uglify"), default = Label("@build_bazel_rules_nodejs//internal/rollup:uglify"),
executable=True, cfg="host"), executable = True,
cfg = "host",
),
})) }))
# Angular wants these named after the entry_point, # Angular wants these named after the entry_point,
@ -352,32 +381,32 @@ NG_PACKAGE_ATTRS = dict(NPM_PACKAGE_ATTRS, **dict(ROLLUP_ATTRS, **{
# some/path/to/my/package/index.js # some/path/to/my/package/index.js
# we assume the files should be named "package.*.js" # we assume the files should be named "package.*.js"
def primary_entry_point_name(name, entry_point, entry_point_name): def primary_entry_point_name(name, entry_point, entry_point_name):
if entry_point_name: if entry_point_name:
# If an explicit entry_point_name is given, use that. # If an explicit entry_point_name is given, use that.
return entry_point_name return entry_point_name
elif entry_point.find("/") >= 0: elif entry_point.find("/") >= 0:
# If the entry_point has multiple path segments, use the second one. # If the entry_point has multiple path segments, use the second one.
# E.g., for "@angular/cdk/a11y", use "cdk". # E.g., for "@angular/cdk/a11y", use "cdk".
return entry_point.split("/")[-2] return entry_point.split("/")[-2]
else: else:
# Fall back to the name of the ng_package rule. # Fall back to the name of the ng_package rule.
return name return name
def ng_package_outputs(name, entry_point, entry_point_name): def ng_package_outputs(name, entry_point, entry_point_name):
basename = primary_entry_point_name(name, entry_point, entry_point_name) basename = primary_entry_point_name(name, entry_point, entry_point_name)
outputs = { outputs = {
"fesm5": "fesm5/%s.js" % basename, "fesm5": "fesm5/%s.js" % basename,
"fesm2015": "fesm2015/%s.js" % basename, "fesm2015": "fesm2015/%s.js" % basename,
"umd": "%s.umd.js" % basename, "umd": "%s.umd.js" % basename,
"umd_min": "%s.umd.min.js" % basename, "umd_min": "%s.umd.min.js" % basename,
} }
for key in NPM_PACKAGE_OUTPUTS: for key in NPM_PACKAGE_OUTPUTS:
# NPM_PACKAGE_OUTPUTS is a "normal" dict-valued outputs so it looks like # NPM_PACKAGE_OUTPUTS is a "normal" dict-valued outputs so it looks like
# "pack": "%{name}.pack", # "pack": "%{name}.pack",
# But this is a function-valued outputs. # But this is a function-valued outputs.
# Bazel won't replace the %{name} token so we have to do it. # Bazel won't replace the %{name} token so we have to do it.
outputs[key] = NPM_PACKAGE_OUTPUTS[key].replace("%{name}", name) outputs[key] = NPM_PACKAGE_OUTPUTS[key].replace("%{name}", name)
return outputs return outputs
ng_package = rule( ng_package = rule(
implementation = _ng_package_impl, implementation = _ng_package_impl,

View File

@ -10,118 +10,125 @@
build-optimizer is hard-coded to look for and transform. build-optimizer is hard-coded to look for and transform.
""" """
load("@build_bazel_rules_nodejs//internal/rollup:rollup_bundle.bzl", load(
"rollup_module_mappings_aspect", "@build_bazel_rules_nodejs//internal/rollup:rollup_bundle.bzl",
"ROLLUP_ATTRS", "ROLLUP_ATTRS",
"ROLLUP_OUTPUTS", "ROLLUP_OUTPUTS",
"write_rollup_config", "rollup_module_mappings_aspect",
"run_rollup", "run_rollup",
"run_sourcemapexplorer",
"run_uglify", "run_uglify",
"run_sourcemapexplorer") "write_rollup_config",
)
load("@build_bazel_rules_nodejs//internal:collect_es6_sources.bzl", collect_es2015_sources = "collect_es6_sources") load("@build_bazel_rules_nodejs//internal:collect_es6_sources.bzl", collect_es2015_sources = "collect_es6_sources")
load(":esm5.bzl", "esm5_outputs_aspect", "flatten_esm5", "esm5_root_dir") load(":esm5.bzl", "esm5_outputs_aspect", "esm5_root_dir", "flatten_esm5")
PACKAGES=["packages/core/src", "packages/common/src", "packages/compiler/src", "external/rxjs"] PACKAGES = ["packages/core/src", "packages/common/src", "packages/compiler/src", "external/rxjs"]
PLUGIN_CONFIG="{sideEffectFreeModules: [\n%s]}" % ",\n".join( PLUGIN_CONFIG = "{sideEffectFreeModules: [\n%s]}" % ",\n".join(
[" '.esm5/{0}'".format(p) for p in PACKAGES]) [" '.esm5/{0}'".format(p) for p in PACKAGES],
BO_ROLLUP="angular_cli/packages/angular_devkit/build_optimizer/src/build-optimizer/rollup-plugin.js" )
BO_PLUGIN="require('%s').default(%s)" % (BO_ROLLUP, PLUGIN_CONFIG) BO_ROLLUP = "angular_cli/packages/angular_devkit/build_optimizer/src/build-optimizer/rollup-plugin.js"
BO_PLUGIN = "require('%s').default(%s)" % (BO_ROLLUP, PLUGIN_CONFIG)
def _use_plain_rollup(ctx): def _use_plain_rollup(ctx):
"""Determine whether to use the Angular or upstream versions of the rollup_bundle rule. """Determine whether to use the Angular or upstream versions of the rollup_bundle rule.
In most modes, the Angular version of rollup is used. This runs build optimizer as part of its In most modes, the Angular version of rollup is used. This runs build optimizer as part of its
processing, which affects decorators and annotations. processing, which affects decorators and annotations.
In JIT modes, an emulation of the upstream rollup_bundle rule is used. This avoids running In JIT modes, an emulation of the upstream rollup_bundle rule is used. This avoids running
build optimizer on code which isn't designed to be optimized by it. build optimizer on code which isn't designed to be optimized by it.
Args: Args:
ctx: skylark rule execution context ctx: skylark rule execution context
Returns: Returns:
true iff the Angular version of rollup with build optimizer should be used, false otherwise true iff the Angular version of rollup with build optimizer should be used, false otherwise
""" """
if 'compile' not in ctx.var: if "compile" not in ctx.var:
return False return False
strategy = ctx.var['compile']
return strategy == 'jit'
strategy = ctx.var["compile"]
return strategy == "jit"
def run_brotli(ctx, input, output): def run_brotli(ctx, input, output):
ctx.actions.run( ctx.actions.run(
executable = ctx.executable._brotli, executable = ctx.executable._brotli,
inputs = [input], inputs = [input],
outputs = [output], outputs = [output],
arguments = ["--output=%s" % output.path, input.path], arguments = ["--output=%s" % output.path, input.path],
) )
# Borrowed from bazelbuild/rules_nodejs # Borrowed from bazelbuild/rules_nodejs
def _run_tsc(ctx, input, output): def _run_tsc(ctx, input, output):
args = ctx.actions.args() args = ctx.actions.args()
args.add("--target", "es5") args.add("--target", "es5")
args.add("--allowJS") args.add("--allowJS")
args.add(input) args.add(input)
args.add("--outFile", output) args.add("--outFile", output)
ctx.action( ctx.action(
executable = ctx.executable._tsc, executable = ctx.executable._tsc,
inputs = [input], inputs = [input],
outputs = [output], outputs = [output],
arguments = [args] arguments = [args],
) )
# Borrowed from bazelbuild/rules_nodejs, with the addition of brotli compression output # Borrowed from bazelbuild/rules_nodejs, with the addition of brotli compression output
def _plain_rollup_bundle(ctx): def _plain_rollup_bundle(ctx):
rollup_config = write_rollup_config(ctx) rollup_config = write_rollup_config(ctx)
run_rollup(ctx, collect_es2015_sources(ctx), rollup_config, ctx.outputs.build_es6) run_rollup(ctx, collect_es2015_sources(ctx), rollup_config, ctx.outputs.build_es6)
_run_tsc(ctx, ctx.outputs.build_es6, ctx.outputs.build_es5) _run_tsc(ctx, ctx.outputs.build_es6, ctx.outputs.build_es5)
source_map = run_uglify(ctx, ctx.outputs.build_es5, ctx.outputs.build_es5_min) source_map = 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) run_uglify(ctx, ctx.outputs.build_es5, ctx.outputs.build_es5_min_debug, debug = True)
umd_rollup_config = write_rollup_config(ctx, filename = "_%s_umd.rollup.conf.js", output_format = "umd") umd_rollup_config = write_rollup_config(ctx, filename = "_%s_umd.rollup.conf.js", output_format = "umd")
run_rollup(ctx, collect_es2015_sources(ctx), umd_rollup_config, ctx.outputs.build_umd) run_rollup(ctx, collect_es2015_sources(ctx), umd_rollup_config, ctx.outputs.build_umd)
run_sourcemapexplorer(ctx, ctx.outputs.build_es5_min, source_map, ctx.outputs.explore_html) run_sourcemapexplorer(ctx, ctx.outputs.build_es5_min, source_map, ctx.outputs.explore_html)
run_brotli(ctx, ctx.outputs.build_es5_min, ctx.outputs.build_es5_min_compressed) run_brotli(ctx, ctx.outputs.build_es5_min, ctx.outputs.build_es5_min_compressed)
files = [ctx.outputs.build_es5_min, source_map] files = [ctx.outputs.build_es5_min, source_map]
return DefaultInfo(files = depset(files), runfiles = ctx.runfiles(files)) return DefaultInfo(files = depset(files), runfiles = ctx.runfiles(files))
def _ng_rollup_bundle(ctx): def _ng_rollup_bundle(ctx):
# Escape and use the plain rollup rule if the compilation strategy requires it # Escape and use the plain rollup rule if the compilation strategy requires it
if _use_plain_rollup(ctx): if _use_plain_rollup(ctx):
return _plain_rollup_bundle(ctx) return _plain_rollup_bundle(ctx)
# We don't expect anyone to make use of this bundle yet, but it makes this rule # 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 # compatible with rollup_bundle which allows them to be easily swapped back and
# forth. # forth.
esm2015_rollup_config = write_rollup_config(ctx, filename = "_%s.rollup_es6.conf.js") 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) run_rollup(ctx, collect_es2015_sources(ctx), esm2015_rollup_config, ctx.outputs.build_es6)
esm5_sources = flatten_esm5(ctx) esm5_sources = flatten_esm5(ctx)
rollup_config = write_rollup_config(ctx, [BO_PLUGIN], "/".join([ctx.bin_dir.path, ctx.label.package, esm5_root_dir(ctx)])) rollup_config = write_rollup_config(ctx, [BO_PLUGIN], "/".join([ctx.bin_dir.path, ctx.label.package, esm5_root_dir(ctx)]))
rollup_sourcemap = run_rollup(ctx, esm5_sources, rollup_config, ctx.outputs.build_es5) rollup_sourcemap = run_rollup(ctx, esm5_sources, rollup_config, ctx.outputs.build_es5)
sourcemap = run_uglify(ctx, sourcemap = run_uglify(
ctx.outputs.build_es5, ctx,
ctx.outputs.build_es5_min, ctx.outputs.build_es5,
comments = False, ctx.outputs.build_es5_min,
in_source_map = rollup_sourcemap) comments = False,
run_uglify(ctx, in_source_map = rollup_sourcemap,
ctx.outputs.build_es5, )
ctx.outputs.build_es5_min_debug, run_uglify(
debug = True, comments = False) ctx,
ctx.outputs.build_es5,
ctx.outputs.build_es5_min_debug,
debug = True,
comments = False,
)
umd_rollup_config = write_rollup_config(ctx, filename = "_%s_umd.rollup.conf.js", output_format = "umd") umd_rollup_config = write_rollup_config(ctx, filename = "_%s_umd.rollup.conf.js", output_format = "umd")
run_rollup(ctx, collect_es2015_sources(ctx), umd_rollup_config, ctx.outputs.build_umd) run_rollup(ctx, collect_es2015_sources(ctx), umd_rollup_config, ctx.outputs.build_umd)
run_brotli(ctx, ctx.outputs.build_es5_min, ctx.outputs.build_es5_min_compressed) run_brotli(ctx, ctx.outputs.build_es5_min, ctx.outputs.build_es5_min_compressed)
run_sourcemapexplorer(ctx, ctx.outputs.build_es5_min, sourcemap, ctx.outputs.explore_html) run_sourcemapexplorer(ctx, ctx.outputs.build_es5_min, sourcemap, ctx.outputs.explore_html)
return DefaultInfo(files=depset([ctx.outputs.build_es5_min, sourcemap])) return DefaultInfo(files = depset([ctx.outputs.build_es5_min, sourcemap]))
ng_rollup_bundle = rule( ng_rollup_bundle = rule(
implementation = _ng_rollup_bundle, implementation = _ng_rollup_bundle,
@ -133,11 +140,13 @@ ng_rollup_bundle = rule(
"_rollup": attr.label( "_rollup": attr.label(
executable = True, executable = True,
cfg = "host", cfg = "host",
default = Label("@angular//packages/bazel/src:rollup_with_build_optimizer")), default = Label("@angular//packages/bazel/src:rollup_with_build_optimizer"),
),
"_brotli": attr.label( "_brotli": attr.label(
executable = True, executable = True,
cfg = "host", cfg = "host",
default = Label("@org_brotli//:brotli")), default = Label("@org_brotli//:brotli"),
),
}), }),
outputs = dict(ROLLUP_OUTPUTS, **{ outputs = dict(ROLLUP_OUTPUTS, **{
"build_es5_min_compressed": "%{name}.min.js.br", "build_es5_min_compressed": "%{name}.min.js.br",

View File

@ -5,9 +5,10 @@
"""Implementation of the protractor_web_test and protractor_web_test_suite rules. """Implementation of the protractor_web_test and protractor_web_test_suite rules.
""" """
load("@build_bazel_rules_nodejs//internal:node.bzl", load(
"sources_aspect", "@build_bazel_rules_nodejs//internal:node.bzl",
"expand_path_into_runfiles", "expand_path_into_runfiles",
"sources_aspect",
) )
load("@io_bazel_rules_webtesting//web:web.bzl", "web_test_suite") load("@io_bazel_rules_webtesting//web:web.bzl", "web_test_suite")
load("@io_bazel_rules_webtesting//web/internal:constants.bzl", "DEFAULT_WRAPPED_TEST_TAGS") load("@io_bazel_rules_webtesting//web/internal:constants.bzl", "DEFAULT_WRAPPED_TEST_TAGS")
@ -16,69 +17,71 @@ load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary")
_CONF_TMPL = "//packages/bazel/src/protractor:protractor.conf.js" _CONF_TMPL = "//packages/bazel/src/protractor:protractor.conf.js"
def _protractor_web_test_impl(ctx): def _protractor_web_test_impl(ctx):
configuration = ctx.actions.declare_file( configuration = ctx.actions.declare_file(
"%s.conf.js" % ctx.label.name, "%s.conf.js" % ctx.label.name,
sibling=ctx.outputs.executable) sibling = ctx.outputs.executable,
)
files = depset(ctx.files.srcs) files = depset(ctx.files.srcs)
for d in ctx.attr.deps: for d in ctx.attr.deps:
if hasattr(d, "node_sources"): if hasattr(d, "node_sources"):
files = depset(transitive = [files, d.node_sources]) files = depset(transitive = [files, d.node_sources])
elif hasattr(d, "files"): elif hasattr(d, "files"):
files = depset(transitive = [files, d.files]) files = depset(transitive = [files, d.files])
specs = [ specs = [
expand_path_into_runfiles(ctx, f.short_path) expand_path_into_runfiles(ctx, f.short_path)
for f in files for f in files
] ]
configuration_sources = [] configuration_sources = []
if ctx.file.configuration: if ctx.file.configuration:
configuration_sources = [ctx.file.configuration] configuration_sources = [ctx.file.configuration]
if hasattr(ctx.attr.configuration, "node_sources"): if hasattr(ctx.attr.configuration, "node_sources"):
configuration_sources = ctx.attr.configuration.node_sources.to_list() configuration_sources = ctx.attr.configuration.node_sources.to_list()
configuration_file = ctx.file.configuration configuration_file = ctx.file.configuration
if hasattr(ctx.attr.configuration, "typescript"): if hasattr(ctx.attr.configuration, "typescript"):
configuration_file = ctx.attr.configuration.typescript.es5_sources.to_list()[0] configuration_file = ctx.attr.configuration.typescript.es5_sources.to_list()[0]
on_prepare_sources = [] on_prepare_sources = []
if ctx.file.on_prepare: if ctx.file.on_prepare:
on_prepare_sources = [ctx.file.on_prepare] on_prepare_sources = [ctx.file.on_prepare]
if hasattr(ctx.attr.on_prepare, "node_sources"): if hasattr(ctx.attr.on_prepare, "node_sources"):
on_prepare_sources = ctx.attr.on_prepare.node_sources.to_list() on_prepare_sources = ctx.attr.on_prepare.node_sources.to_list()
on_prepare_file = ctx.file.on_prepare on_prepare_file = ctx.file.on_prepare
if hasattr(ctx.attr.on_prepare, "typescript"): if hasattr(ctx.attr.on_prepare, "typescript"):
on_prepare_file = ctx.attr.on_prepare.typescript.es5_sources.to_list()[0] on_prepare_file = ctx.attr.on_prepare.typescript.es5_sources.to_list()[0]
protractor_executable_path = ctx.executable.protractor.short_path protractor_executable_path = ctx.executable.protractor.short_path
if protractor_executable_path.startswith('..'): if protractor_executable_path.startswith(".."):
protractor_executable_path = "external" + protractor_executable_path[2:] protractor_executable_path = "external" + protractor_executable_path[2:]
server_executable_path = '' server_executable_path = ""
if ctx.executable.server: if ctx.executable.server:
server_executable_path = ctx.executable.server.short_path server_executable_path = ctx.executable.server.short_path
if server_executable_path.startswith('..'): if server_executable_path.startswith(".."):
server_executable_path = "external" + protractor_executable_path[2:] server_executable_path = "external" + protractor_executable_path[2:]
ctx.actions.expand_template( ctx.actions.expand_template(
output = configuration, output = configuration,
template = ctx.file._conf_tmpl, template = ctx.file._conf_tmpl,
substitutions = { substitutions = {
"TMPL_config": expand_path_into_runfiles(ctx, configuration_file.short_path) if configuration_file else "", "TMPL_config": expand_path_into_runfiles(ctx, configuration_file.short_path) if configuration_file else "",
"TMPL_on_prepare": expand_path_into_runfiles(ctx, on_prepare_file.short_path) if on_prepare_file else "", "TMPL_on_prepare": expand_path_into_runfiles(ctx, on_prepare_file.short_path) if on_prepare_file else "",
"TMPL_workspace": ctx.workspace_name, "TMPL_workspace": ctx.workspace_name,
"TMPL_server": server_executable_path, "TMPL_server": server_executable_path,
"TMPL_specs": "\n".join([" '%s'," % e for e in specs]), "TMPL_specs": "\n".join([" '%s'," % e for e in specs]),
}) },
)
runfiles = [configuration] + configuration_sources + on_prepare_sources runfiles = [configuration] + configuration_sources + on_prepare_sources
ctx.actions.write( ctx.actions.write(
output = ctx.outputs.executable, output = ctx.outputs.executable,
is_executable = True, is_executable = True,
content = """#!/usr/bin/env bash content = """#!/usr/bin/env bash
if [ -e "$RUNFILE_MANIFEST_FILE" ]; then if [ -e "$RUNFILE_MANIFEST_FILE" ]; then
while read line; do while read line; do
declare -a PARTS=($line) declare -a PARTS=($line)
@ -101,19 +104,22 @@ echo "Protractor $PROTRACTOR_VERSION"
# Run the protractor binary # Run the protractor binary
$PROTRACTOR $CONF $PROTRACTOR $CONF
""".format(TMPL_protractor = protractor_executable_path, """.format(
TMPL_conf = configuration.short_path)) TMPL_protractor = protractor_executable_path,
return [DefaultInfo( TMPL_conf = configuration.short_path,
files = depset([ctx.outputs.executable]), ),
runfiles = ctx.runfiles( )
files = runfiles, return [DefaultInfo(
transitive_files = files, files = depset([ctx.outputs.executable]),
# Propagate protractor_bin and its runfiles runfiles = ctx.runfiles(
collect_data = True, files = runfiles,
collect_default = True, transitive_files = files,
), # Propagate protractor_bin and its runfiles
executable = ctx.outputs.executable, collect_data = True,
)] collect_default = True,
),
executable = ctx.outputs.executable,
)]
_protractor_web_test = rule( _protractor_web_test = rule(
implementation = _protractor_web_test_impl, implementation = _protractor_web_test_impl,
@ -124,36 +130,43 @@ _protractor_web_test = rule(
doc = "Protractor configuration file", doc = "Protractor configuration file",
allow_single_file = True, allow_single_file = True,
cfg = "data", cfg = "data",
aspects = [sources_aspect]), aspects = [sources_aspect],
),
"srcs": attr.label_list( "srcs": attr.label_list(
doc = "A list of JavaScript test files", doc = "A list of JavaScript test files",
allow_files = [".js"]), allow_files = [".js"],
),
"on_prepare": attr.label( "on_prepare": attr.label(
doc = """A file with a node.js script to run once before all tests run. doc = """A file with a node.js script to run once before all tests run.
If the script exports a function which returns a promise, protractor If the script exports a function which returns a promise, protractor
will wait for the promise to resolve before beginning tests.""", will wait for the promise to resolve before beginning tests.""",
allow_single_file = True, allow_single_file = True,
cfg = "data", cfg = "data",
aspects = [sources_aspect]), aspects = [sources_aspect],
),
"deps": attr.label_list( "deps": attr.label_list(
doc = "Other targets which produce JavaScript such as `ts_library`", doc = "Other targets which produce JavaScript such as `ts_library`",
allow_files = True, allow_files = True,
aspects = [sources_aspect]), aspects = [sources_aspect],
),
"data": attr.label_list( "data": attr.label_list(
doc = "Runtime dependencies", doc = "Runtime dependencies",
cfg = "data"), cfg = "data",
),
"server": attr.label( "server": attr.label(
doc = "Optional server executable target", doc = "Optional server executable target",
executable = True, executable = True,
cfg = "data", cfg = "data",
single_file = False, single_file = False,
allow_files = True), allow_files = True,
),
"protractor": attr.label( "protractor": attr.label(
doc = "Protractor executable target (set by protractor_web_test macro)", doc = "Protractor executable target (set by protractor_web_test macro)",
executable = True, executable = True,
cfg = "data", cfg = "data",
single_file = False, single_file = False,
allow_files = True), allow_files = True,
),
"_conf_tmpl": attr.label( "_conf_tmpl": attr.label(
default = Label(_CONF_TMPL), default = Label(_CONF_TMPL),
allow_single_file = True, allow_single_file = True,
@ -162,180 +175,184 @@ _protractor_web_test = rule(
) )
def protractor_web_test( def protractor_web_test(
name, name,
configuration = None, configuration = None,
on_prepare = None, on_prepare = None,
srcs = [], srcs = [],
deps = [], deps = [],
data = [], data = [],
server = None, server = None,
tags = [], tags = [],
**kwargs): **kwargs):
"""Runs a protractor test in a browser. """Runs a protractor test in a browser.
Args: Args:
name: The name of the test name: The name of the test
configuration: Protractor configuration file. configuration: Protractor configuration file.
on_prepare: A file with a node.js script to run once before all tests run. on_prepare: A file with a node.js script to run once before all tests run.
If the script exports a function which returns a promise, protractor If the script exports a function which returns a promise, protractor
will wait for the promise to resolve before beginning tests. will wait for the promise to resolve before beginning tests.
srcs: JavaScript source files srcs: JavaScript source files
deps: Other targets which produce JavaScript such as `ts_library` deps: Other targets which produce JavaScript such as `ts_library`
data: Runtime dependencies data: Runtime dependencies
server: Optional server executable target server: Optional server executable target
tags: Standard Bazel tags, this macro adds one for ibazel tags: Standard Bazel tags, this macro adds one for ibazel
**kwargs: passed through to `_protractor_web_test` **kwargs: passed through to `_protractor_web_test`
""" """
protractor_bin_name = name + "_protractor_bin" protractor_bin_name = name + "_protractor_bin"
nodejs_binary( nodejs_binary(
name = protractor_bin_name, name = protractor_bin_name,
entry_point = "protractor/bin/protractor", entry_point = "protractor/bin/protractor",
data = srcs + deps + data, data = srcs + deps + data,
node_modules = "@//:node_modules", node_modules = "@//:node_modules",
testonly = 1, testonly = 1,
visibility = ["//visibility:private"], visibility = ["//visibility:private"],
) )
# Our binary dependency must be in data[] for collect_data to pick it up # Our binary dependency must be in data[] for collect_data to pick it up
# FIXME: maybe we can just ask :protractor_bin_name for its runfiles attr # FIXME: maybe we can just ask :protractor_bin_name for its runfiles attr
web_test_data = data + [":" + protractor_bin_name] web_test_data = data + [":" + protractor_bin_name]
if server: if server:
web_test_data += [server] web_test_data += [server]
_protractor_web_test( _protractor_web_test(
name = name, name = name,
configuration = configuration, configuration = configuration,
on_prepare=on_prepare, on_prepare = on_prepare,
srcs = srcs, srcs = srcs,
deps = deps, deps = deps,
data = web_test_data, data = web_test_data,
server = server, server = server,
protractor = protractor_bin_name, protractor = protractor_bin_name,
tags = tags + [ tags = tags + [
# Users don't need to know that this tag is required to run under ibazel # Users don't need to know that this tag is required to run under ibazel
"ibazel_notify_changes", "ibazel_notify_changes",
], ],
**kwargs) **kwargs
)
def protractor_web_test_suite( def protractor_web_test_suite(
name, name,
configuration = None, configuration = None,
on_prepare = None, on_prepare = None,
srcs = [], srcs = [],
deps = [], deps = [],
data = [], data = [],
server = None, server = None,
browsers=["@io_bazel_rules_webtesting//browsers:chromium-local"], browsers = ["@io_bazel_rules_webtesting//browsers:chromium-local"],
args=None, args = None,
browser_overrides=None, browser_overrides = None,
config=None, config = None,
flaky=None, flaky = None,
local=None, local = None,
shard_count=None, shard_count = None,
size=None, size = None,
tags = [], tags = [],
test_suite_tags=None, test_suite_tags = None,
timeout=None, timeout = None,
visibility=None, visibility = None,
web_test_data=[], web_test_data = [],
wrapped_test_tags=None, wrapped_test_tags = None,
**remaining_keyword_args): **remaining_keyword_args):
"""Defines a test_suite of web_test targets that wrap a protractor_web_test target. """Defines a test_suite of web_test targets that wrap a protractor_web_test target.
Args: Args:
name: The base name of the test. name: The base name of the test.
configuration: Protractor configuration file. configuration: Protractor configuration file.
on_prepare: A file with a node.js script to run once before all tests run. on_prepare: A file with a node.js script to run once before all tests run.
If the script exports a function which returns a promise, protractor If the script exports a function which returns a promise, protractor
will wait for the promise to resolve before beginning tests. will wait for the promise to resolve before beginning tests.
srcs: JavaScript source files srcs: JavaScript source files
deps: Other targets which produce JavaScript such as `ts_library` deps: Other targets which produce JavaScript such as `ts_library`
data: Runtime dependencies data: Runtime dependencies
server: Optional server executable target server: Optional server executable target
browsers: A sequence of labels specifying the browsers to use. browsers: A sequence of labels specifying the browsers to use.
args: Args for web_test targets generated by this extension. args: Args for web_test targets generated by this extension.
browser_overrides: Dictionary; optional; default is an empty dictionary. A browser_overrides: Dictionary; optional; default is an empty dictionary. A
dictionary mapping from browser names to browser-specific web_test dictionary mapping from browser names to browser-specific web_test
attributes, such as shard_count, flakiness, timeout, etc. For example: attributes, such as shard_count, flakiness, timeout, etc. For example:
{'//browsers:chrome-native': {'shard_count': 3, 'flaky': 1} {'//browsers:chrome-native': {'shard_count': 3, 'flaky': 1}
'//browsers:firefox-native': {'shard_count': 1, 'timeout': 100}}. '//browsers:firefox-native': {'shard_count': 1, 'timeout': 100}}.
config: Label; optional; Configuration of web test features. config: Label; optional; Configuration of web test features.
flaky: A boolean specifying that the test is flaky. If set, the test will flaky: A boolean specifying that the test is flaky. If set, the test will
be retried up to 3 times (default: 0) be retried up to 3 times (default: 0)
local: boolean; optional. local: boolean; optional.
shard_count: The number of test shards to use per browser. (default: 1) shard_count: The number of test shards to use per browser. (default: 1)
size: A string specifying the test size. (default: 'large') size: A string specifying the test size. (default: 'large')
tags: A list of test tag strings to apply to each generated web_test target. tags: A list of test tag strings to apply to each generated web_test target.
This macro adds a couple for ibazel. This macro adds a couple for ibazel.
test_suite_tags: A list of tag strings for the generated test_suite. test_suite_tags: A list of tag strings for the generated test_suite.
timeout: A string specifying the test timeout (default: computed from size) timeout: A string specifying the test timeout (default: computed from size)
visibility: List of labels; optional. visibility: List of labels; optional.
web_test_data: Data dependencies for the web_test. web_test_data: Data dependencies for the web_test.
wrapped_test_tags: A list of test tag strings to use for the wrapped test wrapped_test_tags: A list of test tag strings to use for the wrapped test
**remaining_keyword_args: Arguments for the wrapped test target. **remaining_keyword_args: Arguments for the wrapped test target.
""" """
# Check explicitly for None so that users can set this to the empty list
if wrapped_test_tags == None:
wrapped_test_tags = DEFAULT_WRAPPED_TEST_TAGS
size = size or "large" # Check explicitly for None so that users can set this to the empty list
if wrapped_test_tags == None:
wrapped_test_tags = DEFAULT_WRAPPED_TEST_TAGS
wrapped_test_name = name + "_wrapped_test" size = size or "large"
protractor_bin_name = name + "_protractor_bin"
# Users don't need to know that this tag is required to run under ibazel wrapped_test_name = name + "_wrapped_test"
tags = tags + ["ibazel_notify_changes"] protractor_bin_name = name + "_protractor_bin"
nodejs_binary( # Users don't need to know that this tag is required to run under ibazel
name = protractor_bin_name, tags = tags + ["ibazel_notify_changes"]
entry_point = "protractor/bin/protractor",
data = srcs + deps + data,
node_modules = "@//:node_modules",
testonly = 1,
visibility = ["//visibility:private"],
)
# Our binary dependency must be in data[] for collect_data to pick it up nodejs_binary(
# FIXME: maybe we can just ask the :protractor_bin_name for its runfiles attr name = protractor_bin_name,
web_test_data = web_test_data + [":" + protractor_bin_name] entry_point = "protractor/bin/protractor",
if server: data = srcs + deps + data,
web_test_data += [server] node_modules = "@//:node_modules",
testonly = 1,
visibility = ["//visibility:private"],
)
_protractor_web_test( # Our binary dependency must be in data[] for collect_data to pick it up
name=wrapped_test_name, # FIXME: maybe we can just ask the :protractor_bin_name for its runfiles attr
configuration=configuration, web_test_data = web_test_data + [":" + protractor_bin_name]
on_prepare=on_prepare, if server:
srcs=srcs, web_test_data += [server]
deps=deps,
data=web_test_data,
server=server,
protractor=protractor_bin_name,
args=args,
flaky=flaky,
local=local,
shard_count=shard_count,
size=size,
tags=wrapped_test_tags,
timeout=timeout,
visibility=["//visibility:private"],
**remaining_keyword_args)
web_test_suite( _protractor_web_test(
name=name, name = wrapped_test_name,
launcher=":"+wrapped_test_name, configuration = configuration,
args=args, on_prepare = on_prepare,
browsers=browsers, srcs = srcs,
browser_overrides=browser_overrides, deps = deps,
config=config, data = web_test_data,
data=web_test_data, server = server,
flaky=flaky, protractor = protractor_bin_name,
local=local, args = args,
shard_count=shard_count, flaky = flaky,
size=size, local = local,
tags=tags, shard_count = shard_count,
test=wrapped_test_name, size = size,
test_suite_tags=test_suite_tags, tags = wrapped_test_tags,
timeout=timeout, timeout = timeout,
visibility=visibility) visibility = ["//visibility:private"],
**remaining_keyword_args
)
web_test_suite(
name = name,
launcher = ":" + wrapped_test_name,
args = args,
browsers = browsers,
browser_overrides = browser_overrides,
config = config,
data = web_test_data,
flaky = flaky,
local = local,
shard_count = shard_count,
size = size,
tags = tags,
test = wrapped_test_name,
test_suite_tags = test_suite_tags,
timeout = timeout,
visibility = visibility,
)

View File

@ -1,19 +1,20 @@
"""Allows different paths for these imports in google3. """Allows different paths for these imports in google3.
""" """
load("@build_bazel_rules_typescript//internal:build_defs.bzl", load(
"@build_bazel_rules_typescript//internal:build_defs.bzl",
_tsc_wrapped_tsconfig = "tsc_wrapped_tsconfig", _tsc_wrapped_tsconfig = "tsc_wrapped_tsconfig",
) )
load(
load("@build_bazel_rules_typescript//internal:common/compilation.bzl", "@build_bazel_rules_typescript//internal:common/compilation.bzl",
_COMMON_ATTRIBUTES = "COMMON_ATTRIBUTES", _COMMON_ATTRIBUTES = "COMMON_ATTRIBUTES",
_COMMON_OUTPUTS = "COMMON_OUTPUTS", _COMMON_OUTPUTS = "COMMON_OUTPUTS",
_compile_ts = "compile_ts",
_DEPS_ASPECTS = "DEPS_ASPECTS", _DEPS_ASPECTS = "DEPS_ASPECTS",
_compile_ts = "compile_ts",
_ts_providers_dict_to_struct = "ts_providers_dict_to_struct", _ts_providers_dict_to_struct = "ts_providers_dict_to_struct",
) )
load(
load("@build_bazel_rules_typescript//internal:common/json_marshal.bzl", "@build_bazel_rules_typescript//internal:common/json_marshal.bzl",
_json_marshal = "json_marshal", _json_marshal = "json_marshal",
) )

View File

@ -6,16 +6,16 @@
""" """
def _extract_flat_module_index(ctx): def _extract_flat_module_index(ctx):
files = [] files = []
for dep in ctx.attr.deps: for dep in ctx.attr.deps:
if hasattr(dep, "angular"): if hasattr(dep, "angular"):
metadata = dep.angular.flat_module_metadata metadata = dep.angular.flat_module_metadata
files.extend([metadata.metadata_file, metadata.typings_file]) files.extend([metadata.metadata_file, metadata.typings_file])
return [DefaultInfo(files = depset(files))] return [DefaultInfo(files = depset(files))]
extract_flat_module_index = rule( extract_flat_module_index = rule(
implementation = _extract_flat_module_index, implementation = _extract_flat_module_index,
attrs = { attrs = {
"deps": attr.label_list(), "deps": attr.label_list(),
}, },
) )

View File

@ -9,14 +9,15 @@ This allows editors and other tools to easily use the language service bundle
without having to provide all of the angular specific peer dependencies. without having to provide all of the angular specific peer dependencies.
""" """
load("@build_bazel_rules_nodejs//internal/rollup:rollup_bundle.bzl", load(
"@build_bazel_rules_nodejs//internal/rollup:rollup_bundle.bzl",
"ROLLUP_ATTRS", "ROLLUP_ATTRS",
"rollup_module_mappings_aspect", "rollup_module_mappings_aspect",
"write_rollup_config",
"run_rollup", "run_rollup",
"run_uglify" "run_uglify",
"write_rollup_config",
) )
load("//packages/bazel/src:esm5.bzl", "esm5_outputs_aspect", "flatten_esm5", "esm5_root_dir") load("//packages/bazel/src:esm5.bzl", "esm5_outputs_aspect", "esm5_root_dir", "flatten_esm5")
# Note: the file is called "umd.js" and "umd.min.js" because of historical # Note: the file is called "umd.js" and "umd.min.js" because of historical
# reasons. The format is actually amd and not umd, but we are afraid to rename # reasons. The format is actually amd and not umd, but we are afraid to rename
@ -28,13 +29,15 @@ _ROLLUP_OUTPUTS = {
} }
def _ls_rollup_bundle(ctx): def _ls_rollup_bundle(ctx):
esm5_sources = flatten_esm5(ctx) esm5_sources = flatten_esm5(ctx)
rollup_config = write_rollup_config(ctx, rollup_config = write_rollup_config(
root_dir = "/".join([ctx.bin_dir.path, ctx.label.package, esm5_root_dir(ctx)]), ctx,
output_format = "amd") root_dir = "/".join([ctx.bin_dir.path, ctx.label.package, esm5_root_dir(ctx)]),
run_rollup(ctx, esm5_sources, rollup_config, ctx.outputs.build_umd) output_format = "amd",
source_map = run_uglify(ctx, ctx.outputs.build_umd, ctx.outputs.build_umd_min) )
return DefaultInfo(files=depset([ctx.outputs.build_umd, ctx.outputs.build_umd_min, source_map])) run_rollup(ctx, esm5_sources, rollup_config, ctx.outputs.build_umd)
source_map = run_uglify(ctx, ctx.outputs.build_umd, ctx.outputs.build_umd_min)
return DefaultInfo(files = depset([ctx.outputs.build_umd, ctx.outputs.build_umd_min, source_map]))
ls_rollup_bundle = rule( ls_rollup_bundle = rule(
implementation = _ls_rollup_bundle, implementation = _ls_rollup_bundle,
@ -45,4 +48,4 @@ ls_rollup_bundle = rule(
]), ]),
}), }),
outputs = _ROLLUP_OUTPUTS, outputs = _ROLLUP_OUTPUTS,
) )

View File

@ -12,65 +12,65 @@ DEFAULT_NODE_MODULES = "@angular_deps//:node_modules"
# Packages which are versioned together on npm # Packages which are versioned together on npm
ANGULAR_SCOPED_PACKAGES = ["@angular/%s" % p for p in [ ANGULAR_SCOPED_PACKAGES = ["@angular/%s" % p for p in [
# core should be the first package because it's the main package in the group # core should be the first package because it's the main package in the group
# this is significant for Angular CLI and "ng update" specifically, @angular/core # this is significant for Angular CLI and "ng update" specifically, @angular/core
# is considered the identifier of the group by these tools. # is considered the identifier of the group by these tools.
"core", "core",
"bazel", "bazel",
"common", "common",
"compiler", "compiler",
"compiler-cli", "compiler-cli",
"animations", "animations",
"elements", "elements",
"platform-browser", "platform-browser",
"platform-browser-dynamic", "platform-browser-dynamic",
"forms", "forms",
"http", "http",
"platform-server", "platform-server",
"platform-webworker", "platform-webworker",
"platform-webworker-dynamic", "platform-webworker-dynamic",
"upgrade", "upgrade",
"router", "router",
"language-service", "language-service",
"service-worker", "service-worker",
]] ]]
PKG_GROUP_REPLACEMENTS = { PKG_GROUP_REPLACEMENTS = {
"\"NG_UPDATE_PACKAGE_GROUP\"": """[ "\"NG_UPDATE_PACKAGE_GROUP\"": """[
%s %s
]""" % ",\n ".join(["\"%s\"" % s for s in ANGULAR_SCOPED_PACKAGES]) ]""" % ",\n ".join(["\"%s\"" % s for s in ANGULAR_SCOPED_PACKAGES]),
} }
def ts_library(tsconfig = None, node_modules = DEFAULT_NODE_MODULES, testonly = False, **kwargs): def ts_library(tsconfig = None, node_modules = DEFAULT_NODE_MODULES, testonly = False, **kwargs):
if not tsconfig: if not tsconfig:
if testonly: if testonly:
tsconfig = DEFAULT_TSCONFIG_TEST tsconfig = DEFAULT_TSCONFIG_TEST
else: else:
tsconfig = DEFAULT_TSCONFIG_BUILD tsconfig = DEFAULT_TSCONFIG_BUILD
_ts_library(tsconfig = tsconfig, node_modules = node_modules, testonly = testonly, **kwargs) _ts_library(tsconfig = tsconfig, node_modules = node_modules, testonly = testonly, **kwargs)
def ng_module(name, tsconfig = None, entry_point = None, node_modules = DEFAULT_NODE_MODULES, testonly = False, **kwargs): def ng_module(name, tsconfig = None, entry_point = None, node_modules = DEFAULT_NODE_MODULES, testonly = False, **kwargs):
if not tsconfig: if not tsconfig:
if testonly: if testonly:
tsconfig = DEFAULT_TSCONFIG_TEST tsconfig = DEFAULT_TSCONFIG_TEST
else: else:
tsconfig = DEFAULT_TSCONFIG_BUILD tsconfig = DEFAULT_TSCONFIG_BUILD
if not entry_point: if not entry_point:
entry_point = "public_api.ts" entry_point = "public_api.ts"
_ng_module(name = name, flat_module_out_file = name, tsconfig = tsconfig, entry_point = entry_point, node_modules = node_modules, testonly = testonly, **kwargs) _ng_module(name = name, flat_module_out_file = name, tsconfig = tsconfig, entry_point = entry_point, node_modules = node_modules, testonly = testonly, **kwargs)
# ivy_ng_module behaves like ng_module, and under --define=compile=legacy it runs ngc with global # ivy_ng_module behaves like ng_module, and under --define=compile=legacy it runs ngc with global
# analysis but produces Ivy outputs. Under other compile modes, it behaves as ng_module. # analysis but produces Ivy outputs. Under other compile modes, it behaves as ng_module.
# TODO(alxhub): remove when ngtsc supports the same use cases. # TODO(alxhub): remove when ngtsc supports the same use cases.
def ivy_ng_module(name, tsconfig = None, entry_point = None, testonly = False, **kwargs): def ivy_ng_module(name, tsconfig = None, entry_point = None, testonly = False, **kwargs):
if not tsconfig: if not tsconfig:
if testonly: if testonly:
tsconfig = DEFAULT_TSCONFIG_TEST tsconfig = DEFAULT_TSCONFIG_TEST
else: else:
tsconfig = DEFAULT_TSCONFIG_BUILD tsconfig = DEFAULT_TSCONFIG_BUILD
if not entry_point: if not entry_point:
entry_point = "public_api.ts" entry_point = "public_api.ts"
_internal_global_ng_module(name = name, flat_module_out_file = name, tsconfig = tsconfig, entry_point = entry_point, testonly = testonly, **kwargs) _internal_global_ng_module(name = name, flat_module_out_file = name, tsconfig = tsconfig, entry_point = entry_point, testonly = testonly, **kwargs)
def ng_package(name, node_modules = DEFAULT_NODE_MODULES, readme_md = None, license_banner = None, **kwargs): def ng_package(name, node_modules = DEFAULT_NODE_MODULES, readme_md = None, license_banner = None, **kwargs):
if not readme_md: if not readme_md:
@ -88,18 +88,19 @@ def ng_package(name, node_modules = DEFAULT_NODE_MODULES, readme_md = None, lice
) )
def npm_package(name, replacements = {}, **kwargs): def npm_package(name, replacements = {}, **kwargs):
_npm_package( _npm_package(
name = name, name = name,
replacements = dict(replacements, **PKG_GROUP_REPLACEMENTS), replacements = dict(replacements, **PKG_GROUP_REPLACEMENTS),
**kwargs) **kwargs
)
def ts_web_test_suite(bootstrap = [], deps = [], **kwargs): def ts_web_test_suite(bootstrap = [], deps = [], **kwargs):
if not bootstrap: if not bootstrap:
bootstrap = ["//:web_test_bootstrap_scripts"] bootstrap = ["//:web_test_bootstrap_scripts"]
local_deps = [ local_deps = [
"@angular_deps//:node_modules/tslib/tslib.js", "@angular_deps//:node_modules/tslib/tslib.js",
"//tools/testing:browser", "//tools/testing:browser",
] + deps ] + deps
_ts_web_test_suite( _ts_web_test_suite(
bootstrap = bootstrap, bootstrap = bootstrap,

View File

@ -2,17 +2,19 @@
See https://www.npmjs.com/package/http-server See https://www.npmjs.com/package/http-server
""" """
load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary") load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary")
def http_server(templated_args = [], **kwargs): def http_server(templated_args = [], **kwargs):
# By default, we pass an argument pointing the http server to the # By default, we pass an argument pointing the http server to the
# package of the caller. # package of the caller.
# This assumes there is an index.html in the package directory. # This assumes there is an index.html in the package directory.
if not templated_args: if not templated_args:
templated_args = [native.package_name()] templated_args = [native.package_name()]
nodejs_binary( nodejs_binary(
node_modules = "@http-server_runtime_deps//:node_modules", node_modules = "@http-server_runtime_deps//:node_modules",
entry_point = "http-server/bin/http-server", entry_point = "http-server/bin/http-server",
templated_args = templated_args, templated_args = templated_args,
**kwargs) **kwargs
)

View File

@ -9,7 +9,7 @@ load("@build_bazel_rules_nodejs//:defs.bzl", "yarn_install")
load("@angular//packages/bazel/src:ng_setup_workspace.bzl", _ng_setup_workspace = "ng_setup_workspace") load("@angular//packages/bazel/src:ng_setup_workspace.bzl", _ng_setup_workspace = "ng_setup_workspace")
def ng_setup_workspace(): def ng_setup_workspace():
"""This repository rule should be called from your WORKSPACE file. """This repository rule should be called from your WORKSPACE file.
It creates some additional Bazel external repositories that are used internally It creates some additional Bazel external repositories that are used internally
to build angular to build angular
@ -235,6 +235,7 @@ filegroup(
"node_modules/protractor/**", "node_modules/protractor/**",
"node_modules/@schematics/angular/**", "node_modules/@schematics/angular/**",
])) ]))
""") """,
)
_ng_setup_workspace() _ng_setup_workspace()

View File

@ -8,7 +8,7 @@
# This does a deep import under //internal because of not wanting the wrapper macro # This does a deep import under //internal because of not wanting the wrapper macro
# because it introduces an extra target_bin target. # because it introduces an extra target_bin target.
load("@build_bazel_rules_nodejs//internal/node:node.bzl", "nodejs_test", "nodejs_binary") load("@build_bazel_rules_nodejs//internal/node:node.bzl", "nodejs_binary", "nodejs_test")
DEFAULT_NODE_MODULES = "@angular_deps//:node_modules" DEFAULT_NODE_MODULES = "@angular_deps//:node_modules"

View File

@ -14,44 +14,46 @@
"""Runs ts_api_guardian """Runs ts_api_guardian
""" """
load("@build_bazel_rules_nodejs//internal/node:node.bzl", "nodejs_test", "nodejs_binary")
load("@build_bazel_rules_nodejs//internal/node:node.bzl", "nodejs_binary", "nodejs_test")
COMMON_MODULE_IDENTIFIERS = ["angular", "jasmine", "protractor"] COMMON_MODULE_IDENTIFIERS = ["angular", "jasmine", "protractor"]
def ts_api_guardian_test(name, golden, actual, data = [], **kwargs): def ts_api_guardian_test(name, golden, actual, data = [], **kwargs):
"""Runs ts_api_guardian """Runs ts_api_guardian
""" """
data += [ data += [
"//tools/ts-api-guardian:lib", "//tools/ts-api-guardian:lib",
"//tools/ts-api-guardian:bin/ts-api-guardian", "//tools/ts-api-guardian:bin/ts-api-guardian",
"@bazel_tools//tools/bash/runfiles", "@bazel_tools//tools/bash/runfiles",
] ]
args = [ args = [
# Needed so that node doesn't walk back to the source directory. # Needed so that node doesn't walk back to the source directory.
# From there, the relative imports would point to .ts files. # From there, the relative imports would point to .ts files.
"--node_options=--preserve-symlinks", "--node_options=--preserve-symlinks",
"--stripExportPattern", "^\(__\\)", "--stripExportPattern",
] "^\(__\\)",
for i in COMMON_MODULE_IDENTIFIERS: ]
args += ["--allowModuleIdentifiers", i] for i in COMMON_MODULE_IDENTIFIERS:
args += ["--allowModuleIdentifiers", i]
nodejs_test( nodejs_test(
name = name, name = name,
data = data, data = data,
node_modules = "@ts-api-guardian_runtime_deps//:node_modules", node_modules = "@ts-api-guardian_runtime_deps//:node_modules",
entry_point = "angular/tools/ts-api-guardian/bin/ts-api-guardian", entry_point = "angular/tools/ts-api-guardian/bin/ts-api-guardian",
templated_args = args + ["--verify", golden, actual], templated_args = args + ["--verify", golden, actual],
testonly = 1, testonly = 1,
**kwargs **kwargs
) )
nodejs_binary( nodejs_binary(
name = name + ".accept", name = name + ".accept",
testonly = True, testonly = True,
data = data, data = data,
node_modules = "@ts-api-guardian_runtime_deps//:node_modules", node_modules = "@ts-api-guardian_runtime_deps//:node_modules",
entry_point = "angular/tools/ts-api-guardian/bin/ts-api-guardian", entry_point = "angular/tools/ts-api-guardian/bin/ts-api-guardian",
templated_args = args + ["--out", golden, actual], templated_args = args + ["--out", golden, actual],
**kwargs **kwargs
) )