From 981e7c1ab431c79dd83bcf5d495fefc4a0e02e45 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Fri, 17 Jul 2020 23:08:49 +0100 Subject: [PATCH] feat(typescript): support for declarationdir on ts_project (#2048) * feat(typescript): support for declarationdir on ts_project * refactor: rename ts_library attributes to the snake_case version of their TypeScript names --outDir == out_dir --declarationDir == declaration_dir --rootDir == root_dir Note this is non-breaking since none of these attributes are on the 1.x branch --- packages/typescript/internal/ts_project.bzl | 58 ++++++++++++------- .../ts_project/declarationdir/BUILD.bazel | 36 ++++++++++++ .../ts_project/declarationdir/subdir/a.ts | 1 + .../ts_project/declarationdir/tsconfig.json | 8 +++ .../test/ts_project/declarationdir/verify.js | 8 +++ .../declarationdir_with_value/BUILD.bazel | 37 ++++++++++++ .../declarationdir_with_value/subdir/a.ts | 1 + .../declarationdir_with_value/tsconfig.json | 8 +++ .../declarationdir_with_value/verify.js | 15 +++++ .../test/ts_project/json/BUILD.bazel | 2 +- .../test/ts_project/outdir/BUILD.bazel | 2 +- .../ts_project/rootdir_with_value/BUILD.bazel | 2 +- 12 files changed, 154 insertions(+), 24 deletions(-) create mode 100644 packages/typescript/test/ts_project/declarationdir/BUILD.bazel create mode 100644 packages/typescript/test/ts_project/declarationdir/subdir/a.ts create mode 100644 packages/typescript/test/ts_project/declarationdir/tsconfig.json create mode 100644 packages/typescript/test/ts_project/declarationdir/verify.js create mode 100644 packages/typescript/test/ts_project/declarationdir_with_value/BUILD.bazel create mode 100644 packages/typescript/test/ts_project/declarationdir_with_value/subdir/a.ts create mode 100644 packages/typescript/test/ts_project/declarationdir_with_value/tsconfig.json create mode 100644 packages/typescript/test/ts_project/declarationdir_with_value/verify.js diff --git a/packages/typescript/internal/ts_project.bzl b/packages/typescript/internal/ts_project.bzl index 63de42f51c..4060f15e87 100644 --- a/packages/typescript/internal/ts_project.bzl +++ b/packages/typescript/internal/ts_project.bzl @@ -11,10 +11,11 @@ _DEFAULT_TSC = ( _ATTRS = { "args": attr.string_list(), + "declaration_dir": attr.string(), "deps": attr.label_list(providers = [DeclarationInfo]), "extends": attr.label_list(allow_files = [".json"]), - "outdir": attr.string(), - "rootdir": attr.string(), + "out_dir": attr.string(), + "root_dir": attr.string(), # NB: no restriction on extensions here, because tsc sometimes adds type-check support # for more file kinds (like require('some.json')) and also # if you swap out the `compiler` attribute (like with ngtsc) @@ -56,14 +57,15 @@ def _ts_project_impl(ctx): "--project", ctx.file.tsconfig.path, "--outDir", - _join(ctx.bin_dir.path, ctx.label.package, ctx.attr.outdir), + _join(ctx.bin_dir.path, ctx.label.package, ctx.attr.out_dir), "--rootDir", - _join(ctx.label.package, ctx.attr.rootdir) if ctx.label.package else ".", + _join(ctx.label.package, ctx.attr.root_dir) if ctx.label.package else ".", ]) if len(ctx.outputs.typings_outs) > 0: + declaration_dir = ctx.attr.declaration_dir if ctx.attr.declaration_dir else ctx.attr.out_dir arguments.add_all([ "--declarationDir", - _join(ctx.bin_dir.path, ctx.label.package, ctx.attr.outdir), + _join(ctx.bin_dir.path, ctx.label.package, declaration_dir), ]) # When users report problems, we can ask them to re-build with @@ -98,13 +100,13 @@ def _ts_project_impl(ctx): inputs.extend(ctx.files.extends) # We do not try to predeclare json_outs, because their output locations generally conflict with their path in the source tree. - # (The exception is when outdir is used, then the .json output is a different path than the input.) + # (The exception is when out_dir is used, then the .json output is a different path than the input.) # However tsc will copy .json srcs to the output tree so we want to declare these outputs to include along with .js Default outs # NB: We don't have emit_declaration_only setting here, so use presence of any JS outputs as an equivalent. # tsc will only produce .json if it also produces .js if len(ctx.outputs.js_outs): json_outs = [ - ctx.actions.declare_file(_join(ctx.attr.outdir, src.short_path[len(ctx.label.package) + 1:])) + ctx.actions.declare_file(_join(ctx.attr.out_dir, src.short_path[len(ctx.label.package) + 1:])) for src in ctx.files.srcs if src.basename.endswith(".json") ] @@ -115,7 +117,7 @@ def _ts_project_impl(ctx): if ctx.outputs.buildinfo_out: outputs.append(ctx.outputs.buildinfo_out) runtime_outputs = depset(json_outs + ctx.outputs.js_outs + ctx.outputs.map_outs) - typings_outputs = ctx.outputs.typings_outs + [s for s in ctx.files.srcs if s.path.endswith(".d.ts")] + typings_outputs = ctx.outputs.typings_outs + ctx.outputs.typing_maps_outs + [s for s in ctx.files.srcs if s.path.endswith(".d.ts")] if len(outputs) > 0: run_node( @@ -230,8 +232,9 @@ def ts_project_macro( emit_declaration_only = False, tsc = None, validate = True, - outdir = None, - rootdir = None, + declaration_dir = None, + out_dir = None, + root_dir = None, **kwargs): """Compiles one TypeScript project using `tsc --project` @@ -357,13 +360,21 @@ 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. + root_dir: a string specifying a subdirectory under the input package which should be consider the + root directory of all the input files. + Equivalent to the TypeScript --rootDir option. + By default it is '.', meaning the source directory where the BUILD file lives. + + out_dir: a string specifying a subdirectory under the bazel-out folder where outputs are written. + Equivalent to the TypeScript --outDir option. 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 + so if your rule appears in path/to/my/package/BUILD.bazel and out_dir = "foo" then the .js files + will appear in bazel-out/[arch]/bin/path/to/my/package/foo/*.js. + By default the out_dir is '.', meaning the packages folder in bazel-out. - rootdir: a string specifying a subdirectory under the input package which should be consider the - root directory of all the input files. + declaration_dir: a string specifying a subdirectory under the bazel-out folder where generated declaration + outputs are written. Equivalent to the TypeScript --declarationDir option. + By default declarations are written to the out_dir. declaration: if the `declaration` bit is set in the tsconfig. Instructs Bazel to expect a `.d.ts` output for each `.ts` source. @@ -377,6 +388,8 @@ def ts_project_macro( Instructs Bazel to expect a `.tsbuildinfo` output. emit_declaration_only: if the `emitDeclarationOnly` bit is set in the tsconfig. Instructs Bazel *not* to expect `.js` or `.js.map` outputs for `.ts` sources. + + **kwargs: passed through to underlying rule, allows eg. visibility, tags """ if srcs == None: @@ -402,6 +415,8 @@ def ts_project_macro( ) extra_deps.append("_validate_%s_options" % name) + typings_out_dir = declaration_dir if declaration_dir else out_dir + ts_project( name = name, srcs = srcs, @@ -409,12 +424,13 @@ def ts_project_macro( deps = deps + extra_deps, tsconfig = tsconfig, extends = extends, - outdir = outdir, - rootdir = rootdir, - js_outs = _out_paths(srcs, outdir, rootdir, ".js") if not emit_declaration_only else [], - map_outs = _out_paths(srcs, outdir, rootdir, ".js.map") if source_map and not emit_declaration_only else [], - typings_outs = _out_paths(srcs, outdir, rootdir, ".d.ts") if declaration or composite else [], - typing_maps_outs = _out_paths(srcs, outdir, rootdir, ".d.ts.map") if declaration_map else [], + declaration_dir = declaration_dir, + out_dir = out_dir, + root_dir = root_dir, + js_outs = _out_paths(srcs, out_dir, root_dir, ".js") if not emit_declaration_only else [], + map_outs = _out_paths(srcs, out_dir, root_dir, ".js.map") if source_map and not emit_declaration_only else [], + typings_outs = _out_paths(srcs, typings_out_dir, root_dir, ".d.ts") if declaration or composite else [], + typing_maps_outs = _out_paths(srcs, typings_out_dir, root_dir, ".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/declarationdir/BUILD.bazel b/packages/typescript/test/ts_project/declarationdir/BUILD.bazel new file mode 100644 index 0000000000..67bed1bbf9 --- /dev/null +++ b/packages/typescript/test/ts_project/declarationdir/BUILD.bazel @@ -0,0 +1,36 @@ +load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_test") +load("//packages/typescript:index.bzl", "ts_project") + +# Ensure that subdir/a.ts produces outDir/a.js and outDir/a.d.ts +SRCS = [ + "subdir/a.ts", +] + +ts_project( + name = "tsconfig", + srcs = SRCS, + declaration = True, + declaration_map = True, + out_dir = "out", + root_dir = "subdir", + source_map = True, +) + +filegroup( + name = "types", + srcs = [":tsconfig"], + output_group = "types", +) + +nodejs_test( + name = "test", + data = [ + ":tsconfig", + ":types", + ], + entry_point = "verify.js", + templated_args = [ + "$(locations :types)", + "$(locations :tsconfig)", + ], +) diff --git a/packages/typescript/test/ts_project/declarationdir/subdir/a.ts b/packages/typescript/test/ts_project/declarationdir/subdir/a.ts new file mode 100644 index 0000000000..a668b7e336 --- /dev/null +++ b/packages/typescript/test/ts_project/declarationdir/subdir/a.ts @@ -0,0 +1 @@ +export const a: string = 'hello'; diff --git a/packages/typescript/test/ts_project/declarationdir/tsconfig.json b/packages/typescript/test/ts_project/declarationdir/tsconfig.json new file mode 100644 index 0000000000..098887abfd --- /dev/null +++ b/packages/typescript/test/ts_project/declarationdir/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "sourceMap": true, + "declaration": true, + "declarationMap": true, + "types": [] + } +} diff --git a/packages/typescript/test/ts_project/declarationdir/verify.js b/packages/typescript/test/ts_project/declarationdir/verify.js new file mode 100644 index 0000000000..93dd5c6c4f --- /dev/null +++ b/packages/typescript/test/ts_project/declarationdir/verify.js @@ -0,0 +1,8 @@ +const assert = require('assert'); + +const types_files = process.argv.slice(2, 4); +const code_files = process.argv.slice(4, 6); +assert.ok(types_files.some(f => f.endsWith('declarationdir/out/a.d.ts')), 'Missing a.d.ts'); +assert.ok(types_files.some(f => f.endsWith('declarationdir/out/a.d.ts.map')), 'Missing a.d.ts.map'); +assert.ok(code_files.some(f => f.endsWith('declarationdir/out/a.js')), 'Missing a.js'); +assert.ok(code_files.some(f => f.endsWith('declarationdir/out/a.js.map')), 'Missing a.js.map'); diff --git a/packages/typescript/test/ts_project/declarationdir_with_value/BUILD.bazel b/packages/typescript/test/ts_project/declarationdir_with_value/BUILD.bazel new file mode 100644 index 0000000000..ecb94da512 --- /dev/null +++ b/packages/typescript/test/ts_project/declarationdir_with_value/BUILD.bazel @@ -0,0 +1,37 @@ +load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_test") +load("//packages/typescript:index.bzl", "ts_project") + +# Ensure that subdir/a.ts produces outDir/a.js and declarationDir/a.d.ts +SRCS = [ + "subdir/a.ts", +] + +ts_project( + name = "tsconfig", + srcs = SRCS, + declaration = True, + declaration_dir = "out/types", + declaration_map = True, + out_dir = "out/code", + root_dir = "subdir", + source_map = True, +) + +filegroup( + name = "types", + srcs = [":tsconfig"], + output_group = "types", +) + +nodejs_test( + name = "test", + data = [ + ":tsconfig", + ":types", + ], + entry_point = "verify.js", + templated_args = [ + "$(locations :types)", + "$(locations :tsconfig)", + ], +) diff --git a/packages/typescript/test/ts_project/declarationdir_with_value/subdir/a.ts b/packages/typescript/test/ts_project/declarationdir_with_value/subdir/a.ts new file mode 100644 index 0000000000..a668b7e336 --- /dev/null +++ b/packages/typescript/test/ts_project/declarationdir_with_value/subdir/a.ts @@ -0,0 +1 @@ +export const a: string = 'hello'; diff --git a/packages/typescript/test/ts_project/declarationdir_with_value/tsconfig.json b/packages/typescript/test/ts_project/declarationdir_with_value/tsconfig.json new file mode 100644 index 0000000000..098887abfd --- /dev/null +++ b/packages/typescript/test/ts_project/declarationdir_with_value/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "sourceMap": true, + "declaration": true, + "declarationMap": true, + "types": [] + } +} diff --git a/packages/typescript/test/ts_project/declarationdir_with_value/verify.js b/packages/typescript/test/ts_project/declarationdir_with_value/verify.js new file mode 100644 index 0000000000..6755a1b301 --- /dev/null +++ b/packages/typescript/test/ts_project/declarationdir_with_value/verify.js @@ -0,0 +1,15 @@ +const assert = require('assert'); + +const types_files = process.argv.slice(2, 4); +const code_files = process.argv.slice(4, 6); +assert.ok( + types_files.some(f => f.endsWith('declarationdir_with_value/out/types/a.d.ts')), + 'Missing a.d.ts'); +assert.ok( + types_files.some(f => f.endsWith('declarationdir_with_value/out/types/a.d.ts.map')), + 'Missing a.d.ts.map'); +assert.ok( + code_files.some(f => f.endsWith('declarationdir_with_value/out/code/a.js')), 'Missing a.js'); +assert.ok( + code_files.some(f => f.endsWith('declarationdir_with_value/out/code/a.js.map')), + 'Missing a.js.map'); diff --git a/packages/typescript/test/ts_project/json/BUILD.bazel b/packages/typescript/test/ts_project/json/BUILD.bazel index badbe241a5..af6f4b4736 100644 --- a/packages/typescript/test/ts_project/json/BUILD.bazel +++ b/packages/typescript/test/ts_project/json/BUILD.bazel @@ -10,7 +10,7 @@ SRCS = [ ts_project( name = "tsconfig", srcs = SRCS, - outdir = "foobar", + out_dir = "foobar", ) ts_project( diff --git a/packages/typescript/test/ts_project/outdir/BUILD.bazel b/packages/typescript/test/ts_project/outdir/BUILD.bazel index 3e23905763..80ec6c4340 100644 --- a/packages/typescript/test/ts_project/outdir/BUILD.bazel +++ b/packages/typescript/test/ts_project/outdir/BUILD.bazel @@ -10,7 +10,7 @@ load("//packages/typescript:index.bzl", "ts_project") format, ], # Write the output files to an extra nested directory - outdir = format, + out_dir = format, tsconfig = "tsconfig.json", ) for format in [ diff --git a/packages/typescript/test/ts_project/rootdir_with_value/BUILD.bazel b/packages/typescript/test/ts_project/rootdir_with_value/BUILD.bazel index 9742ae01a4..7a66fc6293 100644 --- a/packages/typescript/test/ts_project/rootdir_with_value/BUILD.bazel +++ b/packages/typescript/test/ts_project/rootdir_with_value/BUILD.bazel @@ -9,7 +9,7 @@ SRCS = [ ts_project( name = "tsconfig", srcs = SRCS, - rootdir = "subdir", + root_dir = "subdir", ) nodejs_test(