Make tsconfig default to label //:tsconfig.json
This avoids the need for users to create their own ts_library macro to follow the best-practice of having the same tsconfig.json for all libraries, and fixes the issue that it's easy to have no tsconfig applied to a ts_library if the attribute is omitted
BREAKING CHANGE:
ts_library targets with no tsconfig attribute will now
default to //:tsconfig.json.
If this breaks you, do one of these:
1) create a file at that location (in the WORKSPACE root)
2) create an alias rule in the root BUILD.bazel file like
alias(name="tsconfig.json", actual="//path/to:tsconfig-something.json")
3) give an explicit tsconfig attribute to your ts_library rules
Closes #232
PiperOrigin-RevId: 205759471
diff --git a/BUILD.bazel b/BUILD.bazel
index 8da32ff..5941297 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -21,6 +21,14 @@
load("@io_bazel_rules_go//go:def.bzl", "gazelle")
load("@build_bazel_rules_nodejs//internal/js_library:js_library.bzl", "js_library")
+# ts_library defaults to this label in the top-level package.
+# Point to where the file actually lives.
+alias(
+ name = "tsconfig.json",
+ actual = "//examples:tsconfig.json",
+ visibility = ["//visibility:public"],
+)
+
gazelle(
name = "gazelle",
prefix = "github.com/bazelbuild/rules_typescript",
diff --git a/defs.bzl b/defs.bzl
index e7caea1..aa3bafb 100644
--- a/defs.bzl
+++ b/defs.bzl
@@ -17,7 +17,7 @@
Users should not load files under "/internal"
"""
load("//internal:ts_repositories.bzl", _ts_setup_workspace = "ts_setup_workspace")
-load("//internal:build_defs.bzl", _ts_library = "ts_library")
+load("//internal:build_defs.bzl", _ts_library = "ts_library_macro")
load("//internal:ts_config.bzl", _ts_config = "ts_config")
load("//internal/devserver:ts_devserver.bzl", _ts_devserver = "ts_devserver_macro")
load("//internal/karma:ts_web_test.bzl",
diff --git a/internal/BUILD.bazel b/internal/BUILD.bazel
index 438f315..b4a1530 100644
--- a/internal/BUILD.bazel
+++ b/internal/BUILD.bazel
@@ -17,13 +17,14 @@
package(default_visibility = ["//visibility:public"])
exports_files([
+ "tsconfig.json",
# Exported to be consumed for generating skydoc.
"build_defs.bzl",
"ts_config.bzl",
"ts_repositories.bzl",
])
-load("//internal:build_defs.bzl", "ts_library")
+load("//:defs.bzl", "ts_library")
load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary", "jasmine_node_test")
# Vanilla typescript compiler: run the tsc.js binary distributed by TypeScript
diff --git a/internal/build_defs.bzl b/internal/build_defs.bzl
index ee3b32d..db78616 100644
--- a/internal/build_defs.bzl
+++ b/internal/build_defs.bzl
@@ -162,7 +162,7 @@
)
return ts_providers_dict_to_struct(ts_providers)
-ts_library = rule(
+_ts_library = rule(
_ts_library_impl,
attrs = dict(COMMON_ATTRIBUTES, **{
"srcs": attr.label_list(
@@ -217,3 +217,9 @@
It produces declarations files (`.d.ts`) which are used for compiling downstream
TypeScript targets and JavaScript for the browser and Closure compiler.
"""
+
+def ts_library_macro(tsconfig = None, **kwargs):
+ if not tsconfig:
+ tsconfig = "//:tsconfig.json"
+
+ _ts_library(tsconfig = tsconfig, **kwargs)
diff --git a/internal/e2e/default_tsconfig_test.js b/internal/e2e/default_tsconfig_test.js
new file mode 100644
index 0000000..298e8eb
--- /dev/null
+++ b/internal/e2e/default_tsconfig_test.js
@@ -0,0 +1,128 @@
+/**
+ * @fileoverview
+ * This tests interactions between multiple Bazel workspaces.
+ *
+ * We have learned from experience in the rules_nodejs repo that it's not
+ * practical to simply check in the nested WORKSPACE files and try to build
+ * them, because
+ * - it's hard to exclude them from the parent WORKSPACE - each nested workspace
+ * must be registered there with a matching name
+ * - testing a child workspace requires `cd` into the directory, which doesn't
+ * fit the CI model of `bazel test ...`
+ *
+ * The test is written in JavaScript simply to make it more portable, so we can
+ * run it on Windows for example. We don't use TypeScript here since we are
+ * running outside the build system.
+ */
+
+const fs = require('fs');
+const path = require('path');
+const child_process = require('child_process');
+const os = require('os');
+
+const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), 'wksp'));
+const WORKSPACE_BOILERPLATE = `
+http_archive(
+ name = "build_bazel_rules_nodejs",
+ urls = ["https://github.com/bazelbuild/rules_nodejs/archive/0.10.0.zip"],
+ strip_prefix = "rules_nodejs-0.10.0",
+)
+http_archive(
+ name = "io_bazel_rules_webtesting",
+ urls = ["https://github.com/bazelbuild/rules_webtesting/archive/v0.2.0.zip"],
+ strip_prefix = "rules_webtesting-0.2.0",
+)
+http_archive(
+ name = "io_bazel_rules_go",
+ urls = [
+ "http://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/0.10.3/rules_go-0.10.3.tar.gz",
+ "https://github.com/bazelbuild/rules_go/releases/download/0.10.3/rules_go-0.10.3.tar.gz"
+ ],
+)
+local_repository(
+ name = "build_bazel_rules_typescript",
+ path = "${process.cwd()}",
+)
+load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories")
+node_repositories(package_json=[])
+load("@build_bazel_rules_typescript//:defs.bzl", "ts_setup_workspace")
+ts_setup_workspace()
+`;
+
+/**
+ * Create a file at path filename, creating parent directories as needed, under
+ * this test's temp directory. Write the content into that file.
+ */
+function write(filename, content) {
+ var parents = path.dirname(path.join(tmpdir, filename));
+ while (path.dirname(parents) !== parents) {
+ if (!fs.existsSync(path.join(parents))) {
+ fs.mkdirSync(path.join(parents));
+ }
+ parents = path.dirname(parents);
+ }
+ fs.writeFileSync(path.join(tmpdir, filename), content);
+}
+
+function bazel(workspace, args) {
+ const result = child_process.spawnSync('bazel', args, {
+ cwd: path.join(tmpdir, workspace),
+ stdio: 'inherit',
+ });
+ expect(result.status).toBe(0, 'bazel exited with non-zero exit code');
+}
+
+describe('default tsconfig', () => {
+ it(`uses the tsconfig in the workspace defining the rule,
+ not the workspace where the rule is defined (rules_typescript), nor
+ the workspace where the build is occurring`,
+ () => {
+ // Workspace 'a' can't compile with --noImplicitAny.
+ // When workspace 'b' has a dep here, we make sure not to use the
+ // tsconfig from workspace 'b'
+ write('a/WORKSPACE', `
+workspace(name = "a")
+${WORKSPACE_BOILERPLATE}`);
+ write('a/BUILD', `
+load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")
+ts_library(
+ name = "a_lib",
+ srcs=["has_implicit_any.ts"],
+ node_modules = "@build_bazel_rules_typescript_tsc_wrapped_deps//:node_modules",
+ visibility = ["//visibility:public"],
+)
+ `);
+ write('a/tsconfig.json', `{}`);
+ write('a/has_implicit_any.ts', `function f(a) {
+ console.error(a);
+ }`);
+
+ // Workspace 'b' has a default tsconfig that sets --noImplicitAny.
+ write('b/WORKSPACE', `
+workspace(name="b")
+local_repository(name="a", path="../a")
+${WORKSPACE_BOILERPLATE}`);
+ write('b/BUILD', `
+load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")
+exports_files(["tsconfig.json"])
+ts_library(
+ name = "b_lib",
+ srcs = ["file.ts"],
+ deps = ["@a//:a_lib"],
+ node_modules = "@build_bazel_rules_typescript_tsc_wrapped_deps//:node_modules",
+)
+ `);
+ write('b/file.ts', `
+ f('thing');
+ `);
+ write('b/tsconfig.json', `{
+ "compilerOptions": {
+ "noImplicitAny": true
+ }
+ }`);
+
+ // Now build from workspace 'b' and verify that the dep in workspace 'a'
+ // was able to compile.
+ bazel('b', ['build', ':all']);
+ });
+});
diff --git a/internal/karma/BUILD.bazel b/internal/karma/BUILD.bazel
index 8802ef9..c6fee3a 100644
--- a/internal/karma/BUILD.bazel
+++ b/internal/karma/BUILD.bazel
@@ -15,6 +15,7 @@
srcs = glob(["*.ts"]),
module_name = "karma-concat-js",
node_modules = "@build_bazel_rules_typescript_tsc_wrapped_deps//:node_modules",
+ tsconfig = "//internal:tsconfig.json",
)
load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary")
diff --git a/internal/tsconfig.json b/internal/tsconfig.json
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/internal/tsconfig.json
diff --git a/package.json b/package.json
index 664ad04..6d9788a 100644
--- a/package.json
+++ b/package.json
@@ -31,7 +31,8 @@
},
"scripts": {
"pree2e": "webdriver-manager update $CHROMEDRIVER_VERSION_ARG && bazel build examples/app:e2e && bazel build examples/protocol_buffers:e2e",
- "e2e": "yarn e2e-examples-app-devserver && yarn e2e-examples-app-prodserver && yarn e2e-examples-protobuf-devserver && yarn e2e-examples-protobuf-prodserver",
+ "e2e": "yarn e2e-bazel-external && yarn e2e-examples-app-devserver && yarn e2e-examples-app-prodserver && yarn e2e-examples-protobuf-devserver && yarn e2e-examples-protobuf-prodserver",
+ "e2e-bazel-external": "jasmine internal/e2e/default_tsconfig_test.js",
"e2e-examples-app-devserver": "concurrently \"bazel run examples/app:devserver\" \"while ! nc -z 127.0.0.1 8080; do sleep 1; done && protractor --suite app\" --kill-others --success first",
"e2e-examples-app-prodserver": "concurrently \"bazel run examples/app:prodserver\" \"while ! nc -z 127.0.0.1 8080; do sleep 1; done && protractor --suite app\" --kill-others --success first",
"e2e-examples-protobuf-devserver": "concurrently \"bazel run examples/protocol_buffers:devserver\" \"while ! nc -z 127.0.0.1 8080; do sleep 1; done && protractor --suite protocol_buffers\" --kill-others --success first",