diff --git a/packages/typescript/internal/ts_project.bzl b/packages/typescript/internal/ts_project.bzl index da63b196f0..9e50e7f557 100644 --- a/packages/typescript/internal/ts_project.bzl +++ b/packages/typescript/internal/ts_project.bzl @@ -17,6 +17,7 @@ _ATTRS = { "srcs": attr.label_list(allow_files = True, mandatory = True), "args": attr.string_list(), "extends": attr.label_list(allow_files = [".json"]), + "outdir": attr.string(), "tsc": attr.label(default = Label(_DEFAULT_TSC), executable = True, cfg = "host"), "tsconfig": attr.label(mandatory = True, allow_single_file = [".json"]), "deps": attr.label_list(providers = [DeclarationInfo]), @@ -41,6 +42,9 @@ _TsConfigInfo = provider( }, ) +def _join(*elements): + return "/".join([f for f in elements if f]) + def _ts_project_impl(ctx): arguments = ctx.actions.args() @@ -51,7 +55,7 @@ def _ts_project_impl(ctx): "--project", ctx.file.tsconfig.path, "--outDir", - "/".join([ctx.bin_dir.path, ctx.label.package]), + _join(ctx.bin_dir.path, ctx.label.package, ctx.attr.outdir), # Make sure TypeScript writes outputs to same directory structure as inputs "--rootDir", ctx.label.package if ctx.label.package else ".", @@ -59,7 +63,7 @@ def _ts_project_impl(ctx): if len(ctx.outputs.typings_outs) > 0: arguments.add_all([ "--declarationDir", - "/".join([ctx.bin_dir.path, ctx.label.package]), + _join(ctx.bin_dir.path, ctx.label.package, ctx.attr.outdir), ]) # When users report problems, we can ask them to re-build with @@ -198,8 +202,8 @@ validate_options = rule( }, ) -def _out_paths(srcs, ext): - return [f[:f.rindex(".")] + ext for f in srcs if not f.endswith(".d.ts")] +def _out_paths(srcs, outdir, ext): + return [_join(outdir, f[:f.rindex(".")] + ext) for f in srcs if not f.endswith(".d.ts")] def ts_project_macro( name = "tsconfig", @@ -216,6 +220,7 @@ def ts_project_macro( emit_declaration_only = False, tsc = None, validate = True, + outdir = None, **kwargs): """Compiles one TypeScript project using `tsc --project` @@ -341,6 +346,11 @@ def ts_project_macro( validate: boolean; whether to check that the tsconfig settings match the attributes. + outdir: a string specifying a subdirectory under the bazel-out folder where outputs are written. + Note that Bazel always requires outputs be written under a subdirectory matching the input package, + so if your rule appears in path/to/my/package/BUILD.bazel and outdir = "foo" then the .js files + will appear in bazel-out/[arch]/bin/path/to/my/package/foo/*.js + declaration: if the `declaration` bit is set in the tsconfig. Instructs Bazel to expect a `.d.ts` output for each `.ts` source. source_map: if the `sourceMap` bit is set in the tsconfig. @@ -385,10 +395,11 @@ def ts_project_macro( deps = deps + extra_deps, tsconfig = tsconfig, extends = extends, - js_outs = _out_paths(srcs, ".js") if not emit_declaration_only else [], - map_outs = _out_paths(srcs, ".js.map") if source_map and not emit_declaration_only else [], - typings_outs = _out_paths(srcs, ".d.ts") if declaration or composite else [], - typing_maps_outs = _out_paths(srcs, ".d.ts.map") if declaration_map else [], + outdir = outdir, + js_outs = _out_paths(srcs, outdir, ".js") if not emit_declaration_only else [], + map_outs = _out_paths(srcs, outdir, ".js.map") if source_map and not emit_declaration_only else [], + typings_outs = _out_paths(srcs, outdir, ".d.ts") if declaration or composite else [], + typing_maps_outs = _out_paths(srcs, outdir, ".d.ts.map") if declaration_map else [], buildinfo_out = tsconfig[:-5] + ".tsbuildinfo" if composite or incremental else None, tsc = tsc, **kwargs diff --git a/packages/typescript/test/ts_project/outdir/BUILD.bazel b/packages/typescript/test/ts_project/outdir/BUILD.bazel new file mode 100644 index 0000000000..1f0292d892 --- /dev/null +++ b/packages/typescript/test/ts_project/outdir/BUILD.bazel @@ -0,0 +1,34 @@ +load("@build_bazel_rules_nodejs//internal/golden_file_test:golden_file_test.bzl", "golden_file_test") +load("//packages/typescript:index.bzl", "ts_project") + +[ + ts_project( + name = "compile_%s" % format, + srcs = ["a.ts"], + args = [ + "--module", + format, + ], + # Write the output files to an extra nested directory + outdir = format, + tsconfig = "tsconfig.json", + ) + for format in [ + "commonjs", + "esnext", + ] +] + +golden_file_test( + name = "test_cjs", + # Refers to the output from ts_project above + actual = "commonjs/a.js", + golden = "a.golden.cjs", +) + +golden_file_test( + name = "test_esm", + # Refers to the output from ts_project above + actual = "esnext/a.js", + golden = "a.golden.js", +) diff --git a/packages/typescript/test/ts_project/outdir/a.golden.cjs b/packages/typescript/test/ts_project/outdir/a.golden.cjs new file mode 100644 index 0000000000..ec7b4a2baa --- /dev/null +++ b/packages/typescript/test/ts_project/outdir/a.golden.cjs @@ -0,0 +1,3 @@ +"use strict"; +exports.__esModule = true; +exports.a = 1; diff --git a/packages/typescript/test/ts_project/outdir/a.golden.js b/packages/typescript/test/ts_project/outdir/a.golden.js new file mode 100644 index 0000000000..d887f329be --- /dev/null +++ b/packages/typescript/test/ts_project/outdir/a.golden.js @@ -0,0 +1 @@ +export var a = 1; diff --git a/packages/typescript/test/ts_project/outdir/a.ts b/packages/typescript/test/ts_project/outdir/a.ts new file mode 100644 index 0000000000..f4c3747d67 --- /dev/null +++ b/packages/typescript/test/ts_project/outdir/a.ts @@ -0,0 +1 @@ +export const a: number = 1 diff --git a/packages/typescript/test/ts_project/outdir/tsconfig.json b/packages/typescript/test/ts_project/outdir/tsconfig.json new file mode 100644 index 0000000000..56d9d8ed34 --- /dev/null +++ b/packages/typescript/test/ts_project/outdir/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "types": [] + } +}