Skip to content

Commit

Permalink
feat: support srcs with DefaultInfo producing .ts
Browse files Browse the repository at this point in the history
For example, filegroup can now be used to yield the sources we pass to swc.
Like with ts_project, this means we don't pre-declare outputs, since Bazel doesn't support that.
  • Loading branch information
alexeagle committed Feb 18, 2022
1 parent ee3fa88 commit 9d8ef1b
Show file tree
Hide file tree
Showing 12 changed files with 135 additions and 47 deletions.
2 changes: 1 addition & 1 deletion WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ load("//swc:repositories.bzl", "swc_register_toolchains")

swc_register_toolchains(
name = "default_swc",
swc_version = "v1.2.119",
swc_version = "v1.2.141",
)

load("@rules_nodejs//nodejs:repositories.bzl", "nodejs_register_toolchains")
Expand Down
8 changes: 4 additions & 4 deletions docs/swc.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ swc(name = "transpile")
## swc_transpiler

<pre>
swc_transpiler(<a href="#swc_transpiler-name">name</a>, <a href="#swc_transpiler-args">args</a>, <a href="#swc_transpiler-data">data</a>, <a href="#swc_transpiler-js_outs">js_outs</a>, <a href="#swc_transpiler-map_outs">map_outs</a>, <a href="#swc_transpiler-output_dir">output_dir</a>, <a href="#swc_transpiler-srcs">srcs</a>, <a href="#swc_transpiler-swc_cli">swc_cli</a>, <a href="#swc_transpiler-swcrc">swcrc</a>)
swc_transpiler(<a href="#swc_transpiler-name">name</a>, <a href="#swc_transpiler-args">args</a>, <a href="#swc_transpiler-data">data</a>, <a href="#swc_transpiler-js_outs">js_outs</a>, <a href="#swc_transpiler-map_outs">map_outs</a>, <a href="#swc_transpiler-output_dir">output_dir</a>, <a href="#swc_transpiler-source_maps">source_maps</a>, <a href="#swc_transpiler-srcs">srcs</a>, <a href="#swc_transpiler-swc_cli">swc_cli</a>, <a href="#swc_transpiler-swcrc">swcrc</a>)
</pre>

Underlying rule for the `swc` macro.
Expand All @@ -37,6 +37,7 @@ for example to set your own output labels for `js_outs`.
| <a id="swc_transpiler-js_outs"></a>js_outs | list of expected JavaScript output files.<br><br>There must be one for each entry in srcs, and in the same order. | List of labels | optional | |
| <a id="swc_transpiler-map_outs"></a>map_outs | list of expected source map output files.<br><br>Can be empty, meaning no source maps should be produced. If non-empty, there must be one for each entry in srcs, and in the same order. | List of labels | optional | |
| <a id="swc_transpiler-output_dir"></a>output_dir | whether to produce a directory output rather than individual files | Boolean | optional | False |
| <a id="swc_transpiler-source_maps"></a>source_maps | see https://swc.rs/docs/usage/cli#--source-maps--s | String | optional | "false" |
| <a id="swc_transpiler-srcs"></a>srcs | source files, typically .ts files in the source tree | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | required | |
| <a id="swc_transpiler-swc_cli"></a>swc_cli | binary that executes the swc CLI | <a href="https://bazel.build/docs/build-ref.html#labels">Label</a> | optional | @aspect_rules_swc//swc:cli |
| <a id="swc_transpiler-swcrc"></a>swcrc | label of a configuration file for swc, see https://swc.rs/docs/configuration/swcrc | <a href="https://bazel.build/docs/build-ref.html#labels">Label</a> | optional | None |
Expand All @@ -47,7 +48,7 @@ for example to set your own output labels for `js_outs`.
## 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-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>)
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-kwargs">kwargs</a>)
</pre>

Execute the swc compiler
Expand All @@ -63,8 +64,7 @@ Execute the swc compiler
| <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> |
| <a id="swc-source_maps"></a>source_maps | If set, the --source-maps argument is passed to the swc cli with the value. See https://swc.rs/docs/usage/cli#--source-maps--s True/False are automaticaly converted to "true"/"false" string values the cli expects. | <code>False</code> |
| <a id="swc-kwargs"></a>kwargs | additional named parameters like tags or visibility | none |


2 changes: 1 addition & 1 deletion examples/custom_outs/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ load("@bazel_skylib//rules:diff_test.bzl", "diff_test")
name = "transpile_" + format,
srcs = ["in.ts"],
args = [
"-C",
"--config",
"module.type=" + format,
],
js_outs = [format + "/out." + ("cjs" if format == "commonjs" else "js")],
Expand Down
25 changes: 25 additions & 0 deletions examples/filegroup/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
load("@aspect_rules_swc//swc:swc.bzl", "swc")

filegroup(
name = "srcs",
srcs = [
"a.ts",
"b.ts",
],
)

swc(
name = "transpile",
srcs = ["srcs"],
source_maps = "true",
)

# Since the srcs were in a filegroup, the swc macro cannot pre-declare the outputs.
# So there is no label ":a.js" that we can reference from the build file.
# However, a.js is still produced as one of the default outputs of the transpile rule.
# We can verify this in an action that depends on the ":transpile" rule and reads the files.
sh_test(
name = "check_outputs",
srcs = ["check_outputs.sh"],
data = [":transpile"],
)
1 change: 1 addition & 0 deletions examples/filegroup/a.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const a: string = "a";
1 change: 1 addition & 0 deletions examples/filegroup/b.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const b: string = "b";
7 changes: 7 additions & 0 deletions examples/filegroup/check_outputs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -o errexit

cd "$TEST_SRCDIR/$TEST_WORKSPACE/$(dirname $TEST_TARGET)"
grep "export var a" filegroup/a.js
grep "sourceMappingURL=a.js.map" filegroup/a.js
grep "../../../../../examples/filegroup/a.ts" filegroup/a.js.map
1 change: 1 addition & 0 deletions swc/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ bzl_library(
deps = [
"//swc/private:swc",
"@bazel_skylib//lib:paths",
"@bazel_skylib//lib:types",
],
)

Expand Down
80 changes: 69 additions & 11 deletions swc/private/swc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,30 @@
load("@bazel_skylib//lib:paths.bzl", "paths")

_attrs = {
"srcs": attr.label_list(allow_files = True, mandatory = True, doc = "source files, typically .ts files in the source tree"),
"args": attr.string_list(doc = "additional arguments to pass to swc cli, see https://swc.rs/docs/usage/cli"),
"output_dir": attr.bool(doc = "whether to produce a directory output rather than individual files"),
"data": attr.label_list(default = [], allow_files = True, doc = "runtime dependencies propagated to binaries that depend on this"),
"swcrc": attr.label(allow_single_file = True, doc = "label of a configuration file for swc, see https://swc.rs/docs/configuration/swcrc"),
"srcs": attr.label_list(
doc = "source files, typically .ts files in the source tree",
allow_files = True,
mandatory = True,
),
"args": attr.string_list(
doc = "additional arguments to pass to swc cli, see https://swc.rs/docs/usage/cli",
),
"source_maps": attr.string(
doc = "see https://swc.rs/docs/usage/cli#--source-maps--s",
values = ["true", "false", "inline", "both"],
default = "false",
),
"output_dir": attr.bool(
doc = "whether to produce a directory output rather than individual files",
),
"data": attr.label_list(
doc = "runtime dependencies propagated to binaries that depend on this",
allow_files = True,
),
"swcrc": attr.label(
doc = "label of a configuration file for swc, see https://swc.rs/docs/configuration/swcrc",
allow_single_file = True,
),
"swc_cli": attr.label(
doc = "binary that executes the swc CLI",
default = "@aspect_rules_swc//swc:cli",
Expand All @@ -26,14 +45,41 @@ Can be empty, meaning no source maps should be produced.
If non-empty, there must be one for each entry in srcs, and in the same order."""),
}

# In theory, swc can transform .js -> .js.
# But this would cause Bazel outputs to collide with inputs so it requires some re-rooting scheme.
# TODO: add this if users need it
_SUPPORTED_EXTENSIONS = [".ts", ".tsx", ".jsx", ".mjs", ".cjs"]

def _is_supported_src(src):
return paths.split_extension(src)[-1] in _SUPPORTED_EXTENSIONS

def _declare_outputs(ctx, paths):
return [ctx.actions.declare_file(p) for p in paths]

# TODO: aspect_bazel_lib should provide this?
def _relative_to_package(path, ctx):
for prefix in (ctx.bin_dir.path, ctx.label.package):
prefix += "/"
if path.startswith(prefix):
path = path[len(prefix):]
return path

def _calculate_js_outs(srcs):
return [paths.replace_extension(f, ".js") for f in srcs if _is_supported_src(f)]

def _calculate_map_outs(srcs, source_maps):
if source_maps in ["false", "inline"]:
return []
return [paths.replace_extension(f, ".js.map") for f in srcs if _is_supported_src(f)]

def _impl(ctx):
outputs = []
source_maps = len(ctx.outputs.map_outs) > 0
binding = ctx.toolchains["@aspect_rules_swc//swc:toolchain_type"].swcinfo.binding
args = ctx.actions.args()

# Add user specified arguments *before* rule supplied arguments
args.add_all(ctx.attr.args)
args.add_all(["--source-maps", ctx.attr.source_maps])

if ctx.attr.output_dir:
if len(ctx.attr.srcs) != 1:
Expand Down Expand Up @@ -63,14 +109,23 @@ def _impl(ctx):
)

else:
outputs.extend(ctx.outputs.js_outs)
outputs.extend(ctx.outputs.map_outs)
srcs = [_relative_to_package(src.path, ctx) for src in ctx.files.srcs]
if len(ctx.attr.js_outs):
js_outs = ctx.outputs.js_outs
else:
js_outs = _declare_outputs(ctx, _calculate_js_outs(srcs))
if len(ctx.attr.map_outs):
map_outs = ctx.outputs.map_outs
else:
map_outs = _declare_outputs(ctx, _calculate_map_outs(srcs, ctx.attr.source_maps))
outputs.extend(js_outs)
outputs.extend(map_outs)
for i, src in enumerate(ctx.files.srcs):
js_out = ctx.outputs.js_outs[i]
js_out = js_outs[i]
inputs = [src] + ctx.toolchains["@aspect_rules_swc//swc:toolchain_type"].swcinfo.tool_files
outs = [js_out]
if source_maps:
outs.append(ctx.outputs.map_outs[i])
if ctx.attr.source_maps in ["true", "both"]:
outs.append(map_outs[i])

# Pass in the swcrc config if it is set
if ctx.file.swcrc:
Expand Down Expand Up @@ -119,4 +174,7 @@ swc = struct(
implementation = _impl,
attrs = dict(_attrs, **_outputs),
toolchains = ["@aspect_rules_swc//swc:toolchain_type"],
SUPPORTED_EXTENSIONS = _SUPPORTED_EXTENSIONS,
calculate_js_outs = _calculate_js_outs,
calculate_map_outs = _calculate_map_outs,
)
15 changes: 15 additions & 0 deletions swc/private/versions.bzl
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
"Mirror of release info"

TOOL_VERSIONS = {
"v1.2.141": {
"android-arm-eabi": "sha384-1Kpl5sj2A50yTtZu5Ae7rGGY+t+0FYvcy/VCUp1IM8lW36RuhERqfahcWdmgtinB",
"android-arm64": "sha384-F/kmewGo2LlmyIeEQBnctRBJULmcSbko/5Gz1AJNxATWS1JUEX8C06AhdTpV7dk0",
"darwin-arm64": "sha384-UoQMYxodChrzQ785r7bo71aBDUqiu8qE8OSlE+64XrQUk7af0E9QfeA/uOKH81I8",
"darwin-x64": "sha384-nzfWWp7SHinJEGXyzeDFcprHs30oGJ4Xb6KV1NDPBMItkP37dGif9KQ4vjIcRiM2",
"freebsd-x64": "sha384-oS5YYV3h2CkOdPXy0ERp4/33EMn/iBDA353qp7zlSDsfuFpcCjdauFkMEiAwIipi",
"linux-arm-gnueabihf": "sha384-Y0xADrA2UwuwQq0OA3dNdjWLIqq4HwPR0lfhai3Ss+BvMDMpsjtSKLek/shav5++",
"linux-arm64-gnu": "sha384-lFM98s0YsVi2LRW5W082i+omOvkeOhiDFHWWdv2N91VGGXrmQxrMbhDgR85aPZBZ",
"linux-arm64-musl": "sha384-paCvYozCr4y0XQw4wbp5yi00GaAzMo6pqAxUMkYleY3is8H4obpRr48XCXEJ9ouq",
"linux-x64-gnu": "sha384-uNw6WyW05qRZhxam2ls9cQRXi9Cmx1+ItQ1+i2CgXuImHHl3lFhnlMCgvvJTdRyL",
"linux-x64-musl": "sha384-uzzI2i5JdE8G/zwvQPGeMbemVIwRnnqWExifbcQGNVYQ614Md26Hhriw4Ej9YFRA",
"win32-arm64-msvc": "sha384-iBDJmWJVtxouMntcoGrPqpOEZP5OQrXDWPOE1+kIWSvmlC/JDkNhPL0zyiTxX/Bb",
"win32-ia32-msvc": "sha384-p9MgHvIpyHbBoRSbvmAe3llF6xTmjTvfPTjc04xoNgMiIL2wCRG5HaPWNEd/10i0",
"win32-x64-msvc": "sha384-4t9URv/+EEfnaU1sp9aFnG/CVWD3/biCq18o/MLvKiZw4ZzcRtyqWZz12aL/HDUY",
},
"v1.2.119": {
"android-arm64": "sha384-whzMbD0maV04vzMdOVfPQNDhpe3gWy9Tyg+SsXbzgA5oZDoqTjTM3e2Y5MeKWnz3",
"darwin-arm64": "sha384-2E4E13tCxfatne30Ss2FY+o0T8ebMV4gO999oqWPbehFxcaQI/6+D304xGZPF8/x",
Expand Down
38 changes: 9 additions & 29 deletions swc/swc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ swc(name = "transpile")
"""

load("//swc/private:swc.bzl", _swc_lib = "swc")
load("@bazel_skylib//lib:paths.bzl", "paths")
load("@bazel_skylib//lib:types.bzl", "types")

swc_transpiler = rule(
doc = """Underlying rule for the `swc` macro.
Expand All @@ -24,18 +24,7 @@ for example to set your own output labels for `js_outs`.""",
toolchains = _swc_lib.toolchains,
)

# In theory, swc can transform .js -> .js.
# But this would cause Bazel outputs to collide with inputs so it requires some re-rooting scheme.
# TODO: add this if users need it
_SUPPORTED_EXTENSIONS = [".ts", ".tsx", ".jsx", ".mjs", ".cjs"]

def _is_supported_src(src):
for e in _SUPPORTED_EXTENSIONS:
if src.endswith(e):
return True
return False

def swc(name, srcs = None, args = [], data = [], output_dir = False, swcrc = None, source_maps = None, source_map_outputs = False, **kwargs):
def swc(name, srcs = None, args = [], data = [], output_dir = False, swcrc = None, source_maps = False, **kwargs):
"""Execute the swc compiler
Args:
Expand All @@ -45,46 +34,37 @@ def swc(name, srcs = None, args = [], data = [], output_dir = False, swcrc = Non
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.
See https://swc.rs/docs/usage/cli#--source-maps--s
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.
source_map_outputs: if the rule is expected to produce a .js.map file output for each .js file output
swcrc: label of a configuration file for swc, see https://swc.rs/docs/configuration/swcrc
**kwargs: additional named parameters like tags or visibility
"""
if srcs == None:
srcs = native.glob(["**/*" + e for e in _SUPPORTED_EXTENSIONS])
srcs = native.glob(["**/*" + e for e in _swc_lib.SUPPORTED_EXTENSIONS])
elif not types.is_list(srcs):
fail("srcs must be a list, not a " + type(srcs))

# Convert source_maps True/False to "true"/"false" args value
if source_maps == True:
source_maps = "true"
elif source_maps == False:
source_maps = "false"

# Detect if we are expecting sourcemap outputs
if not source_map_outputs:
source_map_outputs = (source_maps == "true" or source_maps == "both")

# Add the source_maps arg
if source_maps:
args = args + ["--source-maps", source_maps]

# Determine js & map outputs
js_outs = []
map_outs = []

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"))
js_outs = _swc_lib.calculate_js_outs(srcs)
map_outs = _swc_lib.calculate_map_outs(srcs, source_maps)

swc_transpiler(
name = name,
srcs = srcs,
js_outs = js_outs,
map_outs = map_outs,
output_dir = output_dir,
source_maps = source_maps,
args = args,
data = data,
swcrc = swcrc,
Expand Down
2 changes: 1 addition & 1 deletion swc/tests/versions_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ load("//swc/private:versions.bzl", "TOOL_VERSIONS")

def _smoke_test_impl(ctx):
env = unittest.begin(ctx)
asserts.equals(env, "v1.2.119", TOOL_VERSIONS.keys()[0])
asserts.equals(env, "v1.2.141", TOOL_VERSIONS.keys()[0])
return unittest.end(env)

# The unittest library requires that we export the test cases as named test rules,
Expand Down

0 comments on commit 9d8ef1b

Please sign in to comment.