blob: 6d992a7ef825f48d67ea89f24fa6319be357f8c4 [file]
# Copyright 2017 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"tsconfig.json files using extends"
load(":ts_lib.bzl", _lib = "lib")
TsConfigInfo = provider(
doc = """Provides TypeScript configuration, in the form of a tsconfig.json file
along with any transitively referenced tsconfig.json files chained by the
"extends" feature""",
fields = {
"deps": "all tsconfig.json files needed to configure TypeScript",
},
)
def _ts_config_impl(ctx):
files = depset([ctx.file.src])
transitive_deps = []
for dep in ctx.attr.deps:
if TsConfigInfo in dep:
transitive_deps.append(dep[TsConfigInfo].deps)
transitive_deps.append(depset(ctx.files.deps))
return [
DefaultInfo(files = files),
TsConfigInfo(deps = depset([ctx.file.src], transitive = transitive_deps)),
]
ts_config = rule(
implementation = _ts_config_impl,
attrs = {
"deps": attr.label_list(
doc = """Additional tsconfig.json files referenced via extends""",
allow_files = True,
),
"src": attr.label(
doc = """The tsconfig.json file passed to the TypeScript compiler""",
allow_single_file = True,
mandatory = True,
),
},
doc = """Allows a tsconfig.json file to extend another file.
Normally, you just give a single `tsconfig.json` file as the tsconfig attribute
of a `ts_library` or `ts_project` rule. However, if your `tsconfig.json` uses the `extends`
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])
def _relative_path(tsconfig, dest):
relative_to = tsconfig.dirname
if dest.is_source:
# Calculate a relative path from the directory where we're writing the tsconfig
# back to the sources root
workspace_root = "/".join([".."] * len(relative_to.split("/")))
return _join(workspace_root, dest.path)
# Bazel guarantees that srcs are beneath the package directory, and we disallow
# tsconfig.json being generated with a "/" in the name.
# So we can calculate a relative path from e.g.
# bazel-out/darwin-fastbuild/bin/packages/typescript/test/ts_project/generated_tsconfig/gen_src
# to <generated file packages/typescript/test/ts_project/generated_tsconfig/gen_src/subdir/a.ts>
result = dest.path[len(relative_to) + 1:]
if not result.startswith("."):
result = "./" + result
return result
def _filter_input_files(files, allow_js, resolve_json_module):
return [
f
for f in files
if _lib.is_ts_src(f.basename, allow_js) or _lib.is_json_src(f.basename, resolve_json_module)
]
def _write_tsconfig_rule(ctx):
# TODO: is it useful to expand Make variables in the content?
content = "\n".join(ctx.attr.content)
if ctx.attr.extends:
content = content.replace(
"__extends__",
_relative_path(ctx.outputs.out, ctx.file.extends),
)
filtered_files = _filter_input_files(ctx.files.files, ctx.attr.allow_js, ctx.attr.resolve_json_module)
if filtered_files:
content = content.replace(
"\"__files__\"",
str([_relative_path(ctx.outputs.out, f) for f in filtered_files]),
)
ctx.actions.write(
output = ctx.outputs.out,
content = content,
)
return [DefaultInfo(files = depset([ctx.outputs.out]))]
write_tsconfig_rule = rule(
implementation = _write_tsconfig_rule,
attrs = {
"content": attr.string_list(),
"extends": attr.label(allow_single_file = True),
"files": attr.label_list(allow_files = True),
"out": attr.output(),
"allow_js": attr.bool(),
"resolve_json_module": attr.bool(),
},
)
# Syntax sugar around skylib's write_file
def write_tsconfig(name, config, files, out, extends = None, allow_js = None, resolve_json_module = None):
"""Wrapper around bazel_skylib's write_file which understands tsconfig paths
Args:
name: name of the resulting write_file rule
config: tsconfig dictionary
files: list of input .ts files to put in the files[] array
out: the file to write
extends: a label for a tsconfig.json file to extend from, if any
"""
if out.find("/") >= 0:
fail("tsconfig should be generated in the package directory, to make relative pathing simple")
if extends:
config["extends"] = "__extends__"
amended_config = struct(
files = "__files__",
**config
)
write_tsconfig_rule(
name = name,
files = files,
extends = extends,
content = [json.encode(amended_config)],
out = out,
allow_js = allow_js,
resolve_json_module = resolve_json_module,
)