"""Helper macros for rendering parts of an Angular application at build time""" load("@build_bazel_rules_nodejs//:index.bzl", _nodejs_binary = "nodejs_binary", _nodejs_test = "nodejs_test") load("@npm//@bazel/typescript:index.bzl", _ts_library = "ts_library") def _get_output_path(route, root_at): return root_at + "/" + route + "/index.html" def ng_prerender(name, index, prerender_roots = [], **kwargs): """ Helper macro for prerendering Angular routes to index files as part of the build The outputs of this macro are: %name% - all the rendered roots, plus the root route / %name%.root - an alias referencing just the root index file %name%.%route% - an alias referencing each rendered route, with / replaced by underscores Args: name: Rule name for the main output genrule index: Label for the production index.html file with which to render into prerender_roots: A list of roots that will be prerendered as part of this macro, the root route / is always rendered """ renderer_lib = "%s_renderer_lib" % name _ts_library( name = renderer_lib, srcs = ["//src:prerender.ts"], deps = [ "//src/app:app_server", "@npm//@angular/platform-server", "@npm//zone.js", "@npm//domino", "@npm//reflect-metadata", "@npm//@types/node", ], ) bin = "%s_bin" % renderer_lib _nodejs_binary( name = bin, data = [ ":%s" % renderer_lib, "@npm//@angular/platform-server", "@npm//zone.js", "@npm//domino", "@npm//reflect-metadata", ], entry_point = "//src:prerender.ts", templated_args = ["--nobazel_run_linker"], ) root_at = "_prerender/" + native.package_name() # we can't output "foo/index.html" since that collides with source files and will likely cross a package boundary # so we output "_prerender/pkg_name/route/index.html" prerender_root_outs = [_get_output_path(route, root_at) for route in prerender_roots] root_index = "%s/index.html" % root_at visibility = kwargs.pop("visibility", []) native.genrule( name = name, srcs = [index], outs = [root_index] + prerender_root_outs, cmd = "$(location :%s) --index $(location %s) --outs $(OUTS) --routes / %s" % (bin, index, " ".join(prerender_roots)), tools = [":%s" % bin], message = "Prerendering Angular", visibility = visibility, tags = kwargs.pop("tags", []), ) # convenience "output groups" from macro native.alias( name = "%s.root" % name, actual = root_index, visibility = visibility, ) [ native.alias( name = "%s.%s" % (name, route.replace("/", "_")), actual = _get_output_path(route, root_at), visibility = visibility, ) for route in prerender_roots ] def ng_prerender_test(name, index, route, expected_elements = [], **kwargs): """ Simple smoke test for a prerendered index file, as generated by ng_prerender Args: name: Rule name for the test index: Label of the index file under test route: The route that this index file belongs to expected_elements: An optional array of expected elements that should appear in the index file """ _ts_library( name = "%s_render_spec" % name, srcs = ["//src:prerender-spec.ts"], deps = [ "@npm//@types/node", ], testonly = 1, ) _nodejs_test( name = name, data = [ ":%s_render_spec" % name, index, ], templated_args = ["--index $(location %s)" % index, "--route %s" % route, "--expected %s" % (" ".join(expected_elements))], entry_point = "//src:prerender-spec.ts", tags = kwargs.pop("tags", []), )