Skip to content

Commit

Permalink
Updates prerender_component() to accept *.js prerender sources an…
Browse files Browse the repository at this point in the history
…d a `js_library()` as a `scripts` dependency.

Refs #39.

This chooses to generate either a `ts_library()` or `js_library()` based on the sources provided to `prerender_component()`. Whatever is given is re-exported via `_js_reexport()` which propagates both `JSModuleInfo` and `JSEcmaScriptModuleInfo`. It sounds like we could use a `ts_library()` or `js_library()` re-export. However, we need to re-export *only* the `scripts` parameter but we must also depend upon component deps' scripts. So we need to re-export some targets as direct sources and other targets as transitive sources, which doesn't appear to be supported by either rule. As a reuslt, `_js_reexport()` is needed to fill this specific niche.

This required an awkward change to support Rollup. `rollup_bundle()` *requires* a label-reference to its entry point. It explicitly rejects a target that returns multiple files. We also need to use ES6 sources (`JSEcmaScriptModuleInfo` so ensure we don't accidentally use UMD sources in Rollup. Unfortunately, `JSEcmaScriptModuleInfo` actually provides two generated files for any one source file from `ts_library()` (a `foo.mjs` file and another `foo.externs.js` file). This means that Rollup *cannot* have an entry point dependency on a `ts_library()` target, because it will always generate two ES6 source files. Normally, you would get around this by having a direct reference to the `:foo.ts` file, which Rollup has special code to translate to the actual generated `foo.mjs` file. To work around this I had to update the generated `.ts` entrypoint to a `.js` entry point, and actually export the file itself beyond the `js_library()` it is included in. That way `rollup_bundle()` can have a label reference to the `.js` entry point without going through a `js_library()` or `ts_library()` indirection that would confuse it.
  • Loading branch information
dgp1130 committed Aug 29, 2021
1 parent b61b29b commit 171cac8
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 24 deletions.
2 changes: 1 addition & 1 deletion examples/custom_bundling/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ ts_library(
# Manually bundle all scripts.
rollup_bundle(
name = "bundle",
entry_point = ":page_scripts.ts",
entry_point = ":page_scripts.js",
config_file = "rollup.config.js",
link_workspace_root = True,
silent = True,
Expand Down
2 changes: 2 additions & 0 deletions packages/rules_prerender/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ bzl_library(
deps = [
":web_resources",
"//common:label",
"//common:paths",
],
)

Expand Down Expand Up @@ -96,6 +97,7 @@ bzl_library(
visibility = ["//:__pkg__"],
deps = [
":web_resources",
"//common:paths",
"//packages/renderer:build_vars",
],
)
Expand Down
119 changes: 104 additions & 15 deletions packages/rules_prerender/prerender_component.bzl
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
"""Defines `prerender_component()` functionality."""

load("@build_bazel_rules_nodejs//:index.bzl", "js_library")
load(
"@build_bazel_rules_nodejs//:providers.bzl",
"JSModuleInfo",
"JSEcmaScriptModuleInfo",
)
load("@npm//@bazel/typescript:index.bzl", "ts_library")
load("//common:label.bzl", "absolute")
load("//common:paths.bzl", "is_js_file")
load(":web_resources.bzl", "web_resources")

def prerender_component(
Expand Down Expand Up @@ -31,11 +38,14 @@ def prerender_component(
Args:
name: The name of this rule.
srcs: The TypeScript source files for use in prerendering.
srcs: The source files for use in prerendering. May be `*.ts` files or
`*.js`/`*.mjs`/`*.cjs`. All source files must be JavaScript, or all
source files must be TypeScript. However, mixing the two is not
allowed. `*.d.ts` files are also allowed in either case.
tsconfig: A label referencing a tsconfig.json file or `ts_config()`
target. Will be used to compile files in `srcs`.
target. Will be used to compile `*.ts` files in `srcs`.
data: See https://docs.bazel.build/versions/master/be/common-definitions.html.
lib_deps: `ts_library()` dependencies for the TypeScript source files.
lib_deps: Dependencies for the source files.
scripts: List of client-side JavaScript libraries which can be included
in the prerendered HTML.
styles: List of CSS files or `filegroup()`s of CSS files which can be
Expand Down Expand Up @@ -66,26 +76,42 @@ def prerender_component(
"""

prerender_lib = "%s_prerender" % name
ts_library(
name = prerender_lib,
srcs = srcs,
tsconfig = tsconfig,
data = data,
deps = lib_deps + ["%s_prerender" % absolute(dep) for dep in deps],
testonly = testonly,
visibility = visibility,
)
if all([src.endswith(".ts") or src.endswith(".d.ts") for src in srcs]):
ts_library(
name = prerender_lib,
srcs = srcs,
tsconfig = tsconfig,
data = data + styles,
deps = lib_deps + ["%s_prerender" % absolute(dep) for dep in deps],
testonly = testonly,
visibility = visibility,
)
elif all([is_js_file(src) or src.endswith(".d.ts") for src in srcs]):
js_library(
name = prerender_lib,
srcs = srcs + data + styles, # `data` is included in `srcs`.
deps = lib_deps + ["%s_prerender" % absolute(dep) for dep in deps],
testonly = testonly,
visibility = visibility,
)
else:
fail(" ".join("""
All sources must be TypeScript (`*.ts`) or all sources must be JavaScript
(`*.js` / `*.mjs` / `*.cjs`). It is not possible to use some JavaScript sources
and some TypeScript sources in the same component (excluding `*.d.ts` files,
which are always allowed).
""".strip().split("\n")))

native.alias(
name = "%s_prerender_for_test" % name,
actual = ":%s" % prerender_lib,
testonly = True,
)

ts_library(
_js_reexport(
name = "%s_scripts" % name,
srcs = [],
deps = scripts + ["%s_scripts" % absolute(dep) for dep in deps],
srcs = scripts,
deps = ["%s_scripts" % absolute(dep) for dep in deps],
testonly = testonly,
visibility = visibility,
)
Expand All @@ -103,3 +129,66 @@ def prerender_component(
visibility = visibility,
deps = resources + ["%s_resources" % absolute(dep) for dep in deps],
)

def _js_reexport_impl(ctx):
merged_js_module_info = JSModuleInfo(
direct_sources = depset([],
transitive = [src[JSModuleInfo].direct_sources
for src in ctx.attr.srcs],
),
sources = depset([],
transitive = [dep[JSModuleInfo].sources
for dep in ctx.attr.srcs + ctx.attr.deps],
),
)

merged_js_ecma_script_module_info = JSEcmaScriptModuleInfo(
direct_sources = depset([],
transitive = [src[JSEcmaScriptModuleInfo].direct_sources
for src in ctx.attr.srcs],
),
sources = depset([],
transitive = [dep[JSEcmaScriptModuleInfo].sources
for dep in ctx.attr.srcs + ctx.attr.deps],
),
)

# Replicates output groups for TS/JS rules. Mostly for debugging purposes.
# https://bazelbuild.github.io/rules_nodejs/TypeScript.html#accessing-javascript-outputs
output_group_info = OutputGroupInfo(
es5_sources = merged_js_module_info.direct_sources,
es6_sources = merged_js_ecma_script_module_info.direct_sources,
)

return [
merged_js_module_info,
merged_js_ecma_script_module_info,
output_group_info,
]

_js_reexport = rule(
implementation = _js_reexport_impl,
attrs = {
"srcs": attr.label_list(
default = [],
providers = [JSModuleInfo, JSEcmaScriptModuleInfo],
),
"deps": attr.label_list(
default = [],
providers = [JSModuleInfo, JSEcmaScriptModuleInfo],
),
},
doc = """
Re-exports the given `ts_library()` and `js_library()` targets. Targets
in `srcs` have their direct sources re-exported as the direct sources of
this target, while targets in `deps` are only included as transitive
sources.
This rule serves two purposes:
1. It re-exports **both** `ts_library()` and `js_library()`.
2. It merges multiple targets together, depending on all of them but
only re-exporting direct sources from the `srcs` attribute. Even
with `ts_library()` re-export it is not possible to re-export only
some of the given targets.
""",
)
7 changes: 6 additions & 1 deletion packages/rules_prerender/prerender_pages.bzl
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
"""Defines `prerender_pages()` functionality."""

load(
"@build_bazel_rules_nodejs//:providers.bzl",
"JSEcmaScriptModuleInfo",
"JSModuleInfo",
)
load("@npm//@bazel/postcss:index.bzl", "postcss_binary")
load("@npm//@bazel/rollup:index.bzl", "rollup_bundle")
load(":multi_inject_resources.bzl", "multi_inject_resources")
Expand Down Expand Up @@ -127,7 +132,7 @@ def prerender_pages(
# Bundle all client-side scripts at `%{name}_bundle.js`.
rollup_bundle(
name = bundle,
entry_point = ":%s_scripts.ts" % prerender_name,
entry_point = ":%s_scripts.js" % prerender_name,
config_file = "//packages/rules_prerender:rollup-default.config.js",
link_workspace_root = True,
silent = True,
Expand Down
20 changes: 14 additions & 6 deletions packages/rules_prerender/prerender_pages_unbundled.bzl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Defines `prerender_pages_unbundled()` functionality."""

load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "nodejs_binary")
load("@npm//@bazel/typescript:index.bzl", "ts_library")
load("//common:label.bzl", "absolute", "file_path_of")
load(":entry_points.bzl", "script_entry_point", "style_entry_point")
Expand Down Expand Up @@ -58,9 +58,9 @@ def prerender_pages_unbundled(
Outputs:
%{name}: A `web_resources()` target containing all the files generated
by the `src` file at their corresponding locations.
%{name}_scripts: A `ts_library()` rule containing all the client-side
%{name}_scripts: A `js_library()` rule containing all the client-side
scripts used by the page. This includes a generated file
`%{name}_scripts.ts` which acts as an entry point, importing all
`%{name}_scripts.js` which acts as an entry point, importing all
scripts that were included in the page via `includeScript()`.
%{name}_styles: A `filegroup()` containing all the CSS styles used by
the page.
Expand Down Expand Up @@ -116,7 +116,11 @@ def prerender_pages_unbundled(

# Execute the runner to generate annotated resources.
annotated = "%s_annotated" % name
js_src = ".ts".join(src.split(".ts")[:-1]) + ".js"
js_src = (
".ts".join(src.split(".ts")[:-1]) + ".js"
if src.endswith(".ts")
else src
)
prerender_resources(
name = annotated,
entry_point = file_path_of(absolute(js_src)),
Expand All @@ -136,16 +140,20 @@ def prerender_pages_unbundled(

# Generate the entry point importing all included scripts.
client_scripts = "%s_scripts" % name
script_entry = "%s.ts" % client_scripts
script_entry = "%s.js" % client_scripts
script_entry_point(
name = "%s_entry" % client_scripts,
metadata = metadata,
output_entry_point = script_entry,
testonly = testonly,
# Export this file so Rollup can have a direct, label reference to the
# entry point, since including the file in a `depset()` with other files
# is not good enough.
visibility = visibility,
)

# Reexport all included scripts at `%{name}_scripts`.
ts_library(
js_library(
name = client_scripts,
srcs = [script_entry],
deps = [":%s" % component_scripts],
Expand Down
3 changes: 2 additions & 1 deletion packages/rules_prerender/prerender_resources.bzl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Defines `prerender_resources()` functionality."""

load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_binary")
load("//common:paths.bzl", "is_js_file")
load("//packages/renderer:build_vars.bzl", "RENDERER_RUNTIME_DEPS")
load(":web_resources.bzl", "WebResourceInfo")

Expand Down Expand Up @@ -60,7 +61,7 @@ def prerender_resources(
visibility: See https://docs.bazel.build/versions/master/be/common-definitions.html.
"""
# Validate `entry_point`.
if "/" not in entry_point or not entry_point.endswith(".js"):
if "/" not in entry_point or not is_js_file(entry_point):
fail(("`entry_point` (%s) *must* be a workspace-relative path of the"
+ " format: \"path/to/pkg/file.js\"") % entry_point)

Expand Down

0 comments on commit 171cac8

Please sign in to comment.