diff --git a/examples/custom_bundling/BUILD.bazel b/examples/custom_bundling/BUILD.bazel index b712343a..1d142346 100644 --- a/examples/custom_bundling/BUILD.bazel +++ b/examples/custom_bundling/BUILD.bazel @@ -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, diff --git a/packages/rules_prerender/BUILD.bazel b/packages/rules_prerender/BUILD.bazel index d7451591..76f816e7 100644 --- a/packages/rules_prerender/BUILD.bazel +++ b/packages/rules_prerender/BUILD.bazel @@ -64,6 +64,7 @@ bzl_library( deps = [ ":web_resources", "//common:label", + "//common:paths", ], ) @@ -96,6 +97,7 @@ bzl_library( visibility = ["//:__pkg__"], deps = [ ":web_resources", + "//common:paths", "//packages/renderer:build_vars", ], ) diff --git a/packages/rules_prerender/prerender_component.bzl b/packages/rules_prerender/prerender_component.bzl index e8b21844..f2eede2c 100644 --- a/packages/rules_prerender/prerender_component.bzl +++ b/packages/rules_prerender/prerender_component.bzl @@ -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( @@ -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 @@ -66,15 +76,31 @@ 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, @@ -82,10 +108,10 @@ def prerender_component( 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, ) @@ -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. + """, +) diff --git a/packages/rules_prerender/prerender_pages.bzl b/packages/rules_prerender/prerender_pages.bzl index 68918e4a..b87bbd28 100644 --- a/packages/rules_prerender/prerender_pages.bzl +++ b/packages/rules_prerender/prerender_pages.bzl @@ -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") @@ -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, diff --git a/packages/rules_prerender/prerender_pages_unbundled.bzl b/packages/rules_prerender/prerender_pages_unbundled.bzl index 8235a7a3..f0146905 100644 --- a/packages/rules_prerender/prerender_pages_unbundled.bzl +++ b/packages/rules_prerender/prerender_pages_unbundled.bzl @@ -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") @@ -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. @@ -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)), @@ -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], diff --git a/packages/rules_prerender/prerender_resources.bzl b/packages/rules_prerender/prerender_resources.bzl index 6e34d8b2..3f5669da 100644 --- a/packages/rules_prerender/prerender_resources.bzl +++ b/packages/rules_prerender/prerender_resources.bzl @@ -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") @@ -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)