angular/devtools/tools/dev-server/index.bzl
AleksanderBodurri 445fbf81fd refactor(devtools): bring the angular devtools directory into the root bazel workspace
Previously devtools used a nested workspace for its bazel configurations. This meant framework dependencies were consumed via npm.

Now devtools is part of the root bazel directory that all other files in this codebase fall under. This allows us to build devtools using local angular packages, removing the need to consume these dependencies with npn. This is useful because we no longer have to update these dependencies with an automated tool like renovate, and our CI tests will always run against the most up to date framework packages.
2022-01-26 16:35:31 -05:00

126 lines
5.1 KiB
Python

load("@build_bazel_rules_nodejs//:providers.bzl", "JSNamedModuleInfo")
"""Gets the workspace name of the given rule context."""
def _get_workspace_name(ctx):
if ctx.label.workspace_root:
# We need the workspace_name for the target being visited.
# Starlark doesn't have this - instead they have a workspace_root
# which looks like "external/repo_name" - so grab the second path segment.
return ctx.label.workspace_root.split("/")[1]
else:
return ctx.workspace_name
"""Implementation of the dev server rule."""
def _dev_server_rule_impl(ctx):
files = depset(ctx.files.srcs)
# List of files which are required for the devserver to run. This includes the
# bazel runfile helpers (to resolve runfiles in bash) and the devserver binary
# with its transitive runfiles (in order to be able to run the devserver).
required_tools = ctx.files._bash_runfile_helpers + \
ctx.files._dev_server_bin + \
ctx.attr._dev_server_bin[DefaultInfo].files.to_list() + \
ctx.attr._dev_server_bin[DefaultInfo].data_runfiles.files.to_list()
# Walk through all dependencies specified in the "deps" attribute. These labels need to be
# unwrapped in case there are built using TypeScript-specific rules. This is because targets
# built using "ts_library" or "ng_module" do not declare the generated JS files as default
# rule output.
for d in ctx.attr.deps:
if JSNamedModuleInfo in d:
files = depset(transitive = [files, d[JSNamedModuleInfo].sources])
elif hasattr(d, "files"):
files = depset(transitive = [files, d.files])
workspace_name = _get_workspace_name(ctx)
root_paths = ["", "/".join([workspace_name, ctx.label.package])] + ctx.attr.additional_root_paths
# We can't use "ctx.actions.args()" because there is no way to convert the args object
# into a string representing the command line arguments. It looks like bazel has some
# internal logic to compute the string representation of "ctx.actions.args()".
args = '--root_paths="%s" ' % ",".join(root_paths)
if ctx.attr.historyApiFallback:
args += "--historyApiFallback "
ctx.actions.expand_template(
template = ctx.file._launcher_template,
output = ctx.outputs.launcher,
substitutions = {
"TEMPLATED_args": args,
},
is_executable = True,
)
return [
DefaultInfo(runfiles = ctx.runfiles(
files = files.to_list() + required_tools,
collect_data = True,
collect_default = True,
)),
]
dev_server_rule = rule(
implementation = _dev_server_rule_impl,
outputs = {
"launcher": "%{name}.sh",
},
attrs = {
"additional_root_paths": attr.string_list(doc = """
Additionally paths to serve files from. The paths should be formatted
as manifest paths (e.g. "my_workspace/src")
"""),
"deps": attr.label_list(
allow_files = True,
doc = """
Dependencies that need to be available to the dev-server. This attribute can be
used for TypeScript targets which provide multiple flavors of output.
""",
),
"historyApiFallback": attr.bool(
default = True,
doc = """
Whether the devserver should fallback to "/index.html" for non-file requests.
This is helpful for single page applications using the HTML history API.
""",
),
"srcs": attr.label_list(allow_files = True, doc = """
Sources that should be available to the dev-server. This attribute can be
used for explicit files. This attribute only uses the files exposed by the
DefaultInfo provider (i.e. TypeScript targets should be added to "deps").
"""),
"_bash_runfile_helpers": attr.label(default = Label("@bazel_tools//tools/bash/runfiles")),
"_dev_server_bin": attr.label(
default = Label("//devtools/tools/dev-server:dev-server_bin"),
),
"_launcher_template": attr.label(allow_single_file = True, default = Label("//devtools/tools/dev-server:launcher_template.sh")),
},
)
"""
Creates a dev server that can depend on individual bazel targets. The server uses
bazel runfile resolution in order to work with Bazel package paths. e.g. developers can
request files through their manifest path: "my_workspace/src/dev-app/my-genfile".
"""
def dev_server(name, testonly = False, port = 4200, tags = [], **kwargs):
dev_server_rule(
name = "%s_launcher" % name,
visibility = ["//visibility:private"],
tags = tags,
testonly = testonly,
**kwargs
)
native.sh_binary(
name = name,
# The "ibazel_notify_changes" tag tells ibazel to not relaunch the executable on file
# changes. Rather it will communicate with the server implementation through "stdin".
tags = tags + ["ibazel_notify_changes"],
srcs = ["%s_launcher.sh" % name],
data = [":%s_launcher" % name],
args = ["--port=%s" % port],
testonly = testonly,
)