Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add emitIsolatedDts support #270

Merged
merged 1 commit into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
docs/*.md
examples/pnpm-lock.yaml
**/expected.js
**/expected.cjs
**/expected.*
**/expected_tsc
6 changes: 4 additions & 2 deletions docs/swc.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

96 changes: 96 additions & 0 deletions examples/emit_types/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_files")
load("@aspect_rules_swc//swc:defs.bzl", "swc")
load("@bazel_skylib//rules:build_test.bzl", "build_test")

# No root/out
swc(
name = "emit_dts",
srcs = [
"src/a.ts",
"src/b.ts",
],
emit_isolated_dts = True,
)

build_test(
name = "emit_dts-test",
targets = [
"src/a.js",
"src/a.d.ts",
"src/b.js",
"src/b.d.ts",
],
)

# With out_dir
swc(
name = "emit_dts_outdir",
srcs = [
"src/a.ts",
"src/b.ts",
],
emit_isolated_dts = True,
out_dir = "out",
)

build_test(
name = "emit_dts_outdir-test",
targets = [
"out/src/a.js",
"out/src/a.d.ts",
"out/src/b.js",
"out/src/b.d.ts",
],
)

# With root_dir
swc(
name = "emit_dts_rootdir",
srcs = [
"src/a.ts",
"src/b.ts",
],
emit_isolated_dts = True,
root_dir = "src",
)

build_test(
name = "emit_dts_rootdir-test",
targets = [
"a.js",
"a.d.ts",
"b.js",
"b.d.ts",
],
)

# With out_dir and root_dir
swc(
name = "emit_dts_outdir_rootdir",
srcs = [
"src/a.ts",
"src/b.ts",
],
emit_isolated_dts = True,
out_dir = "out_root",
root_dir = "src",
)

build_test(
name = "emit_dts_outdir_rootdir-test",
targets = [
"out_root/a.js",
"out_root/a.d.ts",
"out_root/b.js",
"out_root/b.d.ts",
],
)

# Assert the output files are correct, have not changed etc.
write_source_files(
name = "outputs_test",
files = {
"expected.a.d.ts": "src/a.d.ts",
"expected.b.d.ts": "src/b.d.ts",
},
)
8 changes: 8 additions & 0 deletions examples/emit_types/expected.a.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* A basic interface */ export interface Foo {
// teh name!
name: string;
}
// Implicit type
export declare const A: number;
// Explicit type
export declare const AF: Foo;
5 changes: 5 additions & 0 deletions examples/emit_types/expected.b.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Imported and used as types
import { A, Foo } from "./a";
// Use of imported types
export declare const B: typeof A;
/** Another use of imported types */ export declare const BF: Foo;
13 changes: 13 additions & 0 deletions examples/emit_types/src/a.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* A basic interface */
export interface Foo {
// teh name!
name: string;
}

// Implicit type
export const A = 1;

// Explicit type
export const AF: Foo = {
name: "bar",
};
10 changes: 10 additions & 0 deletions examples/emit_types/src/b.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Imported and used as types
import { A, Foo } from "./a";

// Use of imported types
export const B: typeof A = 1;

/** Another use of imported types */
export const BF: Foo = {
name: "baz",
};
3 changes: 3 additions & 0 deletions swc/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,20 @@ 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,
srcs = srcs,
plugins = plugins,
js_outs = js_outs,
map_outs = map_outs,
dts_outs = dts_outs,
output_dir = output_dir,
source_maps = source_maps,
args = args,
Expand Down
61 changes: 56 additions & 5 deletions swc/private/swc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -75,12 +82,19 @@ 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."""),
}

def _is_ts_src(src):
return src.endswith(".ts") or src.endswith(".mts") or src.endswith(".cts") or src.endswith(".tsx") or src.endswith(".jsx")

def _is_typings_src(src):
return src.endswith(".d.ts") or src.endswith(".d.mts") or src.endswith(".d.cts")

def _is_js_src(src):
return src.endswith(".mjs") or src.endswith(".cjs") or src.endswith(".js")

Expand Down Expand Up @@ -112,7 +126,7 @@ def _remove_extension(f):
return f if i <= 0 else f[:-(len(f) - i)]

def _to_js_out(src, out_dir, root_dir, js_outs = []):
if not _is_supported_src(src):
if not _is_supported_src(src) or _is_typings_src(src):
return None

exts = {
Expand Down Expand Up @@ -153,7 +167,7 @@ def _calculate_js_outs(srcs, out_dir, root_dir):
def _to_map_out(src, source_maps, out_dir, root_dir):
if source_maps == "false" or source_maps == "inline":
return None
if not _is_supported_src(src):
if not _is_supported_src(src) or _is_typings_src(src):
return None
exts = {
".mts": ".mjs.map",
Expand All @@ -177,6 +191,23 @@ 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 emit_isolated_dts:
return None
if not _is_supported_src(src) or _is_typings_src(src):
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
Expand Down Expand Up @@ -252,6 +283,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({
jbedard marked this conversation as resolved.
Show resolved Hide resolved
"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")
Expand Down Expand Up @@ -296,19 +336,29 @@ def _swc_impl(ctx):

src_path = _relative_to_package(src.path, ctx)

# This source file is a typings file and not transpiled
if _is_typings_src(src_path):
# Copy to the output directory if emitting dts files is enabled
if ctx.attr.emit_isolated_dts:
output_sources.append(src)
continue

js_out_path = _to_js_out(src_path, ctx.attr.out_dir, ctx.attr.root_dir, js_outs_relative)
if not js_out_path:
# This source file is not a supported src
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)

Expand All @@ -317,7 +367,7 @@ def _swc_impl(ctx):
_swc_action(
ctx,
swc_toolchain.swcinfo.swc_binary,
inputs = src_inputs,
inputs = [src] + inputs,
arguments = [
args,
src_args,
Expand Down Expand Up @@ -377,4 +427,5 @@ swc = struct(
toolchains = ["@aspect_rules_swc//swc:toolchain_type"],
calculate_js_outs = _calculate_js_outs,
calculate_map_outs = _calculate_map_outs,
calculate_dts_outs = _calculate_dts_outs,
)
Loading