WARNING: this is beta-quality software. Breaking changes are likely. Not recommended for production use without expert support.
The TypeScript rules integrate the TypeScript compiler with Bazel.
Looking for Karma rules ts_web_test
and karma_web_test
?
These are now documented in the README at http://npmjs.com/package/@bazel/karma
Add a devDependency on @bazel/typescript
$ yarn add -D @bazel/typescript
# or
$ npm install --save-dev @bazel/typescript
Your WORKSPACE
should declare a yarn_install
or npm_install
rule named npm
.
It should then install the rules found in the npm packages using the install_bazel_dependencies
function.
See https://github.com/bazelbuild/rules_nodejs/#quickstart
Add to your WORKSPACE
file, after install_bazel_dependencies()
:
# Setup TypeScript toolchain
load("@npm_bazel_typescript//:index.bzl", "ts_setup_workspace")
ts_setup_workspace()
Create a BUILD.bazel
file in your workspace root. If your tsconfig.json
file is in the root, use
exports_files(["tsconfig.json"], visibility = ["//visibility:public"])
otherwise create an alias:
alias(
name = "tsconfig.json",
actual = "//path/to/my:tsconfig.json",
)
We recommend you use Bazel managed dependencies but if you would like
Bazel to also install a node_modules
in your workspace you can also
point the node_repositories
repository rule in your WORKSPACE file to
your package.json
.
node_repositories(package_json = ["//:package.json"])
You can then run yarn
in your workspace with:
$ bazel run @nodejs//:yarn
To use your workspace node_modules
folder as a dependency in ts_library
and
other rules, add the following to your root BUILD.bazel
file:
filegroup(
name = "node_modules",
srcs = glob(
include = [
"node_modules/**/*.js",
"node_modules/**/*.d.ts",
"node_modules/**/*.json",
"node_modules/.bin/*",
],
exclude = [
# Files under test & docs may contain file names that
# are not legal Bazel labels (e.g.,
# node_modules/ecstatic/test/public/中文/檔案.html)
"node_modules/**/test/**",
"node_modules/**/docs/**",
# Files with spaces in the name are not legal Bazel labels
"node_modules/**/* */**",
"node_modules/**/* *",
],
),
)
# Create a tsc_wrapped compiler rule to use in the ts_library
# compiler attribute when using self-managed dependencies
nodejs_binary(
name = "@bazel/typescript/tsc_wrapped",
entry_point = "@bazel/typescript/internal/tsc_wrapped/tsc_wrapped.js",
# Point bazel to your node_modules to find the entry point
node_modules = ["//:node_modules"],
)
See https://github.com/bazelbuild/rules_nodejs#dependencies for more information on managing npm dependencies with Bazel.
The ts_library
rule invokes the TypeScript compiler on one compilation unit,
or "library" (generally one directory of source files).
Create a BUILD
file next to your sources:
package(default_visibility=["//visibility:public"])
load("@npm_bazel_typescript//:index.bzl", "ts_library")
ts_library(
name = "my_code",
srcs = glob(["*.ts"]),
deps = ["//path/to/other:library"],
)
If your ts_library target has npm dependencies you can specify these
with fine grained npm dependency targets created by the yarn_install
or
npm_install
rules:
ts_library(
name = "my_code",
srcs = glob(["*.ts"]),
deps = [
"@npm//@types/node",
"@npm//@types/foo",
"@npm//foo",
"//path/to/other:library",
],
)
You can also you the @npm//@types
target which will include all
packages in the @types
scope as dependencies.
If you are using self-managed npm dependencies, you can use the
node_modules
attribute in ts_library
and point it to the
//:node_modules
filegroup defined in your root BUILD.bazel
file.
You'll also need to override the compiler
attribute if you do this
as the Bazel-managed deps and self-managed cannot be used together
in the same rule.
ts_library(
name = "my_code",
srcs = glob(["*.ts"]),
deps = ["//path/to/other:library"],
node_modules = "//:node_modules",
compiler = "//:@bazel/typescript/tsc_wrapped",
)
To build a ts_library
target run:
bazel build //path/to/package:target
The resulting .d.ts
file paths will be printed. Additionally, the .js
outputs from TypeScript will be written to disk, next to the .d.ts
files 1.
Note that the tsconfig.json
file used for compilation should be the same one
your editor references, to keep consistent settings for the TypeScript compiler.
By default, ts_library
uses the tsconfig.json
file in the workspace root
directory. See the notes about the tsconfig
attribute in the ts_library API docs.
1 The declarationDir compiler option will be silently overwritten if present.
There are two choices for development mode:
- Use the
ts_devserver
rule to bring up our simple, fast development server. This is intentionally very simple, to help you get started quickly. However, since there are many development servers available, we do not want to mirror their features in yet another server we maintain. - Teach your real frontend server to serve files from Bazel's output directory.
This is not yet documented. Choose this option if you have an existing server
used in development mode, or if your requirements exceed what the
ts_devserver
supports. Be careful that your development round-trip stays fast (should be under two seconds).
To use ts_devserver
, you simply load
the rule, and call it with deps
that
point to your ts_library
target(s):
load("@npm_bazel_typescript//:index.bzl", "ts_devserver", "ts_library")
ts_library(
name = "app",
srcs = ["app.ts"],
)
ts_devserver(
name = "devserver",
# We'll collect all the devmode JS sources from these TypeScript libraries
deps = [":app"],
# This is the path we'll request from the browser, see index.html
serving_path = "/bundle.js",
# The devserver can serve our static files too
static_files = ["index.html"],
)
The index.html
should be the same one you use for production, and it should
load the JavaScript bundle from the path indicated in serving_path
.
If you don't have an index.html file, a simple one will be generated by the
ts_devserver
.
See examples/app
in this repository for a working example. To run the
devserver, we recommend you use ibazel:
$ ibazel run examples/app:devserver
ibazel
will keep the devserver program running, and provides a LiveReload
server so the browser refreshes the application automatically when each build
finishes.
Bazel's TypeScript compiler has your workspace path mapped, so you can import from an absolute path starting from your workspace.
/WORKSPACE
:
workspace(name = "myworkspace")
/some/long/path/to/deeply/nested/subdirectory.ts
:
import {thing} from 'myworkspace/place';
will import from /place.ts
.
Since this is an extension to the vanillia TypeScript compiler, editors which use the TypeScript language services to provide code completion and inline type checking will not be able to resolve the modules. In the above example, adding
"paths": {
"myworkspace/*": ["*"]
}
to tsconfig.json
will fix the imports for the common case of using absolute paths.
See https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping for more details on the paths syntax.
Similarly, you can use path mapping to teach the editor how to resolve imports
from ts_library
rules which set the module_name
attribute.
If you'd like a "watch mode", try https://github.com/bazelbuild/bazel-watcher (note, it's also quite new).
At some point, we plan to release a tool similar to gazelle to generate the BUILD files from your source code.
ts_config(name, deps, src)
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
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.
name |
Name; required
A unique name for this target. |
deps |
List of labels; required
Additional tsconfig.json files referenced via extends |
src |
Label; required
The tsconfig.json file passed to the TypeScript compiler |
ts_devserver(name, additional_root_paths, bootstrap, data, deps, entry_module, index_html, port, scripts, serving_path, static_files)
ts_devserver is a simple development server intended for a quick "getting started" experience.
Additional documentation at https://github.com/alexeagle/angular-bazel-example/wiki/Running-a-devserver-under-Bazel
name |
Name; required
A unique name for this target. |
additional_root_paths |
List of strings; optional
Additional root paths to serve `static_files` from. Paths should include the workspace name such as `["__main__/resources"]` |
bootstrap |
List of labels; optional
Scripts to include in the JS bundle before the module loader (require.js) |
data |
List of labels; optional
Dependencies that can be require'd while the server is running |
deps |
List of labels; optional
Targets that produce JavaScript, such as `ts_library` |
entry_module |
String; optional
The `entry_module` should be the AMD module name of the entry module such as `"__main__/src/index".` `ts_devserver` concats the following snippet after the bundle to load the application: `require(["entry_module"]);` |
index_html |
Label; optional
An index.html file, we'll inject the script tag for the bundle, as well as script tags for .js static_files and link tags for .css static_files |
port |
Integer; optional
The port that the devserver will listen on. |
scripts |
List of labels; optional
User scripts to include in the JS bundle before the application sources |
serving_path |
String; optional
The path you can request from the client HTML which serves the JavaScript bundle. If you don't specify one, the JavaScript can be loaded at /_/ts_scripts.js |
static_files |
List of labels; optional
Arbitrary files which to be served, such as index.html. They are served relative to the package where this rule is declared. |
ts_library(name, compile_angular_templates, compiler, data, deps, expected_diagnostics, generate_externs, internal_testing_type_check_dependencies, module_name, module_root, node_modules, runtime, runtime_deps, srcs, supports_workers, tsconfig, tsickle_typed)
ts_library
type-checks and compiles a set of TypeScript sources to JavaScript.
It produces declarations files (.d.ts
) which are used for compiling downstream
TypeScript targets and JavaScript for the browser and Closure compiler.
name |
Name; required
A unique name for this target. |
compile_angular_templates |
Boolean; optional
Run the Angular ngtsc compiler under ts_library |
compiler |
Label; optional
Sets a different TypeScript compiler binary to use for this library. For example, we use the vanilla TypeScript tsc.js for bootstrapping, and Angular compilations can replace this with `ngc`. The default ts_library compiler depends on the |
data |
List of labels; optional |
deps |
List of labels; optional
Compile-time dependencies, typically other ts_library targets |
expected_diagnostics |
List of strings; optional |
generate_externs |
Boolean; optional |
internal_testing_type_check_dependencies |
Boolean; optional
Testing only, whether to type check inputs that aren't srcs. |
module_name |
String; optional |
module_root |
String; optional |
node_modules |
Label; optional
The npm packages which should be available during the compile. The default value is This attribute is DEPRECATED. As of version 0.18.0 the recommended
approach to npm dependencies is to use fine grained npm dependencies
which are setup with the For example, in targets that used a
which specifies all files within the
In this case, only the listed npm packages and their
transitive deps are includes as inputs to the The @npm external repository and the fine grained npm package
targets are setup using the yarn_install( name = "npm", package_json = "//:package.json", yarn_lock = "//:yarn.lock", ) |
runtime |
String; optional |
runtime_deps |
List of labels; optional |
srcs |
List of labels; required
The TypeScript source files to compile. |
supports_workers |
Boolean; optional
Intended for internal use only. Allows you to disable the Bazel Worker strategy for this library. Typically used together with the "compiler" setting when using a non-worker aware compiler binary. |
tsconfig |
Label; optional
A tsconfig.json file containing settings for TypeScript compilation.
Note that some properties in the tsconfig are governed by Bazel and will be
overridden, such as The default value is set to
|
tsickle_typed |
Boolean; optional
If using tsickle, instruct it to translate types to ClosureJS format |
ts_proto_library(name, deps, output_name)
Wraps https://github.com/dcodeIO/protobuf.js for use in Bazel.
ts_proto_library
has identical outputs to ts_library
, so it can be used anywhere
a ts_library
can appear, such as in the deps[]
of another ts_library
.
Example:
load("@npm_bazel_typescript//:index.bzl", "ts_library", "ts_proto_library")
proto_library(
name = "car_proto",
srcs = ["car.proto"],
)
ts_proto_library(
name = "car",
deps = [":car_proto"],
)
ts_library(
name = "test_lib",
testonly = True,
srcs = ["car.spec.ts"],
deps = [":car"],
)
Note in this example we named the ts_proto_library
rule car
so that the
result will be car.d.ts
. This means our TypeScript code can just
import {symbols} from './car'
. Use the output_name
attribute if you want to
name the rule differently from the output file.
The JavaScript produced by protobuf.js has a runtime dependency on a support library.
Under devmode (e.g. ts_devserver
, ts_web_test_suite
) you'll need to include these scripts
in the bootstrap
phase (before Require.js loads). You can use the label
@npm_bazel_typescript//:protobufjs_bootstrap_scripts
to reference these scripts
in the bootstrap
attribute of ts_web_test_suite
or ts_devserver
.
To complete the example above, you could write a ts_web_test_suite
:
load("@npm_bazel_karma//:index.bzl", "ts_web_test_suite")
ts_web_test_suite(
name = "test",
deps = ["test_lib"],
bootstrap = ["@npm_bazel_typescript//:protobufjs_bootstrap_scripts"],
browsers = [
"@io_bazel_rules_webtesting//browsers:chromium-local",
"@io_bazel_rules_webtesting//browsers:firefox-local",
],
)
name |
Name; required
A unique name for this target. |
deps |
List of labels; optional
proto_library targets |
output_name |
String; optional
Name of the resulting module, which you will import from. If not specified, the name will match the target's name. |
(name, additional_root_paths, assets, data, index_html)
Assembles a web application from source files.
Injects JS and CSS resources into the index.html.
name |
Name; required
A unique name for this target. |
additional_root_paths |
List of strings; optional
Path prefixes to strip off all assets, in addition to the current package. Longest wins. |
assets |
List of labels; optional
Files which should be referenced from the index_html |
data |
List of labels; optional
Additional files which should be served on request |
index_html |
Label; optional
The entry point of the application |
(name, amd_names, module_name, module_root, srcs)
name |
Name; required
A unique name for this target. |
amd_names |
Dictionary: String -> String; optional
Mapping from require module names to global variables. This allows devmode JS sources to load unnamed UMD bundles from third-party libraries. |
module_name |
String; optional |
module_root |
String; optional |
srcs |
List of labels; optional |
(name, bootstrap, configuration_env_vars, data, entry_point, expected_exit_code, install_source_map_support, node, node_modules, templated_args)
name |
Name; required
A unique name for this target. |
bootstrap |
List of strings; optional
JavaScript modules to be loaded before the entry point. For example, Angular uses this to patch the Jasmine async primitives for zone.js before the first `describe`. |
configuration_env_vars |
List of strings; optional
Pass these configuration environment variables to the resulting binary. Chooses a subset of the configuration environment variables (taken from ctx.var), which also includes anything specified via the --define flag. Note, this can lead to different outputs produced by this rule. |
data |
List of labels; optional
Runtime dependencies which may be loaded during execution. |
entry_point |
String; required
The script which should be executed first, usually containing a main function. This attribute expects a string starting with the workspace name, so that it's not ambiguous in cases where a script with the same name appears in another directory or external workspace. |
expected_exit_code |
Integer; optional
The expected exit code for the test. Defaults to 0. |
install_source_map_support |
Boolean; optional
Install the source-map-support package. Enable this to get stack traces that point to original sources, e.g. if the program was written in TypeScript. |
node |
Label; optional
The node entry point target. |
node_modules |
Label; optional
The npm packages which should be available to `require()` during execution.
|
(name, bootstrap, configuration_env_vars, data, entry_point, install_source_map_support, node, node_modules, templated_args)
name |
Name; required
A unique name for this target. |
bootstrap |
List of strings; optional
JavaScript modules to be loaded before the entry point. For example, Angular uses this to patch the Jasmine async primitives for zone.js before the first `describe`. |
configuration_env_vars |
List of strings; optional
Pass these configuration environment variables to the resulting binary. Chooses a subset of the configuration environment variables (taken from ctx.var), which also includes anything specified via the --define flag. Note, this can lead to different outputs produced by this rule. |
data |
List of labels; optional
Runtime dependencies which may be loaded during execution. |
entry_point |
String; required
The script which should be executed first, usually containing a main function. This attribute expects a string starting with the workspace name, so that it's not ambiguous in cases where a script with the same name appears in another directory or external workspace. |
install_source_map_support |
Boolean; optional
Install the source-map-support package. Enable this to get stack traces that point to original sources, e.g. if the program was written in TypeScript. |
node |
Label; optional
The node entry point target. |
node_modules |
Label; optional
The npm packages which should be available to `require()` during execution.
|
(name, deps, packages, replace_with_version, replacements, srcs, vendor_external)
name |
Name; required
A unique name for this target. |
deps |
List of labels; optional
Other targets which produce files that should be included in the package, such as `rollup_bundle` |
packages |
List of labels; optional
Other npm_package rules whose content is copied into this package. |
replace_with_version |
String; optional
If set this value is replaced with the version stamp data. See the section on stamping in the README. |
replacements |
Dictionary: String -> String; optional
Key-value pairs which are replaced in all the files while building the package. |
srcs |
List of labels; optional
Files inside this directory which are simply copied into the package. |
vendor_external |
List of strings; optional
External workspaces whose contents should be vendored into this workspace. Avoids 'external/foo' path segments in the resulting package. Note: only targets in the workspace root can include files from an external workspace. Targets in nested packages only pick up files from within that package. |
(name, deps)
name |
Name; required
A unique name for this target. |
deps |
List of labels; optional |
(name, additional_entry_points, deps, entry_point, global_name, globals, license_banner, node_modules, srcs)
name |
Name; required
A unique name for this target. |
additional_entry_points |
List of strings; optional
Additional entry points of the application for code splitting, passed as the input to rollup. These should be a path relative to the workspace root.
|
check_rules_typescript_version(version_string)
Verify that a compatible npm_bazel_typescript is loaded a WORKSPACE.
Where COMPAT_VERSION and VERSION come from the npm_bazel_typescript that is loaded in a WORKSPACE, this function will check:
VERSION >= version_string >= COMPAT_VERSION
This should be called from the WORKSPACE
file so that the build fails as
early as possible. For example:
# in WORKSPACE:
load("@npm_bazel_typescript//:index.bzl", "check_rules_typescript_version")
check_rules_typescript_version(version_string = "0.22.0")