Skip to content

Commit 425dbd6

Browse files
authored
feat(typescript): add support for "jsx: preserve" compiler option (#2574)
When tsc is instructed to preserve jsx, the emitted files have a .jsx extension for .tsx and .jsx inputs, which means ts_project needs to be aware of the option to properly define outputs.
1 parent 88c97d0 commit 425dbd6

File tree

7 files changed

+110
-6
lines changed

7 files changed

+110
-6
lines changed

packages/typescript/internal/ts_project.bzl

+31-6
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ def _validate_options_impl(ctx):
269269
allow_js = ctx.attr.allow_js,
270270
declaration = ctx.attr.declaration,
271271
declaration_map = ctx.attr.declaration_map,
272+
preserve_jsx = ctx.attr.preserve_jsx,
272273
composite = ctx.attr.composite,
273274
emit_declaration_only = ctx.attr.emit_declaration_only,
274275
source_map = ctx.attr.source_map,
@@ -299,6 +300,7 @@ validate_options = rule(
299300
"emit_declaration_only": attr.bool(),
300301
"extends": attr.label(allow_files = [".json"]),
301302
"incremental": attr.bool(),
303+
"preserve_jsx": attr.bool(),
302304
"source_map": attr.bool(),
303305
"target": attr.string(),
304306
"ts_build_info_file": attr.string(),
@@ -312,10 +314,20 @@ def _is_ts_src(src, allow_js):
312314
return True
313315
return allow_js and (src.endswith(".js") or src.endswith(".jsx"))
314316

315-
def _out_paths(srcs, outdir, rootdir, allow_js, ext):
317+
def _replace_ext(f, ext_map):
318+
cur_ext = f[f.rindex("."):]
319+
new_ext = ext_map.get(cur_ext)
320+
if new_ext != None:
321+
return new_ext
322+
new_ext = ext_map.get("*")
323+
if new_ext != None:
324+
return new_ext
325+
return None
326+
327+
def _out_paths(srcs, outdir, rootdir, allow_js, ext_map):
316328
rootdir_replace_pattern = rootdir + "/" if rootdir else ""
317329
return [
318-
_join(outdir, f[:f.rindex(".")].replace(rootdir_replace_pattern, "") + ext)
330+
_join(outdir, f[:f.rindex(".")].replace(rootdir_replace_pattern, "") + _replace_ext(f, ext_map))
319331
for f in srcs
320332
if _is_ts_src(f, allow_js)
321333
]
@@ -331,6 +343,7 @@ def ts_project_macro(
331343
declaration = False,
332344
source_map = False,
333345
declaration_map = False,
346+
preserve_jsx = False,
334347
composite = False,
335348
incremental = False,
336349
emit_declaration_only = False,
@@ -551,6 +564,8 @@ def ts_project_macro(
551564
Instructs Bazel to expect a `.js.map` output for each `.ts` source.
552565
declaration_map: if the `declarationMap` bit is set in the tsconfig.
553566
Instructs Bazel to expect a `.d.ts.map` output for each `.ts` source.
567+
preserve_jsx: if the `jsx` value is set to "preserve" in the tsconfig.
568+
Instructs Bazel to expect a `.jsx` or `.jsx.map` output for each `.tsx` source.
554569
composite: if the `composite` bit is set in the tsconfig.
555570
Instructs Bazel to expect a `.tsbuildinfo` output and a `.d.ts` output for each `.ts` source.
556571
incremental: if the `incremental` bit is set in the tsconfig.
@@ -620,6 +635,7 @@ def ts_project_macro(
620635
declaration = declaration,
621636
source_map = source_map,
622637
declaration_map = declaration_map,
638+
preserve_jsx = preserve_jsx,
623639
composite = composite,
624640
incremental = incremental,
625641
ts_build_info_file = ts_build_info_file,
@@ -660,13 +676,22 @@ def ts_project_macro(
660676
typing_maps_outs = []
661677

662678
if not emit_declaration_only:
663-
js_outs.extend(_out_paths(srcs, out_dir, root_dir, allow_js, ".js"))
679+
exts = {
680+
"*": ".js",
681+
".jsx": ".jsx",
682+
".tsx": ".jsx",
683+
} if preserve_jsx else {"*": ".js"}
684+
js_outs.extend(_out_paths(srcs, out_dir, root_dir, allow_js, exts))
664685
if source_map and not emit_declaration_only:
665-
map_outs.extend(_out_paths(srcs, out_dir, root_dir, False, ".js.map"))
686+
exts = {
687+
"*": ".js.map",
688+
".tsx": ".jsx.map",
689+
} if preserve_jsx else {"*": ".js.map"}
690+
map_outs.extend(_out_paths(srcs, out_dir, root_dir, False, exts))
666691
if declaration or composite:
667-
typings_outs.extend(_out_paths(srcs, typings_out_dir, root_dir, allow_js, ".d.ts"))
692+
typings_outs.extend(_out_paths(srcs, typings_out_dir, root_dir, allow_js, {"*": ".d.ts"}))
668693
if declaration_map:
669-
typing_maps_outs.extend(_out_paths(srcs, typings_out_dir, root_dir, allow_js, ".d.ts.map"))
694+
typing_maps_outs.extend(_out_paths(srcs, typings_out_dir, root_dir, allow_js, {"*": ".d.ts.map"}))
670695

671696
if not len(js_outs) and not len(typings_outs):
672697
fail("""ts_project target "//{}:{}" is configured to produce no outputs.

packages/typescript/internal/ts_project_options_validator.ts

+21
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,25 @@ function main([tsconfigPath, output, target, attrsStr]: string[]): 0|1 {
6464
}
6565
}
6666

67+
const jsxEmit: Record<ts.JsxEmit, string|undefined> =
68+
{
69+
[ts.JsxEmit.None]: 'none',
70+
[ts.JsxEmit.Preserve]: 'preserve',
71+
[ts.JsxEmit.React]: 'react',
72+
[ts.JsxEmit.ReactNative]: 'react-native',
73+
}
74+
75+
function
76+
check_preserve_jsx() {
77+
const attr = 'preserve_jsx'
78+
const jsxVal = options['jsx'] as ts.JsxEmit
79+
if ((jsxVal === ts.JsxEmit.Preserve) !== Boolean(attrs[attr])) {
80+
failures.push(
81+
`attribute ${attr}=${attrs[attr]} does not match compilerOptions.jsx=${jsxEmit[jsxVal]}`);
82+
buildozerCmds.push(`set ${attr} ${jsxVal === ts.JsxEmit.Preserve ? 'True' : 'False'}`);
83+
}
84+
}
85+
6786
check('allowJs', 'allow_js');
6887
check('declarationMap', 'declaration_map');
6988
check('emitDeclarationOnly', 'emit_declaration_only');
@@ -72,6 +91,7 @@ function main([tsconfigPath, output, target, attrsStr]: string[]): 0|1 {
7291
check('declaration');
7392
check('incremental');
7493
check('tsBuildInfoFile', 'ts_build_info_file');
94+
check_preserve_jsx();
7595

7696
if (failures.length > 0) {
7797
console.error(`ERROR: ts_project rule ${
@@ -98,6 +118,7 @@ function main([tsconfigPath, output, target, attrsStr]: string[]): 0|1 {
98118
// source_map: ${attrs.source_map}
99119
// emit_declaration_only: ${attrs.emit_declaration_only}
100120
// ts_build_info_file: ${attrs.ts_build_info_file}
121+
// preserve_jsx: ${attrs.preserve_jsx}
101122
`,
102123
'utf-8');
103124
return 0;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_test")
2+
load("//packages/typescript:index.bzl", "ts_project")
3+
4+
# Ensure that jsx inputs result in jsx outputs with preserve_jsx = True
5+
SRCS = [
6+
"a.tsx",
7+
"b.jsx",
8+
]
9+
10+
ts_project(
11+
name = "tsconfig",
12+
srcs = SRCS,
13+
allow_js = True,
14+
declaration = True,
15+
declaration_map = True,
16+
out_dir = "out",
17+
preserve_jsx = True,
18+
source_map = True,
19+
)
20+
21+
filegroup(
22+
name = "types",
23+
srcs = [":tsconfig"],
24+
output_group = "types",
25+
)
26+
27+
nodejs_test(
28+
name = "test",
29+
data = [
30+
":tsconfig",
31+
":types",
32+
],
33+
entry_point = "verify-preserve.js",
34+
templated_args = [
35+
"$(locations :types)",
36+
"$(locations :tsconfig)",
37+
],
38+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const A = <div>a</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const B = <div>b</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"compilerOptions": {
3+
"allowJs": true,
4+
"sourceMap": true,
5+
"declaration": true,
6+
"declarationMap": true,
7+
"jsx": "preserve",
8+
"types": []
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const assert = require('assert');
2+
3+
const files = process.argv.slice(2);
4+
assert.ok(files.some(f => f.endsWith('out/a.d.ts')), 'Missing a.d.ts');
5+
assert.ok(files.some(f => f.endsWith('out/a.d.ts.map')), 'Missing a.d.ts.map');
6+
assert.ok(files.some(f => f.endsWith('out/a.jsx.map'), 'Missing a.jsx.map'));
7+
assert.ok(files.some(f => f.endsWith('out/a.jsx'), 'Missing a.jsx'));
8+
assert.ok(files.some(f => f.endsWith('out/b.jsx'), 'Missing b.jsx'));

0 commit comments

Comments
 (0)