blob: d587d54537229854cc96d56c76d2c35d0e54184c [file] [log] [blame]
# 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.
"""TypeScript rules.
"""
# pylint: disable=unused-argument
# pylint: disable=missing-docstring
load(":common/compilation.bzl", "COMMON_ATTRIBUTES", "compile_ts", "ts_providers_dict_to_struct")
load(":executables.bzl", "get_tsc")
load(":common/tsconfig.bzl", "create_tsconfig")
def _compile_action(ctx, inputs, outputs, config_file_path):
externs_files = []
non_externs_files = []
for output in outputs:
if output.basename.endswith(".externs.js"):
externs_files.append(output)
elif output.basename.endswith(".es5.MF"):
ctx.file_action(output, content="")
else:
non_externs_files.append(output)
# TODO(plf): For now we mock creation of files other than {name}.js.
for externs_file in externs_files:
ctx.file_action(output=externs_file, content="")
action_inputs = inputs + [f for f in ctx.files.node_modules
if f.path.endswith(".ts") or f.path.endswith(".json")]
if ctx.file.tsconfig:
action_inputs += [ctx.file.tsconfig]
# One at-sign makes this a params-file, enabling the worker strategy.
# Two at-signs escapes the argument so it's passed through to tsc_wrapped
# rather than the contents getting expanded.
if ctx.attr.supports_workers:
arguments = ["@@" + config_file_path]
mnemonic = "TypeScriptCompile"
else:
arguments = ["-p", config_file_path]
mnemonic = "tsc"
ctx.action(
progress_message = "Compiling TypeScript (devmode) %s" % ctx.label,
mnemonic = mnemonic,
inputs = action_inputs,
outputs = non_externs_files,
arguments = arguments,
executable = ctx.executable.compiler,
execution_requirements = {
"supports-workers": str(int(ctx.attr.supports_workers)),
},
)
def _devmode_compile_action(ctx, inputs, outputs, config_file_path):
_compile_action(ctx, inputs, outputs, config_file_path)
def tsc_wrapped_tsconfig(ctx,
files,
srcs,
devmode_manifest=None,
tsickle_externs=None,
type_blacklisted_declarations=[],
allowed_deps=depset(),
jsx_factory=None,
**kwargs):
# The location of tsconfig.json is interpreted as the root of the project
# when it is passed to the TS compiler with the `-p` option:
# https://www.typescriptlang.org/docs/handbook/tsconfig-json.html.
# Our tsconfig.json is in bazel-foo/bazel-out/local-fastbuild/bin/{package_path}
# because it's generated in the execution phase. However, our source files are in
# bazel-foo/ and therefore we need to strip some parent directories for each
# f.path.
config = create_tsconfig(ctx, files, srcs,
devmode_manifest=devmode_manifest,
**kwargs)
config["bazelOptions"]["nodeModulesPrefix"] = "node_modules"
# If the user gives a tsconfig attribute, the generated file should extend
# from the user's tsconfig.
# See https://github.com/Microsoft/TypeScript/issues/9876
# We subtract the ".json" from the end before handing to TypeScript because
# this gives extra error-checking.
if ctx.file.tsconfig:
workspace_path = config["compilerOptions"]["rootDir"]
config["extends"] = "/".join([workspace_path, ctx.file.tsconfig.path[:-5]])
return config
# ************ #
# ts_library #
# ************ #
def _ts_library_impl(ctx):
"""Implementation of ts_library.
Args:
ctx: the context.
Returns:
the struct returned by the call to compile_ts.
"""
ts_providers = compile_ts(ctx, is_library=True,
compile_action=_compile_action,
devmode_compile_action=_devmode_compile_action,
tsc_wrapped_tsconfig=tsc_wrapped_tsconfig)
return ts_providers_dict_to_struct(ts_providers)
ts_library = rule(
_ts_library_impl,
attrs = dict(COMMON_ATTRIBUTES, **{
"srcs":
attr.label_list(
allow_files=FileType([
".ts",
".tsx",
]),
mandatory=True,),
# TODO(alexeagle): reconcile with google3: ts_library rules should
# be portable across internal/external, so we need this attribute
# internally as well.
"tsconfig":
attr.label(allow_files = True, single_file=True),
"compiler":
attr.label(
default=get_tsc(),
single_file=False,
allow_files=True,
executable=True,
cfg="host",),
"supports_workers": attr.bool(default = True),
# @// is special syntax for the "main" repository
# The default assumes the user specified a target "node_modules" in their
# root BUILD file.
"node_modules": attr.label(default = Label("@//:node_modules")),
}),
)