diff --git a/docs/swc.md b/docs/swc.md
index 4fefde1..fe1f670 100644
--- a/docs/swc.md
+++ b/docs/swc.md
@@ -18,8 +18,8 @@ swc(
## swc_compile
-swc_compile(name, srcs, data, args, js_outs, map_outs, out_dir, output_dir, plugins, root_dir,
- source_maps, source_root, swcrc)
+swc_compile(name, srcs, data, args, dts_outs, emit_isolated_dts, js_outs, map_outs, out_dir,
+ output_dir, plugins, root_dir, source_maps, source_root, swcrc)
Underlying rule for the `swc` macro.
@@ -39,6 +39,8 @@ for example to set your own output labels for `js_outs`.
| srcs | source files, typically .ts files in the source tree | List of labels | required | |
| data | Runtime dependencies to include in binaries/tests that depend on this target.
Follows the same semantics as `js_library` `data` attribute. See https://docs.aspect.build/rulesets/aspect_rules_js/docs/js_library#data for more info. | List of labels | optional | `[]` |
| args | Additional arguments to pass to swcx cli (NOT swc!).
NB: this is not the same as the CLI arguments for @swc/cli npm package. For performance, rules_swc does not call a Node.js program wrapping the swc rust binding. Instead, we directly spawn the (somewhat experimental) native Rust binary shipped inside the @swc/core npm package, which the swc project calls "swcx" Tracking issue for feature parity: https://github.com/swc-project/swc/issues/4017 | List of strings | optional | `[]` |
+| dts_outs | list of expected TypeScript declaration files.
Can be empty, meaning no dts files should be produced. If non-empty, there should be one for each entry in srcs. | List of labels | optional | `[]` |
+| emit_isolated_dts | Emit .d.ts files instead of .js for TypeScript sources
EXPERIMENTAL: this API is undocumented, experimental and may change without notice | Boolean | optional | `False` |
| js_outs | list of expected JavaScript output files.
There should be one for each entry in srcs. | 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 should be one for each entry in srcs. | List of labels | optional | `[]` |
| out_dir | With output_dir=False, output files will have this directory prefix.
With output_dir=True, this is the name of the output directory. | String | optional | `""` |
diff --git a/e2e/emit_types/.bazelrc b/e2e/emit_types/.bazelrc
new file mode 100644
index 0000000..413c995
--- /dev/null
+++ b/e2e/emit_types/.bazelrc
@@ -0,0 +1,15 @@
+# Import Aspect bazelrc presets
+try-import %workspace%/../../.aspect/bazelrc/bazel7.bazelrc
+import %workspace%/../../.aspect/bazelrc/convenience.bazelrc
+import %workspace%/../../.aspect/bazelrc/correctness.bazelrc
+import %workspace%/../../.aspect/bazelrc/debug.bazelrc
+import %workspace%/../../.aspect/bazelrc/javascript.bazelrc
+import %workspace%/../../.aspect/bazelrc/performance.bazelrc
+
+### YOUR PROJECT SPECIFIC OPTIONS GO HERE ###
+
+# Load any settings & overrides specific to the current user from `.aspect/bazelrc/user.bazelrc`.
+# This file should appear in `.gitignore` so that settings are not shared with team members. This
+# should be last statement in this config so the user configuration is able to overwrite flags from
+# this file. See https://bazel.build/configure/best-practices#bazelrc-file.
+try-import %workspace%/../../.aspect/bazelrc/user.bazelrc
diff --git a/e2e/emit_types/.bazelversion b/e2e/emit_types/.bazelversion
new file mode 120000
index 0000000..96cf949
--- /dev/null
+++ b/e2e/emit_types/.bazelversion
@@ -0,0 +1 @@
+../../.bazelversion
\ No newline at end of file
diff --git a/e2e/emit_types/BUILD.bazel b/e2e/emit_types/BUILD.bazel
new file mode 100644
index 0000000..6d5fd55
--- /dev/null
+++ b/e2e/emit_types/BUILD.bazel
@@ -0,0 +1,34 @@
+load("@aspect_rules_swc//swc:defs.bzl", "swc")
+load("@bazel_skylib//rules:build_test.bzl", "build_test")
+
+swc(
+ name = "compile",
+ srcs = [
+ "a.ts",
+ "b.ts",
+ ],
+)
+
+swc(
+ name = "compile_emit_dts",
+ srcs = [
+ "a.ts",
+ "b.ts",
+ ],
+ emit_isolated_dts = True,
+ out_dir = "dts_out",
+)
+
+build_test(
+ name = "test",
+ targets = [
+ ":compile",
+ ":compile_emit_dts",
+ "a.js",
+ "b.js",
+ "dts_out/a.js",
+ "dts_out/b.js",
+ "dts_out/a.d.ts",
+ "dts_out/b.d.ts",
+ ],
+)
diff --git a/e2e/emit_types/MODULE.bazel b/e2e/emit_types/MODULE.bazel
new file mode 100644
index 0000000..7f8a854
--- /dev/null
+++ b/e2e/emit_types/MODULE.bazel
@@ -0,0 +1,14 @@
+bazel_dep(name = "aspect_rules_swc", version = "0.0.0", dev_dependency = True)
+local_path_override(
+ module_name = "aspect_rules_swc",
+ path = "../..",
+)
+
+bazel_dep(name = "bazel_skylib", version = "1.5.0", dev_dependency = True)
+
+# Use a recent swc version which includes the experimental emitIsolatedDts feature.
+swc = use_extension("@aspect_rules_swc//swc:extensions.bzl", "swc", dev_dependency = True)
+swc.toolchain(
+ name = "swc",
+ swc_version = "v1.6.6",
+)
diff --git a/e2e/emit_types/WORKSPACE.bazel b/e2e/emit_types/WORKSPACE.bazel
new file mode 100644
index 0000000..f2395b6
--- /dev/null
+++ b/e2e/emit_types/WORKSPACE.bazel
@@ -0,0 +1,31 @@
+# Override http_archive for local testing
+local_repository(
+ name = "aspect_rules_swc",
+ path = "../..",
+)
+
+#---SNIP--- Below here is re-used in the snippet published on releases
+
+###################
+# rules_swc setup #
+###################
+
+# Fetches the rules_swc dependencies.
+# If you want to have a different version of some dependency,
+# you should fetch it *before* calling this.
+# Alternatively, you can skip calling this function, so long as you've
+# already fetched all the dependencies.
+load("@aspect_rules_swc//swc:dependencies.bzl", "rules_swc_dependencies")
+
+rules_swc_dependencies()
+
+# Fetches a SWC cli from
+# https://github.com/swc-project/swc/releases
+# If you'd rather compile it from source, you can use rules_rust, fetch the project,
+# then register the toolchain yourself. (Note, this is not yet documented)
+load("@aspect_rules_swc//swc:repositories.bzl", "swc_register_toolchains")
+
+swc_register_toolchains(
+ name = "swc",
+ swc_version = "v1.6.6",
+)
diff --git a/e2e/emit_types/WORKSPACE.bzlmod b/e2e/emit_types/WORKSPACE.bzlmod
new file mode 100644
index 0000000..a9d1093
--- /dev/null
+++ b/e2e/emit_types/WORKSPACE.bzlmod
@@ -0,0 +1 @@
+# This file replaces `WORKSPACE.bazel` when --enable_bzlmod is set.
diff --git a/e2e/emit_types/a.ts b/e2e/emit_types/a.ts
new file mode 100644
index 0000000..f0cf013
--- /dev/null
+++ b/e2e/emit_types/a.ts
@@ -0,0 +1,8 @@
+export interface Foo {
+ name: string;
+}
+
+export const A = 1;
+export const AF: Foo = {
+ name: "bar",
+};
diff --git a/e2e/emit_types/b.ts b/e2e/emit_types/b.ts
new file mode 100644
index 0000000..cf59416
--- /dev/null
+++ b/e2e/emit_types/b.ts
@@ -0,0 +1,6 @@
+import { A, Foo } from "./a";
+
+export const B: typeof A = 1;
+export const BF: Foo = {
+ name: "baz",
+};
diff --git a/swc/defs.bzl b/swc/defs.bzl
index a15e7ad..4ca7656 100644
--- a/swc/defs.bzl
+++ b/swc/defs.bzl
@@ -100,10 +100,12 @@ def swc(name, srcs, args = [], data = [], plugins = [], output_dir = False, swcr
# Determine js & map outputs
js_outs = []
map_outs = []
+ dts_outs = []
if not output_dir:
js_outs = _swc_lib.calculate_js_outs(srcs, out_dir, root_dir)
map_outs = _swc_lib.calculate_map_outs(srcs, source_maps, out_dir, root_dir)
+ dts_outs = _swc_lib.calculate_dts_outs(srcs, kwargs.get("emit_isolated_dts", False), out_dir, root_dir)
swc_compile(
name = name,
@@ -111,6 +113,7 @@ def swc(name, srcs, args = [], data = [], plugins = [], output_dir = False, swcr
plugins = plugins,
js_outs = js_outs,
map_outs = map_outs,
+ dts_outs = dts_outs,
output_dir = output_dir,
source_maps = source_maps,
args = args,
diff --git a/swc/private/swc.bzl b/swc/private/swc.bzl
index 353d723..045c0c9 100644
--- a/swc/private/swc.bzl
+++ b/swc/private/swc.bzl
@@ -66,6 +66,13 @@ https://docs.aspect.build/rulesets/aspect_rules_js/docs/js_library#data for more
"root_dir": attr.string(
doc = "a subdirectory under the input package which should be consider the root directory of all the input files",
),
+ "emit_isolated_dts": attr.bool(
+ doc = """Emit .d.ts files instead of .js for TypeScript sources
+
+EXPERIMENTAL: this API is undocumented, experimental and may change without notice
+""",
+ default = False,
+ ),
}
_outputs = {
@@ -75,6 +82,10 @@ There should be one for each entry in srcs."""),
"map_outs": attr.output_list(doc = """list of expected source map output files.
Can be empty, meaning no source maps should be produced.
+If non-empty, there should be one for each entry in srcs."""),
+ "dts_outs": attr.output_list(doc = """list of expected TypeScript declaration files.
+
+Can be empty, meaning no dts files should be produced.
If non-empty, there should be one for each entry in srcs."""),
}
@@ -184,6 +195,21 @@ def _calculate_map_outs(srcs, source_maps, out_dir, root_dir):
out.append(map_out)
return out
+def _to_dts_out(src, emit_isolated_dts, out_dir, root_dir):
+ if not _is_supported_src(src) or not emit_isolated_dts:
+ return None
+ dts_out = src[:src.rindex(".")] + ".d.ts"
+ dts_out = _to_out_path(dts_out, out_dir, root_dir)
+ return dts_out
+
+def _calculate_dts_outs(srcs, emit_isolated_dts, out_dir, root_dir):
+ out = []
+ for f in srcs:
+ dts_out = _to_dts_out(f, emit_isolated_dts, out_dir, root_dir)
+ if dts_out:
+ out.append(dts_out)
+ return out
+
def _calculate_source_file(ctx, src):
if not (ctx.attr.out_dir or ctx.attr.root_dir):
return src.basename
@@ -259,6 +285,15 @@ def _swc_impl(ctx):
inputs.extend(ctx.files.plugins)
args.add_all(plugin_args)
+ if ctx.attr.emit_isolated_dts:
+ args.add_all(["--config-json", json.encode({
+ "jsc": {
+ "experimental": {
+ "emitIsolatedDts": True,
+ },
+ },
+ })])
+
if ctx.attr.output_dir:
if len(ctx.attr.srcs) != 1:
fail("Under output_dir, there must be a single entry in srcs")
@@ -309,13 +344,16 @@ def _swc_impl(ctx):
continue
js_out = ctx.actions.declare_file(js_out_path)
outputs = [js_out]
- map_out_path = _to_map_out(src_path, ctx.attr.source_maps, ctx.attr.out_dir, ctx.attr.root_dir)
+ map_out_path = _to_map_out(src_path, ctx.attr.source_maps, ctx.attr.out_dir, ctx.attr.root_dir)
if map_out_path:
js_map_out = ctx.actions.declare_file(map_out_path)
outputs.append(js_map_out)
- src_inputs = [src] + inputs
+ dts_out_path = _to_dts_out(src_path, ctx.attr.emit_isolated_dts, ctx.attr.out_dir, ctx.attr.root_dir)
+ if dts_out_path:
+ dts_out = ctx.actions.declare_file(dts_out_path)
+ outputs.append(dts_out)
src_args.add("--out-file", js_out)
@@ -324,7 +362,7 @@ def _swc_impl(ctx):
_swc_action(
ctx,
swc_toolchain.swcinfo.swc_binary,
- inputs = src_inputs,
+ inputs = [src] + inputs,
arguments = [
args,
src_args,
@@ -385,4 +423,5 @@ swc = struct(
SUPPORTED_EXTENSIONS = _SUPPORTED_EXTENSIONS,
calculate_js_outs = _calculate_js_outs,
calculate_map_outs = _calculate_map_outs,
+ calculate_dts_outs = _calculate_dts_outs,
)