Closed enriched closed 6 years ago
Hi Rich,
Thanks for asking. I introduced transpilation in the interest of speed when building large graphs, especially when working on a hot-reloading TypeScript app with ibazel
.
Imagine for example that you have a chain of five ts_library
targets, all depending on the previous one: A -> B -> C -> D -> E. If we change a source file in A
, we need to wait until A
is compiled before we can start compiling B
, because it depends on A
's type definitions. This gets quite slow in a large graph. On the other hand, they can all be transpiled at the same time because transpilation doesn't require type definitions.
When you build a JavaScript library (js_library
target) and/or a web application (web_bundle
target), you don't need type definitions at all. This is why when a js_library
target depends on a ts_library
target, it only depends on the transpiled version. This allows a web_bundle
to be recompiled relatively quickly. However, this also means that you lose type safety, and still need to rely on compiling each ts_library
on its own to verify this.
I must admit this is a bit clunky. Even with the transpiled version, it's not nearly as fast as a single webpack compilation! I'm now exploring whether there are smarter ways to do modular TypeScript builds, possibly without Bazel.
Open to suggestions and pull requests of course.
Regarding the _transpiled
and _compiled
suffixes: Bazel indeed discourages naming a generated folder the same as its target. Here's the warning it shows if you change compiled_dir
from "%{name}_compiled"
to just "${name}"
:
$ bazel build //tests/...
WARNING: /Users/work/dev/bazel-javascript/tests/ts-example/src/nested/BUILD.bazel:5:1: target 'constants' is both a rule and a file; please choose another name for the rule
WARNING: /Users/work/dev/bazel-javascript/tests/ts-example/src/nested/BUILD.bazel:18:1: target 'nodeps' is both a rule and a file; please choose another name for the rule
WARNING: /Users/work/dev/bazel-javascript/tests/ts-example/src/testing/BUILD.bazel:6:1: target 'sum' is both a rule and a file; please choose another name for the rule
WARNING: /Users/work/dev/bazel-javascript/tests/ts-example/src/testing/BUILD.bazel:14:1: target 'sum_test_lib' is both a rule and a file; please choose another name for the rule
WARNING: /Users/work/dev/bazel-javascript/tests/ts-example/src/BUILD.bazel:12:1: target 'main' is both a rule and a file; please choose another name for the rule
WARNING: /Users/work/dev/bazel-javascript/tests/ts-example/src/BUILD.bazel:22:1: target 'logger' is both a rule and a file; please choose another name for the rule
WARNING: /Users/work/dev/bazel-javascript/tests/ts-example/src/react/BUILD.bazel:12:1: target 'storybook' is both a rule and a file; please choose another name for the rule
WARNING: /Users/work/dev/bazel-javascript/tests/ts-example/src/react/BUILD.bazel:23:1: target 'component_story' is both a rule and a file; please choose another name for the rule
WARNING: /Users/work/dev/bazel-javascript/tests/ts-example/src/react/BUILD.bazel:34:1: target 'component' is both a rule and a file; please choose another name for the rule
Thanks for the great explanation François! And I would be really interested in whatever you figure out for modular typescript builds.
Is there a reason to transpile the typescript twice in the ts_library rule? It would seem that if you are already running the full compilation that you don't need to run the faster transpilation. And it could be an optional parameter to this rule.
It would also free up the output folders so that the compiled sources could reside in a folder that is the name of the target. Or is that frowned on in Bazel?