diff --git a/docs/rules.md b/docs/rules.md
index aa5f71f4..526bc822 100644
--- a/docs/rules.md
+++ b/docs/rules.md
@@ -37,10 +37,10 @@ extended configuration file as well, to pass them both to the TypeScript compile
ts_project_rule(name, deps, srcs, data, allow_js, args, assets, buildinfo_out, composite,
declaration, declaration_dir, declaration_map, emit_declaration_only, extends,
- incremental, is_typescript_5_or_greater, js_outs, map_outs, out_dir, preserve_jsx,
- resolve_json_module, resource_set, root_dir, source_map, supports_workers, transpile,
- ts_build_info_file, tsc, tsc_worker, tsconfig, typing_maps_outs, typings_outs,
- validate, validator)
+ incremental, is_typescript_5_or_greater, isolated_declarations, js_outs, map_outs,
+ out_dir, preserve_jsx, resolve_json_module, resource_set, root_dir, source_map,
+ supports_workers, transpile, ts_build_info_file, tsc, tsc_worker, tsconfig,
+ typing_maps_outs, typings_outs, validate, validator)
Implementation rule behind the ts_project macro.
@@ -70,6 +70,7 @@ for srcs and tsconfig, and pre-declaring output files.
| extends | https://www.typescriptlang.org/tsconfig#extends | Label | optional | `None` |
| incremental | https://www.typescriptlang.org/tsconfig#incremental | Boolean | optional | `False` |
| is_typescript_5_or_greater | Whether TypeScript version is >= 5.0.0 | Boolean | optional | `False` |
+| isolated_declarations | https://www.typescriptlang.org/tsconfig/#isolatedDeclarations | Boolean | optional | `False` |
| js_outs | Locations in bazel-out where tsc will write `.js` files | List of labels | optional | `[]` |
| map_outs | Locations in bazel-out where tsc will write `.js.map` files | List of labels | optional | `[]` |
| out_dir | https://www.typescriptlang.org/tsconfig#outDir | String | optional | `""` |
@@ -115,10 +116,10 @@ along with any transitively referenced tsconfig.json files chained by the
## ts_project
-ts_project(name, tsconfig, srcs, args, data, deps, assets, extends, allow_js, declaration,
- source_map, declaration_map, resolve_json_module, preserve_jsx, composite, incremental,
- emit_declaration_only, transpiler, ts_build_info_file, tsc, tsc_worker, validate,
- validator, declaration_dir, out_dir, root_dir, supports_workers, kwargs)
+ts_project(name, tsconfig, srcs, args, data, deps, assets, extends, allow_js, isolated_declarations,
+ declaration, source_map, declaration_map, resolve_json_module, preserve_jsx, composite,
+ incremental, emit_declaration_only, transpiler, ts_build_info_file, tsc, tsc_worker,
+ validate, validator, declaration_dir, out_dir, root_dir, supports_workers, kwargs)
Compiles one TypeScript project using `tsc --project`.
@@ -157,6 +158,7 @@ If you have problems getting your `ts_project` to work correctly, read the dedic
| assets | Files which are needed by a downstream build step such as a bundler.
These files are **not** included as inputs to any actions spawned by `ts_project`. They are not transpiled, and are not visible to the type-checker. Instead, these files appear among the *outputs* of this target.
A typical use is when your TypeScript code has an import that TS itself doesn't understand such as
`import './my.scss'`
and the type-checker allows this because you have an "ambient" global type declaration like
`declare module '*.scss' { ... }`
A bundler like webpack will expect to be able to resolve the `./my.scss` import to a file and doesn't care about the typing declaration. A bundler runs as a build step, so it does not see files included in the `data` attribute.
Note that `data` is used for files that are resolved by some binary, including a test target. Behind the scenes, `data` populates Bazel's Runfiles object in `DefaultInfo`, while this attribute populates the `transitive_sources` of the `JsInfo`. | `[]` |
| extends | Label of the tsconfig file referenced in the `extends` section of tsconfig To support "chaining" of more than one extended config, this label could be a target that provdes `TsConfigInfo` such as `ts_config`. | `None` |
| allow_js | Whether TypeScript will read .js and .jsx files. When used with `declaration`, TypeScript will generate `.d.ts` files from `.js` files. | `False` |
+| isolated_declarations | Whether to enforce that declaration output (.d.ts file) can be produced for a single source file at a time. Requires some additional explicit types on exported symbols. See https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-5.html#isolated-declarations | `False` |
| declaration | Whether the `declaration` bit is set in the tsconfig. Instructs Bazel to expect a `.d.ts` output for each `.ts` source. | `False` |
| source_map | Whether the `sourceMap` bit is set in the tsconfig. Instructs Bazel to expect a `.js.map` output for each `.ts` source. | `False` |
| declaration_map | Whether the `declarationMap` bit is set in the tsconfig. Instructs Bazel to expect a `.d.ts.map` output for each `.ts` source. | `False` |
diff --git a/examples/isolated_declarations/README.md b/examples/isolated_declarations/README.md
deleted file mode 100644
index ec8aefc8..00000000
--- a/examples/isolated_declarations/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# TypeScript isolated declarations
-
-When the `isolated_declarations` compiler option is used, it ensures that a declarations file `.d.ts` can be produced from a single source file at a time.
-
-See https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/#isolated-declarations
-in particular, the section "Use-case: Parallel Declaration Emit and Parallel Checking"
-which this example is based on.
-
-In this example, both `backend` and `frontend` packages depend on `core`, however they could be type-checked in parallel
-before `core` has produced any declarationEmit.
-
-This doesn't make anything faster yet.
-Follow https://github.com/aspect-build/rules_ts/issues/374 for updates on performance improvements to the Bazel action graph.
diff --git a/examples/isolated_declarations/backend/BUILD.bazel b/examples/isolated_declarations/backend/BUILD.bazel
index 1d5ff99d..f494f7e3 100644
--- a/examples/isolated_declarations/backend/BUILD.bazel
+++ b/examples/isolated_declarations/backend/BUILD.bazel
@@ -4,6 +4,7 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_project")
ts_project(
name = "backend",
declaration = True,
+ isolated_declarations = True,
tsconfig = "//examples/isolated_declarations:tsconfig",
deps = ["//examples/isolated_declarations/core"],
)
diff --git a/examples/isolated_declarations/core/BUILD.bazel b/examples/isolated_declarations/core/BUILD.bazel
index b338be5b..e56cb348 100644
--- a/examples/isolated_declarations/core/BUILD.bazel
+++ b/examples/isolated_declarations/core/BUILD.bazel
@@ -3,6 +3,7 @@ load("@aspect_rules_ts//ts:defs.bzl", "ts_project")
ts_project(
name = "core",
declaration = True,
+ isolated_declarations = True,
tsconfig = "//examples/isolated_declarations:tsconfig",
visibility = ["//examples/isolated_declarations:__subpackages__"],
)
diff --git a/examples/isolated_declarations/core/index.ts b/examples/isolated_declarations/core/index.ts
index 5b2b2b36..7dde0ba5 100644
--- a/examples/isolated_declarations/core/index.ts
+++ b/examples/isolated_declarations/core/index.ts
@@ -12,3 +12,9 @@ type UnionToIntersection = (U extends any ? (k: U) => void : never) extends (
type UnionType = { a: number } | { b: string } | { c: boolean }
export type IntersectionType = UnionToIntersection
+
+export const MyIntersectingType: IntersectionType = {
+ a: 1,
+ b: '2',
+ c: true,
+}
diff --git a/examples/isolated_declarations/frontend/BUILD.bazel b/examples/isolated_declarations/frontend/BUILD.bazel
index 0bfcc5d7..3eb38cd5 100644
--- a/examples/isolated_declarations/frontend/BUILD.bazel
+++ b/examples/isolated_declarations/frontend/BUILD.bazel
@@ -5,6 +5,7 @@ ts_project(
tsconfig = {
"compilerOptions": {
"declaration": True,
+ "isolatedDeclarations": True,
},
},
deps = ["//examples/isolated_declarations/core"],
diff --git a/ts/defs.bzl b/ts/defs.bzl
index aa53f75c..7cb58f9c 100644
--- a/ts/defs.bzl
+++ b/ts/defs.bzl
@@ -40,6 +40,7 @@ def ts_project(
assets = [],
extends = None,
allow_js = False,
+ isolated_declarations = None,
declaration = False,
source_map = False,
declaration_map = False,
@@ -152,6 +153,10 @@ def ts_project(
See https://www.typescriptlang.org/docs/handbook/compiler-options.html#compiler-options
Typically useful arguments for debugging are `--listFiles` and `--listEmittedFiles`.
+ isolated_declarations: Whether to enforce that declaration output (.d.ts file) can be produced for a
+ single source file at a time. Requires some additional explicit types on exported symbols.
+ See https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-5.html#isolated-declarations
+
transpiler: A custom transpiler tool to run that produces the JavaScript outputs instead of `tsc`.
Under `--@aspect_rules_ts//ts:default_to_tsc_transpiler`, the default is to use `tsc` to produce
@@ -280,6 +285,8 @@ def ts_project(
allow_js = compiler_options.setdefault("allowJs", allow_js)
if resolve_json_module != None:
resolve_json_module = compiler_options.setdefault("resolveJsonModule", resolve_json_module)
+ if isolated_declarations != None:
+ isolated_declarations = compiler_options.setdefault("isolatedDeclarations", isolated_declarations)
# These options are always passed on the tsc command line so don't include them
# in the tsconfig. At best they're redundant, but at worst we'll have a conflict
@@ -321,8 +328,9 @@ def ts_project(
else:
# To stitch together a tree of ts_project where transpiler is a separate rule,
# we have to produce a few targets
- tsc_target_name = "%s_typings" % name
+ tsc_target_name = "%s_tsc" % name
transpile_target_name = "%s_transpile" % name
+ typings_target_name = "%s_typings" % name
typecheck_target_name = "%s_typecheck" % name
test_target_name = "%s_typecheck_test" % name
@@ -342,14 +350,31 @@ def ts_project(
else:
fail("transpiler attribute should be a rule/macro or a skylib partial. Got " + type(transpiler))
- # Users should build this target to get a failed build when typechecking fails
- native.filegroup(
- name = typecheck_target_name,
- srcs = [tsc_target_name],
- # This causes the types to be produced, which in turn triggers the tsc action to typecheck
- output_group = "types",
- **common_kwargs
- )
+ if isolated_declarations:
+ # Users should build this target to get a failed build when typechecking fails
+ native.filegroup(
+ name = typecheck_target_name,
+ srcs = [tsc_target_name],
+ output_group = "typecheck",
+ **common_kwargs
+ )
+
+ native.filegroup(
+ name = typings_target_name,
+ srcs = [tsc_target_name],
+ # This causes the declarations to be produced, which in turn triggers the tsc action to typecheck
+ output_group = "types",
+ **common_kwargs
+ )
+ else:
+ # Users should build this target to get a failed build when typechecking fails
+ native.filegroup(
+ name = typecheck_target_name,
+ srcs = [tsc_target_name],
+ # This causes the declarations to be produced, which in turn triggers the tsc action to typecheck
+ output_group = "types",
+ **common_kwargs
+ )
# Ensures the typecheck target gets built under `bazel test --build_tests_only`
build_test(
@@ -389,6 +414,7 @@ def ts_project(
incremental = incremental,
preserve_jsx = preserve_jsx,
composite = composite,
+ isolated_declarations = isolated_declarations,
declaration = declaration,
declaration_dir = declaration_dir,
source_map = source_map,
diff --git a/ts/private/ts_lib.bzl b/ts/private/ts_lib.bzl
index de720e58..33244dce 100644
--- a/ts/private/ts_lib.bzl
+++ b/ts/private/ts_lib.bzl
@@ -108,6 +108,9 @@ COMPILER_OPTION_ATTRS = {
"composite": attr.bool(
doc = "https://www.typescriptlang.org/tsconfig#composite",
),
+ "isolated_declarations": attr.bool(
+ doc = "https://www.typescriptlang.org/tsconfig/#isolatedDeclarations",
+ ),
"declaration": attr.bool(
doc = "https://www.typescriptlang.org/tsconfig#declaration",
),
diff --git a/ts/private/ts_project.bzl b/ts/private/ts_project.bzl
index 7437c4ef..9dfff791 100644
--- a/ts/private/ts_project.bzl
+++ b/ts/private/ts_project.bzl
@@ -234,13 +234,12 @@ This is an error because Bazel does not run actions unless their outputs are nee
srcs_tsconfig_deps = ctx.attr.srcs + [ctx.attr.tsconfig] + ctx.attr.deps
inputs_depset = depset()
+ typecheck_validation_outs = depset()
+
if len(outputs) > 0:
transitive_inputs.append(_gather_types_from_js_infos(srcs_tsconfig_deps))
-
- inputs_depset = depset(
- copy_files_to_bin_actions(ctx, inputs),
- transitive = transitive_inputs,
- )
+ copied_srcs = copy_files_to_bin_actions(ctx, inputs)
+ inputs_depset = depset(copied_srcs, transitive = transitive_inputs)
if ctx.attr.transpile != 0 and not ctx.attr.emit_declaration_only:
# Make sure the user has acknowledged that transpiling is slow
@@ -253,23 +252,64 @@ This is an error because Bazel does not run actions unless their outputs are nee
else:
verb = "Type-checking"
- ctx.actions.run(
- executable = executable,
- inputs = inputs_depset,
- arguments = [arguments],
- outputs = outputs,
- mnemonic = "TsProject",
- execution_requirements = execution_requirements,
- resource_set = resource_set(ctx.attr),
- progress_message = "%s TypeScript project %s [tsc -p %s]" % (
- verb,
- ctx.label,
- tsconfig_path,
- ),
- env = {
- "BAZEL_BINDIR": ctx.bin_dir.path,
- },
- )
+ if ctx.attr.isolated_declarations:
+ ctx.actions.run(
+ executable = executable,
+ inputs = depset(copied_srcs),
+ arguments = [arguments, "--noCheck"],
+ outputs = outputs,
+ mnemonic = "TsProjectEmit",
+ execution_requirements = execution_requirements,
+ resource_set = resource_set(ctx.attr),
+ progress_message = "%s TypeScript project %s [tsc -p %s --noCheck]" % (
+ verb,
+ ctx.label,
+ tsconfig_path,
+ ),
+ env = {
+ "BAZEL_BINDIR": ctx.bin_dir.path,
+ },
+ )
+
+ typecheck_marker = ctx.actions.declare_file(ctx.label.name + ".typecheck_ok")
+ typecheck_validation_outs = depset([typecheck_marker])
+
+ ctx.actions.run_shell(
+ command = """{} $@ && echo "" > {} """.format(executable.path, typecheck_marker.path),
+ tools = [executable],
+ inputs = inputs_depset,
+ arguments = [arguments, "--noEmit"],
+ outputs = [typecheck_marker],
+ mnemonic = "TsProjectCheck",
+ execution_requirements = execution_requirements,
+ resource_set = resource_set(ctx.attr),
+ progress_message = "%s TypeScript project %s [tsc -p %s --noEmit]" % (
+ verb,
+ ctx.label,
+ tsconfig_path,
+ ),
+ env = {
+ "BAZEL_BINDIR": ctx.bin_dir.path,
+ },
+ )
+ else:
+ ctx.actions.run(
+ executable = executable,
+ inputs = inputs_depset,
+ arguments = [arguments],
+ outputs = outputs,
+ mnemonic = "TsProject",
+ execution_requirements = execution_requirements,
+ resource_set = resource_set(ctx.attr),
+ progress_message = "%s TypeScript project %s [tsc -p %s]" % (
+ verb,
+ ctx.label,
+ tsconfig_path,
+ ),
+ env = {
+ "BAZEL_BINDIR": ctx.bin_dir.path,
+ },
+ )
transitive_sources = js_lib_helpers.gather_transitive_sources(output_sources, srcs_tsconfig_deps)
@@ -316,6 +356,7 @@ This is an error because Bazel does not run actions unless their outputs are nee
# https://bazel.build/extending/rules#validations_output_group
# "hold the otherwise unused outputs of validation actions"
_validation = validation_outs,
+ typecheck = typecheck_validation_outs,
),
coverage_common.instrumented_files_info(
ctx,
diff --git a/ts/private/ts_project_options_validator.js b/ts/private/ts_project_options_validator.js
index 336e0047..b84fd82a 100755
--- a/ts/private/ts_project_options_validator.js
+++ b/ts/private/ts_project_options_validator.js
@@ -170,6 +170,7 @@ function main(_a) {
check('declaration')
check('incremental')
check('tsBuildInfoFile', 'ts_build_info_file')
+ check('isolatedDeclarations', 'isolated_declarations')
check_preserve_jsx()
if (failures.length > 0) {
console.error(
diff --git a/ts/private/ts_validate_options.bzl b/ts/private/ts_validate_options.bzl
index 09183601..fd4cc27a 100644
--- a/ts/private/ts_validate_options.bzl
+++ b/ts/private/ts_validate_options.bzl
@@ -37,6 +37,7 @@ def _validate_action(ctx, tsconfig_inputs):
source_map = ctx.attr.source_map,
incremental = ctx.attr.incremental,
ts_build_info_file = ctx.attr.ts_build_info_file,
+ isolated_declarations = ctx.attr.isolated_declarations,
)
arguments.add_all([
to_output_relative_path(tsconfig),