Skip to content

Commit

Permalink
feat(bazel): allow for linker mappings and external NPM package extra…
Browse files Browse the repository at this point in the history
…ction in extract rule

Updates the extract JS module output macro to support:

* The extraction of external NPM package sources
* The forwarding of linker mappings of the specified dependencies.

Both of these things would be needed for extracting full-devmode sources
in a way so that they can be fed into e.g. `esbuild` with proper module
resolution. This is done in `angular/components` for example to speed up
local development as devmode output is already ESM and ESBuild would by
default request both devmoe + prodmode output.

In general this rule is designed to help with the current situation of
still building devmode + prodmode. In the future we can fully remove
this rule if there is only a single output flavor using ESM!.
  • Loading branch information
devversion committed Nov 16, 2021
1 parent e67feed commit c0901db
Showing 1 changed file with 38 additions and 2 deletions.
40 changes: 38 additions & 2 deletions bazel/extract_js_module_output.bzl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load("@build_bazel_rules_nodejs//:providers.bzl", "DeclarationInfo", "JSEcmaScriptModuleInfo", "JSModuleInfo", "JSNamedModuleInfo")
load("@build_bazel_rules_nodejs//:providers.bzl", "DeclarationInfo", "ExternalNpmPackageInfo", "JSEcmaScriptModuleInfo", "JSModuleInfo", "JSNamedModuleInfo", "node_modules_aspect")
load("@build_bazel_rules_nodejs//internal/linker:link_node_modules.bzl", "LinkerPackageMappingInfo", "module_mappings_aspect")

"""Converts a provider name to its actually Starlark provider instance."""

Expand All @@ -15,13 +16,25 @@ def _name_to_js_module_provider(name):

def _extract_js_module_output_impl(ctx):
js_module_provider = _name_to_js_module_provider(ctx.attr.provider)
mappings = {}
depsets = []

for dep in ctx.attr.deps:
# Include JavaScript sources (including transitive outputs) based on the
# configured JavaScript module provider.
if js_module_provider in dep:
depsets.append(dep[js_module_provider].sources)

# If external NPM package sources should be included, we also capture the
# sources (including transitive external NPM packages).
if ctx.attr.include_external_npm_packages and ExternalNpmPackageInfo in dep:
depsets.append(dep[ExternalNpmPackageInfo].sources)

# If we intend to forward linker mappings, capture first-party package
# linker mappings and collect them in a dictionary that we will re-expose.
if ctx.attr.forward_linker_mappings and LinkerPackageMappingInfo in dep:
mappings.update(dep[LinkerPackageMappingInfo].mappings)

# Based on whether declarations should be collected, extract direct
# and transitive declaration files using the `DeclarationInfo` provider.
if ctx.attr.include_declarations and DeclarationInfo in dep:
Expand All @@ -37,7 +50,10 @@ def _extract_js_module_output_impl(ctx):

sources = depset(transitive = depsets)

return [DefaultInfo(files = sources)]
return [
DefaultInfo(files = sources),
LinkerPackageMappingInfo(mappings = mappings),
]

"""
Rule that collects declared JavaScript module output files from a list of dependencies
Expand All @@ -57,12 +73,32 @@ extract_js_module_output = rule(
attrs = {
"deps": attr.label_list(
allow_files = True,
aspects = [module_mappings_aspect, node_modules_aspect],
),
"provider": attr.string(
doc = "JavaScript module info provider that is used for collecting sources from the dependencies.",
mandatory = True,
values = ["JSModuleInfo", "JSNamedModuleInfo", "JSEcmaScriptModuleInfo"],
),
"forward_linker_mappings": attr.bool(
mandatory = True,
doc = """
Whether linker mappings of the dependencies should be forwarded.
This can be useful when the sources are put into JavaScript consuming rules
where module mappings are relevant. e.g. when passed as `deps` to `esbuild`.
""",
),
"include_external_npm_packages": attr.bool(
mandatory = True,
doc = """
Whether external NPM package sources should be extracted as well.
NPM packages can contain arbitrary sources that would be included. e.g. a
package could also contain `.json` files that would be extracted here. Also
note that there is no separation of the JS-based providers for external packages,
""",
),
"include_declarations": attr.bool(
mandatory = True,
doc = "Whether declaration files should be collected from the dependencies.",
Expand Down

0 comments on commit c0901db

Please sign in to comment.