From cee5c511ea38163e01bf7d56ad0c10b76863c889 Mon Sep 17 00:00:00 2001 From: Jason Bedard Date: Fri, 20 Sep 2024 01:11:10 -0700 Subject: [PATCH] feat: support ts_project for only type-checking Possible scenarios include: noEmit, transpiler+noEmitDeclarations. --- examples/no_emit/BUILD.bazel | 25 ++++++++++++++++++++-- examples/transpiler/BUILD.bazel | 26 +++++++++++++++++++++-- ts/private/ts_config.bzl | 20 +++++++++--------- ts/private/ts_project.bzl | 37 +++------------------------------ 4 files changed, 60 insertions(+), 48 deletions(-) diff --git a/examples/no_emit/BUILD.bazel b/examples/no_emit/BUILD.bazel index ceebf058..24bd30e5 100644 --- a/examples/no_emit/BUILD.bazel +++ b/examples/no_emit/BUILD.bazel @@ -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, + }, + }, +) diff --git a/examples/transpiler/BUILD.bazel b/examples/transpiler/BUILD.bazel index b047aabd..a358f904 100644 --- a/examples/transpiler/BUILD.bazel +++ b/examples/transpiler/BUILD.bazel @@ -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( @@ -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 = { @@ -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, + }, + }, +) diff --git a/ts/private/ts_config.bzl b/ts/private/ts_config.bzl index 5637e360..29e92742 100644 --- a/ts/private/ts_config.bzl +++ b/ts/private/ts_config.bzl @@ -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, diff --git a/ts/private/ts_project.bzl b/ts/private/ts_project.bzl index b386570f..ee466ba2 100644 --- a/ts/private/ts_project.bzl +++ b/ts/private/ts_project.bzl @@ -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 @@ -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 @@ -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")