Skip to content

Commit

Permalink
feat(typescript): allow alternative transpilers
Browse files Browse the repository at this point in the history
Fixes #3133
  • Loading branch information
alexeagle committed Dec 22, 2021
1 parent dfa2ae8 commit c9cfc1a
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 6 deletions.
35 changes: 32 additions & 3 deletions docs/TypeScript.md
Original file line number Diff line number Diff line change
Expand Up @@ -474,9 +474,9 @@ Defaults to `False`
<pre>
ts_project(<a href="#ts_project-name">name</a>, <a href="#ts_project-tsconfig">tsconfig</a>, <a href="#ts_project-srcs">srcs</a>, <a href="#ts_project-args">args</a>, <a href="#ts_project-deps">deps</a>, <a href="#ts_project-extends">extends</a>, <a href="#ts_project-allow_js">allow_js</a>, <a href="#ts_project-declaration">declaration</a>, <a href="#ts_project-source_map">source_map</a>,
<a href="#ts_project-declaration_map">declaration_map</a>, <a href="#ts_project-resolve_json_module">resolve_json_module</a>, <a href="#ts_project-preserve_jsx">preserve_jsx</a>, <a href="#ts_project-composite">composite</a>, <a href="#ts_project-incremental">incremental</a>,
<a href="#ts_project-emit_declaration_only">emit_declaration_only</a>, <a href="#ts_project-ts_build_info_file">ts_build_info_file</a>, <a href="#ts_project-tsc">tsc</a>, <a href="#ts_project-typescript_package">typescript_package</a>,
<a href="#ts_project-typescript_require_path">typescript_require_path</a>, <a href="#ts_project-validate">validate</a>, <a href="#ts_project-supports_workers">supports_workers</a>, <a href="#ts_project-declaration_dir">declaration_dir</a>, <a href="#ts_project-out_dir">out_dir</a>, <a href="#ts_project-root_dir">root_dir</a>,
<a href="#ts_project-link_workspace_root">link_workspace_root</a>, <a href="#ts_project-kwargs">kwargs</a>)
<a href="#ts_project-emit_declaration_only">emit_declaration_only</a>, <a href="#ts_project-transpiler">transpiler</a>, <a href="#ts_project-transpiler_kwargs">transpiler_kwargs</a>, <a href="#ts_project-ts_build_info_file">ts_build_info_file</a>, <a href="#ts_project-tsc">tsc</a>,
<a href="#ts_project-typescript_package">typescript_package</a>, <a href="#ts_project-typescript_require_path">typescript_require_path</a>, <a href="#ts_project-validate">validate</a>, <a href="#ts_project-supports_workers">supports_workers</a>, <a href="#ts_project-declaration_dir">declaration_dir</a>,
<a href="#ts_project-out_dir">out_dir</a>, <a href="#ts_project-root_dir">root_dir</a>, <a href="#ts_project-link_workspace_root">link_workspace_root</a>, <a href="#ts_project-kwargs">kwargs</a>)
</pre>

Compiles one TypeScript project using `tsc --project`
Expand Down Expand Up @@ -733,6 +733,35 @@ Instructs Bazel *not* to expect `.js` or `.js.map` outputs for `.ts` sources.
Defaults to `False`
<h4 id="ts_project-transpiler">transpiler</h4>
What tool to run that produces the JavaScript outputs.
By default, this is the string `tsc` which means to produce `.js` outputs
in the same action that does the type-checking to produce `.d.ts` outputs.
This is the simplest configuration, however `tsc` is slower than alternatives.
It also means developers must wait for the type-checking in the developer loop.
In theory, Persistent Workers (via the `supports_workers` attribute) remedies the
slow compilation time, however it adds additional complexity because the worker process
can only see one set of dependencies, and so it cannot be shared between different
`ts_project` rules. That attribute is documented as experimental, and may never graduate
to a better support contract.
Instead, you can pass a rule or macro that accepts these arguments:
`(name, srcs, js_outs, map_outs, args, data, tags, visibility)`
The rules_nodejs authors believe that [SWC](https://swc.rs) is a great choice.
Defaults to `"tsc"`
<h4 id="ts_project-transpiler_kwargs">transpiler_kwargs</h4>
if the `transpiler` attribute is a rule or macro, then this dictionary
is passed as additional keyword arguments of that rule or macro.
For example if the transpiler accepts `args`, then use `transpiler_kwargs={"args": ["some-arg"]}`
Defaults to `{}`
<h4 id="ts_project-ts_build_info_file">ts_build_info_file</h4>
the user-specified value of `tsBuildInfoFile` from the tsconfig.
Expand Down
55 changes: 52 additions & 3 deletions packages/typescript/internal/ts_project.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ def _ts_project_impl(ctx):
else:
json_outs = []

# If there are no js_outs, that implies we are producing declarations only.
# We must avoid tsc writing any JS files in this case, as it was only run for typings.
arguments.add("--emitDeclarationOnly")

outputs = json_outs + ctx.outputs.js_outs + ctx.outputs.map_outs + ctx.outputs.typings_outs + ctx.outputs.typing_maps_outs
if ctx.outputs.buildinfo_out:
arguments.add_all([
Expand Down Expand Up @@ -367,6 +371,8 @@ def ts_project_macro(
composite = False,
incremental = False,
emit_declaration_only = False,
transpiler = "tsc",
transpiler_kwargs = {},
ts_build_info_file = None,
tsc = None,
typescript_package = _DEFAULT_TYPESCRIPT_PACKAGE,
Expand Down Expand Up @@ -540,6 +546,27 @@ def ts_project_macro(
args: List of strings of additional command-line arguments to pass to tsc.
transpiler: What tool to run that produces the JavaScript outputs.
By default, this is the string `tsc` which means to produce `.js` outputs
in the same action that does the type-checking to produce `.d.ts` outputs.
This is the simplest configuration, however `tsc` is slower than alternatives.
It also means developers must wait for the type-checking in the developer loop.
In theory, Persistent Workers (via the `supports_workers` attribute) remedies the
slow compilation time, however it adds additional complexity because the worker process
can only see one set of dependencies, and so it cannot be shared between different
`ts_project` rules. That attribute is documented as experimental, and may never graduate
to a better support contract.
Instead, you can pass a rule or macro that accepts these arguments:
`(name, srcs, js_outs, map_outs, args, data, tags, visibility)`
The rules_nodejs authors believe that [SWC](https://swc.rs) is a great choice.
transpiler_kwargs: if the `transpiler` attribute is a rule or macro, then this dictionary
is passed as additional keyword arguments of that rule or macro.
For example if the transpiler accepts `args`, then use `transpiler_kwargs={"args": ["some-arg"]}`
tsc: Label of the TypeScript compiler binary to run.
For example, `tsc = "@my_deps//typescript/bin:tsc"`
Expand Down Expand Up @@ -756,7 +783,29 @@ def ts_project_macro(
if declaration_map:
typing_maps_outs.extend(_out_paths(srcs, typings_out_dir, root_dir, allow_js, {"*": ".d.ts.map"}))

if not len(js_outs) and not len(typings_outs):
if type(transpiler) == "function" or type(transpiler) == "rule":
tsc_js_outs = []
tsc_map_outs = []
transpiler(
name = name + "_transpile",
srcs = srcs,
js_outs = js_outs,
map_outs = map_outs,
data = [],
tags = kwargs.get("tags", []),
visibility = kwargs.get("visibility", None),
testonly = kwargs.get("testonly", None),
**transpiler_kwargs
)
elif transpiler == "tsc":
tsc_js_outs = js_outs
tsc_map_outs = map_outs
if len(transpiler_kwargs.keys()):
fail("transpiler_kwargs should not be used with `transpiler='tsc'`, just pass them to the `args`")
else:
fail("transpiler attribute should be a rule/macro or the string 'tsc'.")

if not len(tsc_js_outs) and not len(typings_outs):
fail("""ts_project target "//{}:{}" is configured to produce no outputs.
Note that ts_project must know the srcs in advance in order to predeclare the outputs.
Expand All @@ -773,8 +822,8 @@ Check the srcs attribute to see that some .ts files are present (or .js files wi
declaration_dir = declaration_dir,
out_dir = out_dir,
root_dir = root_dir,
js_outs = js_outs,
map_outs = map_outs,
js_outs = tsc_js_outs,
map_outs = tsc_map_outs,
typings_outs = typings_outs,
typing_maps_outs = typing_maps_outs,
buildinfo_out = tsbuildinfo_path if composite or incremental else None,
Expand Down
28 changes: 28 additions & 0 deletions packages/typescript/test/ts_project/swc/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
load("@bazel_skylib//rules:write_file.bzl", "write_file")
load("//packages/typescript:index.bzl", "ts_project")
load(":mock_swc.bzl", "swc")

write_file(
name = "gen_ts",
out = "big.ts",
content = [
"export const a{0}: number = {0}".format(x)
for x in range(1000)
],
)

ts_project(
name = "transpile_with_swc",
srcs = ["big.ts"],
transpiler = swc,
transpiler_kwargs = {
"args": ["--env-name=test"],
"swcrc": ".swcrc",
},
tsconfig = {
"compilerOptions": {
"declaration": True,
"sourceMap": True,
},
},
)
18 changes: 18 additions & 0 deletions packages/typescript/test/ts_project/swc/mock_swc.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"Mock swc transpiler macro"

load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
load("@bazel_skylib//rules:write_file.bzl", "write_file")

def swc(name, srcs, js_outs, map_outs, **kwargs):
for i, s in enumerate(srcs):
copy_file(
name = "_{}_{}_js".format(name, s),
src = s,
out = js_outs[i],
)

write_file(
name = "_{}_{}_map".format(name, s),
out = map_outs[i],
content = ["""{"version":3,"sources":["%s"],"mappings":"AAAO,KAAK,CAAC","file":"in.js","sourcesContent":["fake"]}""" % s],
)

0 comments on commit c9cfc1a

Please sign in to comment.