Skip to content

Commit

Permalink
feat(typescript): generate tsconfig.json for ts_project
Browse files Browse the repository at this point in the history
This is an experimental, opt-in feature where ts_project will generate a tsconfig.json file in place of
the user specifying one in the source directory.

I expect a long tail of compatibility bugs since any path appearing in this generated file needs to point from
bazel-out back to the source directory.

Fixes bazel-contrib#2058
  • Loading branch information
Alex Eagle committed Aug 20, 2020
1 parent 660b456 commit 3a506e9
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 38 deletions.
29 changes: 8 additions & 21 deletions packages/typescript/checked_in_ts_project.bzl
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"checked_in_ts_project rule"

load("@build_bazel_rules_nodejs//:index.bzl", "generated_file_test")
load("@build_bazel_rules_nodejs//third_party/github.com/bazelbuild/bazel-skylib:rules/write_file.bzl", "write_file")
load("//packages/typescript:index.bzl", "ts_project")

def checked_in_ts_project(name, src, checked_in_js = None, **kwargs):
Expand All @@ -15,16 +14,13 @@ def checked_in_ts_project(name, src, checked_in_js = None, **kwargs):
if not checked_in_js:
checked_in_js = src[:-3] + ".js"

tsconfig = "tsconfig_%s.json" % name

# workspace is up three dirs (bazel-out/arch/bin) plus number of segments in the package
workspace_root = "/".join([".."] * (3 + len(native.package_name().split("/"))))

# Generate a tsconfig, this is partly an example of how it can be done, per jbedard and toxicable request
write_file(
name = "_gen_tsconfig_%s" % name,
content = [struct(
compilerOptions = struct(
ts_project(
name = name,
srcs = [src],
declaration = True,
# using config means a tsconfig.json file will be generated
config = {
"compilerOptions": struct(
lib = ["es2017", "dom"],
strict = True,
target = "es2015",
Expand All @@ -33,16 +29,7 @@ def checked_in_ts_project(name, src, checked_in_js = None, **kwargs):
declaration = True,
skipLibCheck = True,
),
files = ["/".join([workspace_root, native.package_name(), src])],
).to_json()],
out = tsconfig,
)

ts_project(
name = name,
srcs = [src],
declaration = True,
tsconfig = tsconfig,
},
**kwargs
)

Expand Down
20 changes: 20 additions & 0 deletions packages/typescript/internal/ts_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

"tsconfig.json files using extends"

load("@build_bazel_rules_nodejs//third_party/github.com/bazelbuild/bazel-skylib:rules/write_file.bzl", "write_file")

TsConfigInfo = provider()

def _ts_config_impl(ctx):
Expand Down Expand Up @@ -46,3 +48,21 @@ feature from TypeScript, then the Bazel implementation needs to know about that
extended configuration file as well, to pass them both to the TypeScript compiler.
""",
)

def _join(*elements):
return "/".join([f for f in elements if f])

# Syntax sugar around skylib's write_file
def write_tsconfig(name, config, files, out):
# workspace is up three dirs (bazel-out/arch/bin) plus number of segments in the package
workspace_root = "/".join([".."] * (3 + len(native.package_name().split("/"))))

amended_config = struct(
files = [_join(workspace_root, native.package_name(), f) for f in files],
**config
)
write_file(
name = name,
content = [amended_config.to_json()],
out = out,
)
57 changes: 40 additions & 17 deletions packages/typescript/internal/ts_project.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

load("@build_bazel_rules_nodejs//:providers.bzl", "DeclarationInfo", "NpmPackageInfo", "declaration_info", "js_module_info", "run_node")
load("@build_bazel_rules_nodejs//internal/linker:link_node_modules.bzl", "module_mappings_aspect")
load(":ts_config.bzl", "write_tsconfig")

_DEFAULT_TSC = (
# BEGIN-INTERNAL
Expand Down Expand Up @@ -226,6 +227,7 @@ def _out_paths(srcs, outdir, rootdir, ext):
def ts_project_macro(
name = "tsconfig",
tsconfig = None,
config = None,
srcs = None,
args = [],
deps = [],
Expand Down Expand Up @@ -349,6 +351,13 @@ def ts_project_macro(
deps: List of labels of other rules that produce TypeScript typings (.d.ts files)
config: EXPERIMENTAL a dictionary of tsconfig keys.
If specified, a tsconfig.json file will be generated for this compilation.
You may use only one of config or tsconfig
Each file in srcs will be converted to a relative path in the `files` section.
tsconfig: Label of the tsconfig.json file to use for the compilation.
By default, we add `.json` to the `name` attribute.
Expand Down Expand Up @@ -400,26 +409,40 @@ def ts_project_macro(

if srcs == None:
srcs = native.glob(["**/*.ts", "**/*.tsx"])

if tsconfig == None:
tsconfig = name + ".json"

extra_deps = []

if validate:
validate_options(
name = "_validate_%s_options" % name,
target = "//%s:%s" % (native.package_name(), name),
declaration = declaration,
source_map = source_map,
declaration_map = declaration_map,
composite = composite,
incremental = incremental,
emit_declaration_only = emit_declaration_only,
tsconfig = tsconfig,
extends = extends,
if config != None:
# Usage with a config attribute that generates a tsconfig.json
if tsconfig != None:
fail("only one of config or tsconfig may be set")

tsconfig = "tsconfig_%s.json" % name
write_tsconfig(
name = "_gen_tsconfig_%s" % name,
config = config,
files = srcs,
out = tsconfig,
)
extra_deps.append("_validate_%s_options" % name)

else:
# Usage with a user-defined tsconfig.json file
if tsconfig == None:
tsconfig = name + ".json"

if validate:
validate_options(
name = "_validate_%s_options" % name,
target = "//%s:%s" % (native.package_name(), name),
declaration = declaration,
source_map = source_map,
declaration_map = declaration_map,
composite = composite,
incremental = incremental,
emit_declaration_only = emit_declaration_only,
tsconfig = tsconfig,
extends = extends,
)
extra_deps.append("_validate_%s_options" % name)

typings_out_dir = declaration_dir if declaration_dir else out_dir

Expand Down
32 changes: 32 additions & 0 deletions packages/typescript/test/ts_project/generated_tsconfig/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
load("@build_bazel_rules_nodejs//:index.bzl", "generated_file_test")
load("//packages/typescript:index.bzl", "ts_project")

ts_project(
name = "compile1",
srcs = glob(["*.ts*"]),
config = {},
out_dir = "1",
)

generated_file_test(
name = "test1",
src = "empty_config.js_",
generated = ":1/a.js",
)

ts_project(
name = "compile2",
srcs = glob(["*.ts*"]),
config = {
"compilerOptions": {
"module": "esnext",
},
},
out_dir = "2",
)

generated_file_test(
name = "test2",
src = "esnext.js_",
generated = ":2/a.js",
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const a: string = 'hello world';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"use strict";
exports.__esModule = true;
exports.a = 'hello world';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export var a = 'hello world';

0 comments on commit 3a506e9

Please sign in to comment.