diff --git a/docs/swc.md b/docs/swc.md index 4bdff99..a5a2b01 100644 --- a/docs/swc.md +++ b/docs/swc.md @@ -7,7 +7,7 @@ swc rule ## swc
-swc(name, srcs, args, data, swcrc, source_maps, source_map_outputs, kwargs) +swc(name, srcs, args, data, output_dir, swcrc, source_maps, source_map_outputs, kwargs)Execute the swc compiler @@ -21,6 +21,7 @@ Execute the swc compiler | srcs | source files, typically .ts files in the source tree |
None
|
| args | additional arguments to pass to swc cli, see https://swc.rs/docs/usage/cli | []
|
| data | runtime dependencies to be propagated in the runfiles | []
|
+| output_dir | whether to produce a directory output rather than individual files | False
|
| swcrc | label of a configuration file for swc, see https://swc.rs/docs/configuration/swcrc | None
|
| source_maps | If set, the --source-maps argument is passed to the swc cli with the value. True/False are automaticaly converted to "true"/"false" string values the cli expects. If source_maps is "true" or "both" then source_map_outputs is automatically set to True. | None
|
| source_map_outputs | if the rule is expected to produce a .js.map file output for each .js file output | False
|
diff --git a/examples/directory/BUILD.bazel b/examples/directory/BUILD.bazel
new file mode 100644
index 0000000..c77a31a
--- /dev/null
+++ b/examples/directory/BUILD.bazel
@@ -0,0 +1,33 @@
+"""Directory use case for swc: minifying a folder of code-split files
+
+This has to be a directory-in, directory-out pattern since the codesplitter produces unpredictable filenames.
+
+Note that this example also depends on the setup in /WORKSPACE at the root of this repository.
+"""
+
+load("@aspect_rules_swc//swc:swc.bzl", "swc")
+load(":mocks.bzl", "mock_codesplit")
+
+mock_codesplit(
+ name = "split_app",
+)
+
+# Runs `swc path/to/split_app --out-dir ../../bazel-bin/examples/directory/minify`
+# You can run `bazel build --subcommands //examples/directory:minify`
+# to see the exact command line Bazel runs.
+# Note that by default, sources are found by glob(["**/*.ts"])
+swc(
+ name = "minify",
+ srcs = ["split_app"],
+ output_dir = True,
+)
+
+sh_test(
+ name = "test",
+ srcs = ["test.sh"],
+ # Workaround https://github.com/swc-project/swc/issues/3028
+ # The test will need to know this folder to make the assertion
+ args = ["$(TARGET_CPU)-$(COMPILATION_MODE)"],
+ data = ["minify"],
+ deps = ["@bazel_tools//tools/bash/runfiles"],
+)
diff --git a/examples/directory/mocks.bzl b/examples/directory/mocks.bzl
new file mode 100644
index 0000000..c172341
--- /dev/null
+++ b/examples/directory/mocks.bzl
@@ -0,0 +1,22 @@
+"Placeholder for a rule like webpack which may produce an unpredictable number of JS chunks"
+
+load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory")
+load("@bazel_skylib//rules:write_file.bzl", "write_file")
+
+def mock_codesplit(name):
+ write_file(
+ name = "write1",
+ out = "file1.js",
+ content = ["const a = 1"],
+ )
+
+ write_file(
+ name = "write2",
+ out = "file2.js",
+ content = ["const a = 2"],
+ )
+
+ copy_to_directory(
+ name = name,
+ srcs = ["file1.js", "file2.js"],
+ )
diff --git a/examples/directory/test.sh b/examples/directory/test.sh
new file mode 100755
index 0000000..01f3401
--- /dev/null
+++ b/examples/directory/test.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+
+# --- begin runfiles.bash initialization v2 ---
+# Copy-pasted from the Bazel Bash runfiles library v2.
+set -uo pipefail; f=bazel_tools/tools/bash/runfiles/runfiles.bash
+source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \
+ source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \
+ source "$0.runfiles/$f" 2>/dev/null || \
+ source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
+ source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
+ { echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e
+# --- end runfiles.bash initialization v2 ---
+
+set -o errexit
+
+ARCH=$1
+readonly in_folder=$(rlocation $TEST_WORKSPACE/$(dirname $TEST_BINARY)/minify)
+
+# Due to https://github.com/swc-project/swc/issues/3028 the path is longer than we'd like
+# It should really just contain $in_folder/file1.js
+if ! [[ -e $in_folder/$ARCH/bin/examples/directory/split_app/file1.js ]]; then
+ echo >&2 "Missing expected output file in directory"
+ exit 1
+fi
diff --git a/swc/private/swc.bzl b/swc/private/swc.bzl
index 15eea59..1069a77 100644
--- a/swc/private/swc.bzl
+++ b/swc/private/swc.bzl
@@ -6,6 +6,7 @@ _attrs = {
"srcs": attr.label_list(allow_files = True, mandatory = True),
"args": attr.string_list(),
"source_maps": attr.string(),
+ "output_dir": attr.bool(),
"data": attr.label_list(default = [], allow_files = True),
"swcrc": attr.label(allow_single_file = True),
"swc_cli": attr.label(
@@ -21,60 +22,89 @@ _outputs = {
}
def _impl(ctx):
- outputs = ctx.outputs.js_outs + ctx.outputs.map_outs
+ outputs = []
source_maps = len(ctx.outputs.map_outs) > 0
+ binding = ctx.toolchains["@aspect_rules_swc//swc:toolchain_type"].swcinfo.binding
+ args = ctx.actions.args()
- for src in ctx.files.srcs:
- js_out = ctx.actions.declare_file(paths.replace_extension(src.basename, ".js"), sibling = src)
- outs = [js_out]
- if source_maps:
- outs.append(ctx.actions.declare_file(paths.replace_extension(src.basename, ".js.map"), sibling = src))
- args = ctx.actions.args()
-
- # Add user specified arguments *before* rule supplied arguments
- args.add_all(ctx.attr.args)
-
- # Pass in the swcrc config if it is set; if it is
- # then source paths are expected to be relative to the swcrc directory
- src_path = src.path
- if ctx.file.swcrc:
- swcrc_path = ctx.file.swcrc.path
- swcrc_directory = paths.dirname(swcrc_path)
- args.add_all([
- "--config-file",
- swcrc_path,
- ])
-
- # TODO(greg): determine if this is needed, given that relativize below should provide a correct path
- # if not src_path.startswith(swcrc_directory):
- # fail("sources must be in swcrc directory or subdirectory if swcrc is specified")
- src_path = paths.relativize(src_path, swcrc_directory)
+ # Add user specified arguments *before* rule supplied arguments
+ args.add_all(ctx.attr.args)
+ if ctx.attr.output_dir:
+ if len(ctx.attr.srcs) != 1:
+ fail("Under output_dir, there must be a single entry in srcs")
+ if not ctx.files.srcs[0].is_directory:
+ fail("Under output_dir, the srcs must be directories, not files")
+ out = ctx.actions.declare_file(ctx.label.name)
+ outputs.append(out)
args.add_all([
- src_path,
- "--out-file",
- js_out.path,
+ ctx.files.srcs[0].path,
+ "--out-dir",
+ out.path,
"--no-swcrc",
"-q",
])
- binding = ctx.toolchains["@aspect_rules_swc//swc:toolchain_type"].swcinfo.binding
-
ctx.actions.run(
- inputs = [src] + ctx.toolchains["@aspect_rules_swc//swc:toolchain_type"].swcinfo.tool_files,
+ inputs = ctx.files.srcs + ctx.toolchains["@aspect_rules_swc//swc:toolchain_type"].swcinfo.tool_files,
arguments = [args],
- outputs = outs,
+ outputs = [out],
env = {
# Our patch for @swc/core uses this environment variable to locate the rust binding
"SWC_BINARY_PATH": binding,
},
executable = ctx.executable.swc_cli,
- progress_message = "Transpiling with swc %s [swc %s]" % (
- ctx.label,
- src.short_path,
- ),
+ progress_message = "Transpiling with swc %s" % ctx.label,
)
+ else:
+ outputs.extend(ctx.outputs.js_outs)
+ outputs.extend(ctx.outputs.map_outs)
+ for src in ctx.files.srcs:
+ js_out = ctx.actions.declare_file(paths.replace_extension(src.basename, ".js"), sibling = src)
+ outs = [js_out]
+ if source_maps:
+ outs.append(ctx.actions.declare_file(paths.replace_extension(src.basename, ".js.map"), sibling = src))
+
+ # Pass in the swcrc config if it is set; if it is
+ # then source paths are expected to be relative to the swcrc directory
+ src_path = src.path
+ if ctx.file.swcrc:
+ swcrc_path = ctx.file.swcrc.path
+ swcrc_directory = paths.dirname(swcrc_path)
+ args.add_all([
+ "--config-file",
+ swcrc_path,
+ ])
+
+ # TODO(greg): determine if this is needed, given that relativize below should provide a correct path
+ # if not src_path.startswith(swcrc_directory):
+ # fail("sources must be in swcrc directory or subdirectory if swcrc is specified")
+ src_path = paths.relativize(src_path, swcrc_directory)
+
+ args.add_all([
+ src_path,
+ "--out-file",
+ js_out.path,
+ "--no-swcrc",
+ "-q",
+ ])
+
+ ctx.actions.run(
+ inputs = [src] + ctx.toolchains["@aspect_rules_swc//swc:toolchain_type"].swcinfo.tool_files,
+ arguments = [args],
+ outputs = outs,
+ env = {
+ # Our patch for @swc/core uses this environment variable to locate the rust binding
+ "SWC_BINARY_PATH": binding,
+ },
+ executable = ctx.executable.swc_cli,
+ progress_message = "Transpiling with swc %s [swc %s]" % (
+ ctx.label,
+ src.short_path,
+ ),
+ )
+
providers = [
DefaultInfo(
files = depset(outputs),
diff --git a/swc/swc.bzl b/swc/swc.bzl
index f0d867d..71a7137 100644
--- a/swc/swc.bzl
+++ b/swc/swc.bzl
@@ -20,13 +20,14 @@ def _is_supported_src(src):
return True
return False
-def swc(name, srcs = None, args = [], data = [], swcrc = None, source_maps = None, source_map_outputs = False, **kwargs):
+def swc(name, srcs = None, args = [], data = [], output_dir = False, swcrc = None, source_maps = None, source_map_outputs = False, **kwargs):
"""Execute the swc compiler
Args:
name: A name for the target
srcs: source files, typically .ts files in the source tree
data: runtime dependencies to be propagated in the runfiles
+ output_dir: whether to produce a directory output rather than individual files
args: additional arguments to pass to swc cli, see https://swc.rs/docs/usage/cli
source_maps: If set, the --source-maps argument is passed to the swc cli with the value.
True/False are automaticaly converted to "true"/"false" string values the cli expects.
@@ -55,17 +56,20 @@ def swc(name, srcs = None, args = [], data = [], swcrc = None, source_maps = Non
# Determine js & map outputs
js_outs = []
map_outs = []
- for f in srcs:
- if _is_supported_src(f):
- js_outs.append(paths.replace_extension(f, ".js"))
- if source_map_outputs:
- map_outs.append(paths.replace_extension(f, ".js.map"))
+
+ if not output_dir:
+ for f in srcs:
+ if _is_supported_src(f):
+ js_outs.append(paths.replace_extension(f, ".js"))
+ if source_map_outputs:
+ map_outs.append(paths.replace_extension(f, ".js.map"))
_swc(
name = name,
srcs = srcs,
js_outs = js_outs,
map_outs = map_outs,
+ output_dir = output_dir,
args = args,
data = data,
swcrc = swcrc,