-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Current state
Most of our build is done in our Vulcanization process today.
Before bringing up what a Vulcanization entails, it is important to note that dependencies of a module is defined in HTML via script and import tags. Vulcanization talks the dependency graph and:
- first builds TypeScript files (along with necessary d.ts files) that outputs .js and a .d.ts file
- compile all JSs (generated included) using JSCompiler into one bundle
- depending on options or "environment", it inlines the bundle into the script tag
In all of these processes, it makes sure a dependency is properly provided via Bazel deps.
One interesting artifact of Vulcanization is the concept of webPath. Instead of using relative or absolute path to a module, modules, in its tf_web_library, define a webPath which indicates a path of the module in the bundle artifact. For instance, say there is a module in a/b/c/d/foo.js which defines webPath of /tf-foo/foo. When other modules use the foo, it will define dependency as <import href="/tf-foo/foo" /> (or a relative path from its webPath). This loosely is related to concept of webfiles of Bazel/rules_closure, though, stephanwlee lacks profound knowledge in it yet.
The design choice of webPath and Vulcanization impact how we author JS/TS. The natural way of defining dependency in JavaScript is via CommonJS or ES module (modern browsers have native ES module support) but we have to use namespace and window object (e.g., window.tf.graph.common) because we define dependency via HTML. This is not to say concept of Vulcanization is incompatible with es module completely but it requires non-trivial changes.
Future state
In the future, we want our solution to:
- use Bazel: we don't want part of our app to be on Bazel and others on another build system
- have ability to easily test FE (karma integration at the very least is required)
- have less customization of build system if possible (i.e., deprecate
Vulcanization.java) - other framework (e.g., React) friendly
With above requirements in mind, this section only is attempting capture one point in the vast solution space.
-
Use Bazel/rules_typescript to build TypeScript
This is the canonical way of building TypeScript with Bazel. -
Utilize Bazel/rules_nodejs as much as possible
rules_nodejs allows us to pull dependencies from NPM and use node ecosystem to build and test. -
Move off of JSCompiler and use Webpack, Rollup, or None
JSCompiler is quite sophisticated but it is a bit overkill for our application today. Shaving extra bytes off of JS payload is not worth the complexity and additional maintenance burden. Good alternatives to JSC are Webpack and Rollup.
We can also consider not bundling and loading compiled JavaScript using ES module. With HTTP/2 more mainstream and because our code is not written for Node.js, there is no reason to (1) build JS to shim node module system & Node APIs and (2) create one binary for performance optimization. This at least generally is very good approach for development. One drawback of this is that there are interesting third-party hosted TensorBoard and it may negatively impact them performance wise.