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 a2d6cd6 commit 8ad65af
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 38 deletions.
41 changes: 38 additions & 3 deletions examples/no_emit/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load("@aspect_rules_ts//ts:defs.bzl", "ts_project")
load("@bazel_skylib//rules:build_test.bzl", "build_test")
load("@bazel_skylib//rules:write_file.bzl", "write_file")

write_file(
Expand All @@ -9,12 +10,46 @@ 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,
tsconfig = {
"compilerOptions": {
"noEmit": True,
},
},
)

# Shows how to run `tsc` producing no outputs at all via declarations=False.
ts_project(
name = "typecheck_nodeclarations_js",
srcs = ["b.js"],
out_dir = "typecheck_nodeclarations_js",
tsconfig = {
"compilerOptions": {
"allowJs": True,
"noEmit": True,
},
},
)

build_test(
name = "targets_test",
targets = [
# Ensure the _typecheck targets are declared despite no outputs.
":typecheck_only_noemit_typecheck",
":typecheck_nodeclarations_js_typecheck",
],
)
47 changes: 46 additions & 1 deletion examples/transpiler/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
load("@aspect_rules_ts//ts:defs.bzl", "ts_project")
load("@bazel_skylib//rules:build_test.bzl", "build_test")

# Note, Bazel 6 starlark has lambda so maybe we can stop using partial
load("@bazel_skylib//rules:write_file.bzl", "write_file")
Expand All @@ -19,6 +20,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,7 +40,7 @@ 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(
Expand All @@ -45,3 +54,39 @@ 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,
},
},
)

build_test(
name = "targets_test",
targets = [
# babel ts_project
":babel",
":babel_typecheck",
# babel outputted js
"big.js", # NOTE: does not implement out_dir in this test
# tsc outputted dts
"build-babel/big.d.ts",

# no-emit for type-checking
":no-emit",
":no-emit_typecheck",

# babel outputted .js with no declarations
":no-declarations",
":no-declarations_typecheck",
"a.js", # NOTE: does not implement out_dir in this test
],
)
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
use_tsc_noemit = 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 use_tsc_noemit:
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 use_tsc_noemit:
# 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 8ad65af

Please sign in to comment.