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 resolve_json_module support to ts_project #2384

Closed
Closed
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
9 changes: 8 additions & 1 deletion docs/TypeScript.md
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ Defaults to `False`
**USAGE**

<pre>
ts_project(<a href="#ts_project-name">name</a>, <a href="#ts_project-tsconfig">tsconfig</a>, <a href="#ts_project-srcs">srcs</a>, <a href="#ts_project-args">args</a>, <a href="#ts_project-deps">deps</a>, <a href="#ts_project-extends">extends</a>, <a href="#ts_project-allow_js">allow_js</a>, <a href="#ts_project-declaration">declaration</a>, <a href="#ts_project-source_map">source_map</a>,
ts_project(<a href="#ts_project-name">name</a>, <a href="#ts_project-tsconfig">tsconfig</a>, <a href="#ts_project-srcs">srcs</a>, <a href="#ts_project-args">args</a>, <a href="#ts_project-deps">deps</a>, <a href="#ts_project-extends">extends</a>, <a href="#ts_project-allow_js">allow_js</a>, <a href="#ts_project-resolve_json_module">resolve_json_module</a>, <a href="#ts_project-declaration">declaration</a>, <a href="#ts_project-source_map">source_map</a>,
<a href="#ts_project-declaration_map">declaration_map</a>, <a href="#ts_project-composite">composite</a>, <a href="#ts_project-incremental">incremental</a>, <a href="#ts_project-emit_declaration_only">emit_declaration_only</a>, <a href="#ts_project-ts_build_info_file">ts_build_info_file</a>, <a href="#ts_project-tsc">tsc</a>,
<a href="#ts_project-worker_tsc_bin">worker_tsc_bin</a>, <a href="#ts_project-worker_typescript_module">worker_typescript_module</a>, <a href="#ts_project-validate">validate</a>, <a href="#ts_project-supports_workers">supports_workers</a>, <a href="#ts_project-declaration_dir">declaration_dir</a>,
<a href="#ts_project-out_dir">out_dir</a>, <a href="#ts_project-root_dir">root_dir</a>, <a href="#ts_project-link_workspace_root">link_workspace_root</a>, <a href="#ts_project-kwargs">kwargs</a>)
Expand Down Expand Up @@ -751,6 +751,13 @@ TypeScript will generate .d.ts files from .js files.

Defaults to `False`

<h4 id="ts_project-resolve_json_module">resolve_json_module</h4>

boolean; Specifies whether TypeScript will import and extract types from
.json files.

Defaults to `False`

<h4 id="ts_project-declaration">declaration</h4>

if the `declaration` bit is set in the tsconfig.
Expand Down
66 changes: 47 additions & 19 deletions packages/typescript/internal/ts_project.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,16 @@ def _calculate_root_dir(ctx):
some_source_path = None
root_path = None

# Note we don't have access to the ts_project macro allow_js param here.
# For error-handling purposes, we can assume that any .js/.jsx
# input is meant to be in the rootDir alongside .ts/.tsx sources,
# whether the user meant for them to be sources or not.
# It's a non-breaking change to relax this constraint later, but would be
# a breaking change to restrict it further.
# Note we don't have access to the ts_project macro allow_js and
# resolve_json_module params here. For error-handling purposes, we can
# assume that any .js/.jsx/.json input is meant to be in the rootDir
# alongside .ts/.tsx sources, whether the user meant for them to be sources
# or not. It's a non-breaking change to relax this constraint later, but
# would be a breaking change to restrict it further.
allow_js = True
resolve_json_module = True
for src in ctx.files.srcs:
if _is_ts_src(src.path, allow_js):
if _is_ts_src(src.path, allow_js, resolve_json_module):
if src.is_source:
some_source_path = src.path
else:
Expand Down Expand Up @@ -268,6 +269,7 @@ def _validate_options_impl(ctx):
declaration_map = ctx.attr.declaration_map,
composite = ctx.attr.composite,
emit_declaration_only = ctx.attr.emit_declaration_only,
resolve_json_module = ctx.attr.resolve_json_module,
source_map = ctx.attr.source_map,
incremental = ctx.attr.incremental,
ts_build_info_file = ctx.attr.ts_build_info_file,
Expand Down Expand Up @@ -300,6 +302,7 @@ validate_options = rule(
"emit_declaration_only": attr.bool(),
"extends": attr.label(allow_files = [".json"]),
"incremental": attr.bool(),
"resolve_json_module": attr.bool(),
"source_map": attr.bool(),
"target": attr.string(),
"ts_build_info_file": attr.string(),
Expand All @@ -308,19 +311,33 @@ validate_options = rule(
},
)

def _is_ts_src(src, allow_js):
def _is_ts_src(src, allow_js, resolve_json_module):
if not src.endswith(".d.ts") and (src.endswith(".ts") or src.endswith(".tsx")):
return True

if resolve_json_module and src.endswith(".json"):
return True

return allow_js and (src.endswith(".js") or src.endswith(".jsx"))

def _out_paths(srcs, outdir, rootdir, allow_js, ext):
def _out_paths(srcs, outdir, rootdir, allow_js, resolve_json_module, ext):
rootdir_replace_pattern = rootdir + "/" if rootdir else ""
return [

out_paths = [
_join(outdir, f[:f.rindex(".")].replace(rootdir_replace_pattern, "") + ext)
for f in srcs
if _is_ts_src(f, allow_js)
if _is_ts_src(f, allow_js, False)
]

if resolve_json_module == True:
out_paths = out_paths + [
_join(outdir, f.replace(rootdir_replace_pattern, ""))
for f in srcs
if f.endswith(".json")
]

return out_paths

def ts_project_macro(
name = "tsconfig",
tsconfig = None,
Expand All @@ -329,6 +346,7 @@ def ts_project_macro(
deps = [],
extends = None,
allow_js = False,
resolve_json_module = False,
declaration = False,
source_map = False,
declaration_map = False,
Expand Down Expand Up @@ -541,6 +559,8 @@ def ts_project_macro(

allow_js: boolean; Specifies whether TypeScript will read .js and .jsx files. When used with declaration,
TypeScript will generate .d.ts files from .js files.
resolve_json_module: boolean; Specifies whether TypeScript will read import and extract types from
.json files. TypeScript will generate .d.ts files from .json 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.
Expand Down Expand Up @@ -568,10 +588,16 @@ def ts_project_macro(
"""

if srcs == None:
globs = ["**/*.ts", "**/*.tsx"]

if allow_js == True:
srcs = native.glob(["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"])
else:
srcs = native.glob(["**/*.ts", "**/*.tsx"])
globs = globs + ["**/*.js", "**/*.jsx"]

if resolve_json_module == True:
globs = globs + ["**/*.json"]

srcs = native.glob(globs)

extra_deps = []

if type(extends) == type([]):
Expand All @@ -587,6 +613,7 @@ def ts_project_macro(
declaration_map = compiler_options.setdefault("declarationMap", declaration_map)
emit_declaration_only = compiler_options.setdefault("emitDeclarationOnly", emit_declaration_only)
allow_js = compiler_options.setdefault("allowJs", allow_js)
resolve_json_module = compiler_options.setdefault("resolveJsonModule", resolve_json_module)

# 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
Expand All @@ -601,7 +628,7 @@ def ts_project_macro(
write_tsconfig(
name = "_gen_tsconfig_%s" % name,
config = tsconfig,
files = [s for s in srcs if _is_ts_src(s, allow_js)],
files = [s for s in srcs if _is_ts_src(s, allow_js, resolve_json_module)],
extends = Label("//%s:%s" % (native.package_name(), name)).relative(extends) if extends else None,
out = "tsconfig_%s.json" % name,
)
Expand All @@ -626,6 +653,7 @@ def ts_project_macro(
ts_build_info_file = ts_build_info_file,
emit_declaration_only = emit_declaration_only,
allow_js = allow_js,
resolve_json_module = resolve_json_module,
tsconfig = tsconfig,
extends = extends,
)
Expand Down Expand Up @@ -668,13 +696,13 @@ def ts_project_macro(
typing_maps_outs = []

if not emit_declaration_only:
js_outs.extend(_out_paths(srcs, out_dir, root_dir, False, ".js"))
js_outs.extend(_out_paths(srcs, out_dir, root_dir, False, resolve_json_module, ".js"))
if source_map and not emit_declaration_only:
map_outs.extend(_out_paths(srcs, out_dir, root_dir, False, ".js.map"))
map_outs.extend(_out_paths(srcs, out_dir, root_dir, False, False, ".js.map"))
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, False, ".d.ts"))
Comment on lines +699 to +703
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this is maybe not quite right. We need the json files even if emit_declaration_only is true. I think it might make more sense to put these in typings_outs?

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, False, ".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
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ function main([tsconfigPath, output, target, attrsStr]: string[]): 0|1 {
}

check('allowJs', 'allow_js');
check('resolveJsonModule', 'resolve_json_module');
check('declarationMap', 'declaration_map');
check('emitDeclarationOnly', 'emit_declaration_only');
check('sourceMap', 'source_map');
Expand All @@ -91,6 +92,7 @@ function main([tsconfigPath, output, target, attrsStr]: string[]): 0|1 {
output, `
// ${process.argv[1]} checked attributes for ${target}
// allow_js: ${attrs.allow_js}
// resolve_json_module: ${attrs.resolve_json_module}
// composite: ${attrs.composite}
// declaration: ${attrs.declaration}
// declaration_map: ${attrs.declaration_map}
Expand Down
3 changes: 3 additions & 0 deletions packages/typescript/test/ts_project/json/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,21 @@ ts_project(
name = "tsconfig",
srcs = SRCS,
out_dir = "foobar",
validate = False, # resolveJsonModule is in tsconfig.json but not ts_project
)

ts_project(
name = "tsconfig-no-outdir",
srcs = SRCS,
tsconfig = "tsconfig.json",
validate = False, # resolveJsonModule is in tsconfig.json but not ts_project
)

# Test that we don't try to declare .json outputs when tsc isn't producing any JS
ts_project(
name = "tsconfig-decl-only",
srcs = SRCS,
resolve_json_module = True,
tsconfig = {
"compilerOptions": {
"declaration": True,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
load("@build_bazel_rules_nodejs//:index.bzl", "nodejs_test")
load("//packages/typescript:index.bzl", "ts_project")

# Ensure that config.json produces outDir/config.json
SRCS = [
"index.ts",
"config.json",
]

ts_project(
name = "tsconfig",
srcs = SRCS,
declaration = True,
declaration_map = True,
out_dir = "out",
resolve_json_module = True,
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)",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"pizza": "yum"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import config from './config.json';

export const pizza = config.pizza;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"resolveJsonModule": true,
"sourceMap": true,
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"types": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const assert = require('assert');

const types_files = process.argv.slice(2, 4);
const code_files = process.argv.slice(4, 6);
assert.ok(
code_files.some(f => f.endsWith('out/config.json')), `Missing config.json in ${code_files}`);