Skip to content

Commit

Permalink
feat: support ts_project for only type-checking
Browse files Browse the repository at this point in the history
Possible scenarios include: noEmit, transpiler+noEmitDeclarations.
  • Loading branch information
jbedard committed Sep 20, 2024
1 parent 4f26edc commit cee5c51
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 48 deletions.
25 changes: 23 additions & 2 deletions examples/no_emit/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,33 @@ write_file(
],
)

# Shows how to run `tsc` producing no outputs at all.
write_file(
name = "gen_js",
out = "b.js",
content = [
"export const b = 43",
],
)

# Shows how to run `tsc` producing no outputs at all via no_emit=True.
# It will use a validation action to type-check the file:
# https://bazel.build/extending/rules#validation_actions
# Run bazel with --norun_validations to skip type-checking.
ts_project(
name = "typecheck_only",
name = "typecheck_only_noemit",
srcs = ["a.ts"],
no_emit = True,
)

# Shows how to run `tsc` producing no outputs at all via declarations=False.
ts_project(
name = "typecheck_only_noemitdeclarations",
srcs = ["b.js"],
tsconfig = {
"compilerOptions": {
"allowJs": True,
"noEmit": True,
"declaration": False,
},
},
)
26 changes: 24 additions & 2 deletions examples/transpiler/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ write_file(
],
)

write_file(
name = "gen_a",
out = "a.ts",
content = [
"export const b: number = 43",
],
)

# Runs babel to transpile ts -> js
# and tsc to type-check
ts_project(
Expand All @@ -31,12 +39,12 @@ ts_project(
)

# Runs babel to transpile ts -> js
# and does not produce any declaration outputs.
# and does not produce any declaration outputs due to noEmit=True.
# `tsc` is used for type-check only, as a validation action
# (run bazel with --norun_validations to skip typechecking)
ts_project(
name = "no-emit",
srcs = ["big.ts"],
srcs = ["a.ts"],
no_emit = True,
transpiler = babel,
tsconfig = {
Expand All @@ -45,3 +53,17 @@ ts_project(
},
},
)

# Runs babel to transpile ts -> js
# and does not produce any declaration outputs due to declaration=False.
ts_project(
name = "no-declarations",
srcs = ["a.ts"],
out_dir = "build-nodecls",
transpiler = babel,
tsconfig = {
"compilerOptions": {
"declaration": False,
},
},
)
20 changes: 10 additions & 10 deletions ts/private/ts_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -136,16 +136,16 @@ def _write_tsconfig_rule(ctx):
content = content.replace("__extends__", extends_path)

filtered_files = _filter_input_files(ctx.files.files, ctx.attr.allow_js, ctx.attr.resolve_json_module)
if filtered_files:
# Update file paths to be relative to the tsconfig file, including a ./ prefix
# to ensure paths are all relative to the config file.
package_prefix = ctx.label.package + "/"
filtered_files = [
"./" + f.short_path.removeprefix(package_prefix)
for f in filtered_files
]

content = content.replace("\"__files__\"", str(filtered_files))

# Update file paths to be relative to the tsconfig file, including a ./ prefix
# to ensure paths are all relative to the config file.
package_prefix = ctx.label.package + "/"
filtered_files = [
"./" + f.short_path.removeprefix(package_prefix)
for f in filtered_files
]

content = content.replace("\"__files__\"", str(filtered_files))
ctx.actions.write(
output = ctx.outputs.out,
content = content,
Expand Down
37 changes: 3 additions & 34 deletions ts/private/ts_project.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -177,43 +177,12 @@ See https://github.com/aspect-build/rules_ts/issues/361 for more details.

typings_srcs = [s for s in srcs_inputs if _lib.is_typings_src(s.path)]

if len(js_outs) + len(typings_outs) < 1 and not ctx.attr.no_emit:
label = "//{}:{}".format(ctx.label.package, ctx.label.name)
if len(typings_srcs) > 0:
no_outs_msg = """ts_project target {target} only has typings in srcs.
Since there is no `tsc` action to perform, there are no generated outputs.
For "typecheck-only" the TypeScript 'noEmit' feature can be used.
For grouping typings this should be changed to js_library, which can be done by running:
buildozer 'new_load @aspect_rules_js//js:defs.bzl js_library' //{pkg}:__pkg__
buildozer 'set kind js_library' {target}
buildozer 'remove declaration' {target}
""".format(
target = label,
pkg = ctx.label.package,
)
elif ctx.attr.transpile != 0:
no_outs_msg = """ts_project target %s is configured to produce no outputs.
This might be because
- you configured it with `noEmit`
- the `srcs` are empty
- `srcs` has elements producing non-ts outputs
""" % label
else:
no_outs_msg = "ts_project target %s with custom transpiler needs 'declaration = True'." % label
fail(no_outs_msg + """
This is an error because Bazel does not run actions unless their outputs are needed for the requested targets to build.
""")

# Make sure the user has acknowledged that transpiling is slow
if len(outputs) > 0 and ctx.attr.transpile == -1 and not ctx.attr.emit_declaration_only and not options.default_to_tsc_transpiler:
fail(transpiler_selection_required)

output_types = typings_outs + typing_maps_outs + typings_srcs
no_emit = ctx.attr.no_emit or (ctx.attr.transpile == 0 and not ctx.attr.declaration)

# Default outputs (DefaultInfo files) is what you see on the command-line for a built
# library, and determines what files are used by a simple non-provider-aware downstream
Expand All @@ -225,7 +194,7 @@ This is an error because Bazel does not run actions unless their outputs are nee
else:
# We must avoid tsc writing any JS files in this case, as tsc was only run for typings, and some other
# action will try to write the JS files. We must avoid collisions where two actions write the same file.
if not ctx.attr.no_emit:
if not no_emit:
arguments.add("--emitDeclarationOnly")

# We don't produce any DefaultInfo outputs in this case, because we avoid running the tsc action
Expand All @@ -236,7 +205,7 @@ This is an error because Bazel does not run actions unless their outputs are nee

stdout_file = ""

if ctx.attr.no_emit:
if no_emit:
# The type-checking action still need to produce some output, so we output the stdout
# to a .typecheck file that ends up in the typecheck output group.
typecheck_output = ctx.actions.declare_file(ctx.attr.name + ".typecheck")
Expand Down

0 comments on commit cee5c51

Please sign in to comment.