Skip to content

Commit

Permalink
feat: support --output-dir flag (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeagle authored Dec 14, 2021
1 parent 7002e88 commit 300e69c
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 44 deletions.
3 changes: 2 additions & 1 deletion docs/swc.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ swc rule
## swc

<pre>
swc(<a href="#swc-name">name</a>, <a href="#swc-srcs">srcs</a>, <a href="#swc-args">args</a>, <a href="#swc-data">data</a>, <a href="#swc-swcrc">swcrc</a>, <a href="#swc-source_maps">source_maps</a>, <a href="#swc-source_map_outputs">source_map_outputs</a>, <a href="#swc-kwargs">kwargs</a>)
swc(<a href="#swc-name">name</a>, <a href="#swc-srcs">srcs</a>, <a href="#swc-args">args</a>, <a href="#swc-data">data</a>, <a href="#swc-output_dir">output_dir</a>, <a href="#swc-swcrc">swcrc</a>, <a href="#swc-source_maps">source_maps</a>, <a href="#swc-source_map_outputs">source_map_outputs</a>, <a href="#swc-kwargs">kwargs</a>)
</pre>

Execute the swc compiler
Expand All @@ -21,6 +21,7 @@ Execute the swc compiler
| <a id="swc-srcs"></a>srcs | source files, typically .ts files in the source tree | <code>None</code> |
| <a id="swc-args"></a>args | additional arguments to pass to swc cli, see https://swc.rs/docs/usage/cli | <code>[]</code> |
| <a id="swc-data"></a>data | runtime dependencies to be propagated in the runfiles | <code>[]</code> |
| <a id="swc-output_dir"></a>output_dir | whether to produce a directory output rather than individual files | <code>False</code> |
| <a id="swc-swcrc"></a>swcrc | label of a configuration file for swc, see https://swc.rs/docs/configuration/swcrc | <code>None</code> |
| <a id="swc-source_maps"></a>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. | <code>None</code> |
| <a id="swc-source_map_outputs"></a>source_map_outputs | if the rule is expected to produce a .js.map file output for each .js file output | <code>False</code> |
Expand Down
33 changes: 33 additions & 0 deletions examples/directory/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -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"],
)
22 changes: 22 additions & 0 deletions examples/directory/mocks.bzl
Original file line number Diff line number Diff line change
@@ -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"],
)
24 changes: 24 additions & 0 deletions examples/directory/test.sh
Original file line number Diff line number Diff line change
@@ -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
104 changes: 67 additions & 37 deletions swc/private/swc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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),
Expand Down
16 changes: 10 additions & 6 deletions swc/swc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 300e69c

Please sign in to comment.