From 9d8ef1b2920d78c80588f5a6b55d96224e7abe01 Mon Sep 17 00:00:00 2001 From: Alex Eagle Date: Thu, 17 Feb 2022 21:50:04 -0800 Subject: [PATCH] feat: support srcs with DefaultInfo producing .ts 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. --- WORKSPACE | 2 +- docs/swc.md | 8 +-- examples/custom_outs/BUILD.bazel | 2 +- examples/filegroup/BUILD.bazel | 25 +++++++++ examples/filegroup/a.ts | 1 + examples/filegroup/b.ts | 1 + examples/filegroup/check_outputs.sh | 7 +++ swc/BUILD.bazel | 1 + swc/private/swc.bzl | 80 +++++++++++++++++++++++++---- swc/private/versions.bzl | 15 ++++++ swc/swc.bzl | 38 ++++---------- swc/tests/versions_test.bzl | 2 +- 12 files changed, 135 insertions(+), 47 deletions(-) create mode 100644 examples/filegroup/BUILD.bazel create mode 100644 examples/filegroup/a.ts create mode 100644 examples/filegroup/b.ts create mode 100755 examples/filegroup/check_outputs.sh diff --git a/WORKSPACE b/WORKSPACE index eb010a4..19e7f82 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -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") diff --git a/docs/swc.md b/docs/swc.md index 4d816b8..1b6f37d 100644 --- a/docs/swc.md +++ b/docs/swc.md @@ -16,7 +16,7 @@ swc(name = "transpile") ## swc_transpiler
-swc_transpiler(name, args, data, js_outs, map_outs, output_dir, srcs, swc_cli, swcrc)
+swc_transpiler(name, args, data, js_outs, map_outs, output_dir, source_maps, srcs, swc_cli, swcrc)
 
Underlying rule for the `swc` macro. @@ -37,6 +37,7 @@ for example to set your own output labels for `js_outs`. | js_outs | list of expected JavaScript output files.

There must be one for each entry in srcs, and in the same order. | List of labels | optional | | | map_outs | list of expected source map output files.

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 | | | output_dir | whether to produce a directory output rather than individual files | Boolean | optional | False | +| source_maps | see https://swc.rs/docs/usage/cli#--source-maps--s | String | optional | "false" | | srcs | source files, typically .ts files in the source tree | List of labels | required | | | swc_cli | binary that executes the swc CLI | Label | optional | @aspect_rules_swc//swc:cli | | swcrc | label of a configuration file for swc, see https://swc.rs/docs/configuration/swcrc | Label | optional | None | @@ -47,7 +48,7 @@ for example to set your own output labels for `js_outs`. ## swc
-swc(name, srcs, args, data, output_dir, swcrc, source_maps, source_map_outputs, kwargs)
+swc(name, srcs, args, data, output_dir, swcrc, source_maps, kwargs)
 
Execute the swc compiler @@ -63,8 +64,7 @@ Execute the swc compiler | 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 | +| 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. | False | | kwargs | additional named parameters like tags or visibility | none | diff --git a/examples/custom_outs/BUILD.bazel b/examples/custom_outs/BUILD.bazel index 778dd1b..c6001a8 100644 --- a/examples/custom_outs/BUILD.bazel +++ b/examples/custom_outs/BUILD.bazel @@ -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")], diff --git a/examples/filegroup/BUILD.bazel b/examples/filegroup/BUILD.bazel new file mode 100644 index 0000000..1170b76 --- /dev/null +++ b/examples/filegroup/BUILD.bazel @@ -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"], +) diff --git a/examples/filegroup/a.ts b/examples/filegroup/a.ts new file mode 100644 index 0000000..f6c1fdd --- /dev/null +++ b/examples/filegroup/a.ts @@ -0,0 +1 @@ +export const a: string = "a"; diff --git a/examples/filegroup/b.ts b/examples/filegroup/b.ts new file mode 100644 index 0000000..bd865e3 --- /dev/null +++ b/examples/filegroup/b.ts @@ -0,0 +1 @@ +export const b: string = "b"; diff --git a/examples/filegroup/check_outputs.sh b/examples/filegroup/check_outputs.sh new file mode 100755 index 0000000..6a4faf9 --- /dev/null +++ b/examples/filegroup/check_outputs.sh @@ -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 \ No newline at end of file diff --git a/swc/BUILD.bazel b/swc/BUILD.bazel index df8cbed..3100e1d 100644 --- a/swc/BUILD.bazel +++ b/swc/BUILD.bazel @@ -63,6 +63,7 @@ bzl_library( deps = [ "//swc/private:swc", "@bazel_skylib//lib:paths", + "@bazel_skylib//lib:types", ], ) diff --git a/swc/private/swc.bzl b/swc/private/swc.bzl index 05c8584..6925c67 100644 --- a/swc/private/swc.bzl +++ b/swc/private/swc.bzl @@ -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", @@ -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: @@ -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: @@ -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, ) diff --git a/swc/private/versions.bzl b/swc/private/versions.bzl index 888f927..eb19328 100644 --- a/swc/private/versions.bzl +++ b/swc/private/versions.bzl @@ -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", diff --git a/swc/swc.bzl b/swc/swc.bzl index 0ee3eff..08ae7e2 100644 --- a/swc/swc.bzl +++ b/swc/swc.bzl @@ -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. @@ -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: @@ -45,14 +34,15 @@ 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: @@ -60,24 +50,13 @@ def swc(name, srcs = None, args = [], data = [], output_dir = False, swcrc = Non 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, @@ -85,6 +64,7 @@ def swc(name, srcs = None, args = [], data = [], output_dir = False, swcrc = Non js_outs = js_outs, map_outs = map_outs, output_dir = output_dir, + source_maps = source_maps, args = args, data = data, swcrc = swcrc, diff --git a/swc/tests/versions_test.bzl b/swc/tests/versions_test.bzl index 79d3887..00cd2ab 100644 --- a/swc/tests/versions_test.bzl +++ b/swc/tests/versions_test.bzl @@ -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,