Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(typescript): add support for "jsx: preserve" compiler option #2574

Merged
merged 1 commit into from
Apr 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 31 additions & 6 deletions packages/typescript/internal/ts_project.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ def _validate_options_impl(ctx):
allow_js = ctx.attr.allow_js,
declaration = ctx.attr.declaration,
declaration_map = ctx.attr.declaration_map,
preserve_jsx = ctx.attr.preserve_jsx,
composite = ctx.attr.composite,
emit_declaration_only = ctx.attr.emit_declaration_only,
source_map = ctx.attr.source_map,
Expand Down Expand Up @@ -299,6 +300,7 @@ validate_options = rule(
"emit_declaration_only": attr.bool(),
"extends": attr.label(allow_files = [".json"]),
"incremental": attr.bool(),
"preserve_jsx": attr.bool(),
"source_map": attr.bool(),
"target": attr.string(),
"ts_build_info_file": attr.string(),
Expand All @@ -312,10 +314,20 @@ def _is_ts_src(src, allow_js):
return True
return allow_js and (src.endswith(".js") or src.endswith(".jsx"))

def _out_paths(srcs, outdir, rootdir, allow_js, ext):
def _replace_ext(f, ext_map):
cur_ext = f[f.rindex("."):]
new_ext = ext_map.get(cur_ext)
if new_ext != None:
return new_ext
new_ext = ext_map.get("*")
if new_ext != None:
return new_ext
return None

def _out_paths(srcs, outdir, rootdir, allow_js, ext_map):
rootdir_replace_pattern = rootdir + "/" if rootdir else ""
return [
_join(outdir, f[:f.rindex(".")].replace(rootdir_replace_pattern, "") + ext)
_join(outdir, f[:f.rindex(".")].replace(rootdir_replace_pattern, "") + _replace_ext(f, ext_map))
for f in srcs
if _is_ts_src(f, allow_js)
]
Expand All @@ -331,6 +343,7 @@ def ts_project_macro(
declaration = False,
source_map = False,
declaration_map = False,
preserve_jsx = False,
composite = False,
incremental = False,
emit_declaration_only = False,
Expand Down Expand Up @@ -551,6 +564,8 @@ def ts_project_macro(
Instructs Bazel to expect a `.js.map` output for each `.ts` source.
declaration_map: if the `declarationMap` bit is set in the tsconfig.
Instructs Bazel to expect a `.d.ts.map` output for each `.ts` source.
preserve_jsx: if the `jsx` value is set to "preserve" in the tsconfig.
Instructs Bazel to expect a `.jsx` or `.jsx.map` output for each `.tsx` source.
composite: if the `composite` bit is set in the tsconfig.
Instructs Bazel to expect a `.tsbuildinfo` output and a `.d.ts` output for each `.ts` source.
incremental: if the `incremental` bit is set in the tsconfig.
Expand Down Expand Up @@ -620,6 +635,7 @@ def ts_project_macro(
declaration = declaration,
source_map = source_map,
declaration_map = declaration_map,
preserve_jsx = preserve_jsx,
composite = composite,
incremental = incremental,
ts_build_info_file = ts_build_info_file,
Expand Down Expand Up @@ -660,13 +676,22 @@ def ts_project_macro(
typing_maps_outs = []

if not emit_declaration_only:
js_outs.extend(_out_paths(srcs, out_dir, root_dir, allow_js, ".js"))
exts = {
"*": ".js",
".jsx": ".jsx",
".tsx": ".jsx",
} if preserve_jsx else {"*": ".js"}
js_outs.extend(_out_paths(srcs, out_dir, root_dir, allow_js, exts))
if source_map and not emit_declaration_only:
map_outs.extend(_out_paths(srcs, out_dir, root_dir, False, ".js.map"))
exts = {
"*": ".js.map",
".tsx": ".jsx.map",
} if preserve_jsx else {"*": ".js.map"}
map_outs.extend(_out_paths(srcs, out_dir, root_dir, False, exts))
if declaration or composite:
typings_outs.extend(_out_paths(srcs, typings_out_dir, root_dir, allow_js, ".d.ts"))
typings_outs.extend(_out_paths(srcs, typings_out_dir, root_dir, allow_js, {"*": ".d.ts"}))
if declaration_map:
typing_maps_outs.extend(_out_paths(srcs, typings_out_dir, root_dir, allow_js, ".d.ts.map"))
typing_maps_outs.extend(_out_paths(srcs, typings_out_dir, root_dir, allow_js, {"*": ".d.ts.map"}))

if not len(js_outs) and not len(typings_outs):
fail("""ts_project target "//{}:{}" is configured to produce no outputs.
Expand Down
21 changes: 21 additions & 0 deletions packages/typescript/internal/ts_project_options_validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,25 @@ function main([tsconfigPath, output, target, attrsStr]: string[]): 0|1 {
}
}

const jsxEmit: Record<ts.JsxEmit, string|undefined> =
{
[ts.JsxEmit.None]: 'none',
[ts.JsxEmit.Preserve]: 'preserve',
[ts.JsxEmit.React]: 'react',
[ts.JsxEmit.ReactNative]: 'react-native',
}

function
check_preserve_jsx() {
const attr = 'preserve_jsx'
const jsxVal = options['jsx'] as ts.JsxEmit
if ((jsxVal === ts.JsxEmit.Preserve) !== Boolean(attrs[attr])) {
failures.push(
`attribute ${attr}=${attrs[attr]} does not match compilerOptions.jsx=${jsxEmit[jsxVal]}`);
buildozerCmds.push(`set ${attr} ${jsxVal === ts.JsxEmit.Preserve ? 'True' : 'False'}`);
}
}

check('allowJs', 'allow_js');
check('declarationMap', 'declaration_map');
check('emitDeclarationOnly', 'emit_declaration_only');
Expand All @@ -72,6 +91,7 @@ function main([tsconfigPath, output, target, attrsStr]: string[]): 0|1 {
check('declaration');
check('incremental');
check('tsBuildInfoFile', 'ts_build_info_file');
check_preserve_jsx();

if (failures.length > 0) {
console.error(`ERROR: ts_project rule ${
Expand All @@ -98,6 +118,7 @@ function main([tsconfigPath, output, target, attrsStr]: string[]): 0|1 {
// source_map: ${attrs.source_map}
// emit_declaration_only: ${attrs.emit_declaration_only}
// ts_build_info_file: ${attrs.ts_build_info_file}
// preserve_jsx: ${attrs.preserve_jsx}
`,
'utf-8');
return 0;
Expand Down
38 changes: 38 additions & 0 deletions packages/typescript/test/ts_project/jsx/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_test")
load("//packages/typescript:index.bzl", "ts_project")

# Ensure that jsx inputs result in jsx outputs with preserve_jsx = True
SRCS = [
"a.tsx",
"b.jsx",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the second file is just redundant? reduce the test case to one file if that gives the same assertions/coverage

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea of the second file is to assert that jsx files are not transformed to js files (which happens if jsx is not set to preserve).

]

ts_project(
name = "tsconfig",
srcs = SRCS,
allow_js = True,
declaration = True,
declaration_map = True,
out_dir = "out",
preserve_jsx = True,
source_map = True,
)

filegroup(
name = "types",
srcs = [":tsconfig"],
output_group = "types",
)

nodejs_test(
name = "test",
data = [
":tsconfig",
":types",
],
entry_point = "verify-preserve.js",
templated_args = [
"$(locations :types)",
"$(locations :tsconfig)",
],
)
1 change: 1 addition & 0 deletions packages/typescript/test/ts_project/jsx/a.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const A = <div>a</div>
1 change: 1 addition & 0 deletions packages/typescript/test/ts_project/jsx/b.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const B = <div>b</div>
10 changes: 10 additions & 0 deletions packages/typescript/test/ts_project/jsx/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"allowJs": true,
"sourceMap": true,
"declaration": true,
"declarationMap": true,
"jsx": "preserve",
"types": []
}
}
8 changes: 8 additions & 0 deletions packages/typescript/test/ts_project/jsx/verify-preserve.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const assert = require('assert');

const files = process.argv.slice(2);
assert.ok(files.some(f => f.endsWith('out/a.d.ts')), 'Missing a.d.ts');
assert.ok(files.some(f => f.endsWith('out/a.d.ts.map')), 'Missing a.d.ts.map');
assert.ok(files.some(f => f.endsWith('out/a.jsx.map'), 'Missing a.jsx.map'));
assert.ok(files.some(f => f.endsWith('out/a.jsx'), 'Missing a.jsx'));
assert.ok(files.some(f => f.endsWith('out/b.jsx'), 'Missing b.jsx'));