diff --git a/examples/app/BUILD.bazel b/examples/app/BUILD.bazel index 2ab4d2220d..01a23c6675 100644 --- a/examples/app/BUILD.bazel +++ b/examples/app/BUILD.bazel @@ -1,9 +1,10 @@ load("@build_bazel_rules_nodejs//internal/web_package:web_package.bzl", "web_package") load("@npm//http-server:index.bzl", "http_server") +load("@npm//typescript:index.bzl", "tsc") load("@npm_bazel_protractor//:index.bzl", "protractor_web_test_suite") load("@npm_bazel_rollup//:index.bzl", "rollup_bundle") load("@npm_bazel_terser//:index.bzl", "terser_minified") -load("@npm_bazel_typescript//:index.bzl", "ts_config", "ts_devserver", "ts_library") +load("@npm_bazel_typescript//:index.bzl", "ts_devserver", "ts_library") package(default_visibility = ["//visibility:public"]) @@ -46,20 +47,28 @@ http_server( templated_args = ["package"], ) -ts_config( - name = "tsconfig-test", - src = "tsconfig-test.json", - deps = [":tsconfig.json"], -) - -ts_library( +# we could use ts_library here, but we use plain typescript to demonstrate that it works +tsc( name = "e2e", testonly = 1, - srcs = ["app.e2e-spec.ts"], - tsconfig = ":tsconfig-test", - deps = [ + # Remember that Bazel requires it know what outputs are created ahead of time + # so that it can construct a dependency graph. + outs = [ + "app.e2e-spec.js", + ], + args = [ + "-p", + "$(location tsconfig-test.json)", + "--outDir", + # $@ is a shorthand for the dist/bin directory where Bazel requires we write outputs + "$@", + ], + data = [ + "app.e2e-spec.ts", + "tsconfig.json", + "tsconfig-test.json", "@npm//@types/jasmine", - "@npm//jasmine", + "@npm//@types/node", "@npm//protractor", ], ) diff --git a/examples/app/package.json b/examples/app/package.json index 59a023b1ec..cc0cec1473 100644 --- a/examples/app/package.json +++ b/examples/app/package.json @@ -11,7 +11,7 @@ "rollup": "1.20.3", "stylus": "^0.54.7", "terser": "4.3.1", - "typescript": "2.7.x" + "typescript": "3.6.3" }, "scripts": { "test": "bazel test ..." diff --git a/examples/app/tsconfig-test.json b/examples/app/tsconfig-test.json index 634f399304..087d8237fb 100644 --- a/examples/app/tsconfig-test.json +++ b/examples/app/tsconfig-test.json @@ -3,5 +3,6 @@ "compilerOptions": { "types": ["jasmine", "node"] - } + }, + "include": ["*-spec.ts"] } diff --git a/examples/app/yarn.lock b/examples/app/yarn.lock index 7c76078719..226aa26207 100644 --- a/examples/app/yarn.lock +++ b/examples/app/yarn.lock @@ -1339,10 +1339,10 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -typescript@2.7.x: - version "2.7.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.7.2.tgz#2d615a1ef4aee4f574425cdff7026edf81919836" - integrity sha512-p5TCYZDAO0m4G344hD+wx/LATebLWZNkkh2asWUFqSsD2OrDNhbAHuSjobrmsUmdzjJjEeZVU9g1h3O6vpstnw== +typescript@3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3.tgz#fea942fabb20f7e1ca7164ff626f1a9f3f70b4da" + integrity sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw== union@~0.4.3: version "0.4.6" diff --git a/internal/node/npm_package_bin.bzl b/internal/node/npm_package_bin.bzl index 3b40f729a8..35ec9069a2 100644 --- a/internal/node/npm_package_bin.bzl +++ b/internal/node/npm_package_bin.bzl @@ -20,7 +20,13 @@ _ATTRS = { # because the output_dir is a tree artifact # so we weren't able to give it a label def _expand_location(ctx, s): - s = s.replace("$@", "/".join([ctx.bin_dir.path, ctx.label.package, ctx.attr.name])) + outdir_segments = [ctx.bin_dir.path, ctx.label.package] + if ctx.attr.output_dir: + # We'll write into a newly created directory named after the rule + outdir_segments.append(ctx.attr.name) + + # The list comprehension removes empty segments like if we are in the root package + s = s.replace("$@", "/".join([o for o in outdir_segments if o])) return ctx.expand_location(s, targets = ctx.attr.data) def _impl(ctx): @@ -53,7 +59,7 @@ _npm_package_bin = rule( ) def npm_package_bin(tool = None, package = None, package_bin = None, data = [], outs = [], args = [], output_dir = False, **kwargs): - """Run an arbitrary npm package binary (anything under node_modules/.bin/*) under Bazel. + """Run an arbitrary npm package binary (e.g. a program under node_modules/.bin/*) under Bazel. It must produce outputs. If you just want to run a program with `bazel run`, use the nodejs_binary rule. @@ -69,13 +75,14 @@ def npm_package_bin(tool = None, package = None, package_bin = None, data = [], outs: similar to [genrule.outs](https://docs.bazel.build/versions/master/be/general.html#genrule.outs) output_dir: set to True if you want the output to be a directory Exactly one of `outs`, `output_dir` may be used. - If you output a directory, there can only be one output, which will be named the same as the target. + If you output a directory, there can only be one output, which will be a directory named the same as the target. args: Command-line arguments to the tool. Subject to 'Make variable' substitution. Can use $(location) expansion. See https://docs.bazel.build/versions/master/be/make-variables.html You may also refer to the location of the output_dir with the special `$@` replacement, like genrule. + If output_dir=False then $@ will refer to the output directory for this package. package: an npm package whose binary to run, like "terser". Assumes your node_modules are installed in a workspace called "npm" package_bin: the "bin" entry from `package` that should be run. By default package_bin is the same string as `package` diff --git a/packages/typescript/docs/install.md b/packages/typescript/docs/install.md index 2301adfb83..ee1a6fc49b 100644 --- a/packages/typescript/docs/install.md +++ b/packages/typescript/docs/install.md @@ -7,6 +7,45 @@ 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 +## Alternatives + +This package provides Bazel wrappers around the TypeScript compiler, and are how we compile TS code at Google. + +These rules are opinionated, for example: + +- Your TS code must compile under the `--declaration` flag so that downstream libraries depend only on types, not implementation. This makes Bazel faster by avoiding cascading rebuilds in cases where the types aren't changed. +- We control the output format and module syntax so that downstream rules can rely on them. + +They are also fast and optimized: + +- We keep a running TypeScript compile running as a daemon, using Bazel workers. This process avoids re-parse and re-JIT of the >1MB `typescript.js` and keeps cached bound ASTs for input files which saves time. + +We understand this is a tradeoff. If you want to use the plain TypeScript compiler provided by the TS team at Microsoft, you can do this by calling its CLI directly. For example, + +```python +load("@npm//typescript:index.bzl", "tsc") + +srcs = glob(["*.ts"]) +deps = ["@npm//@types/node"] + +tsc( + name = "compile", + data = srcs + deps, + outs = [s.replace(".ts", ext) for ext in [".js", ".d.ts"] for s in srcs], + args = [ + "--outDir", + "$@", + "--lib", + "es2017,dom", + "--downlevelIteration", + "--declaration", + ] + [ + "$(location %s)" % s + for s in srcs + ], +) +``` + ## Installation Add a devDependency on `@bazel/typescript` @@ -208,6 +247,37 @@ directory. See the notes about the `tsconfig` attribute in the [ts_library API d [ts_library API docs]: http://tsetse.info/api/build_defs.html#ts_library +## Accessing JavaScript outputs + +The default output of the `ts_library` rule is the `.d.ts` files. +This is for a couple reasons: + +- help ensure that downstream rules which access default outputs will not require + a cascading re-build when only the implementation changes but not the types +- make you think about whether you want the devmode (named UMD) or prodmode outputs + +You can access the JS output by adding a `filegroup` rule after the `ts_library`, +for example + +```python +ts_library( + name = "compile", + srcs = ["thing.ts"], +) + +filegroup( + name = "thing.js", + srcs = ["compile"], + # Change to es6_sources to get the 'prodmode' JS + output_group = "es5_sources", +) + +my_rule( + name = "uses_js", + deps = ["thing.js"], +) +``` + ## Serving TypeScript for development There are two choices for development mode: