Developing Ivy requires being able to compile the same framework code, examples, tests, etc. using the legacy pipeline (ngc, view engine, etc) and using one of several new Ivy pipelines. Eventually, too, we want to start testing Ivy in g3, via a multi-phase rollout.
We have several different kinds of code, both in the Angular repository and in others (e.g. CLI). In developing Ivy, we need to be able to test this code against the new compiler(s) and runtime, while continuing to support development of the existing Angular.
Types of code include:
- Framework packages (core, common, router, etc).
- Core framework tests
- Documentation examples & tests
- Example applications (Hello World, TODO, etc)
- Downstream libraries that we want to test (Material)
In the legacy environment (View Engine), the decision of whether to run code under JIT or AOT is a runtime decision. By calling either the platformBrowserDynamic
or platformBrowser
API, either JIT or AOT mode will be utilized. The compiled code includes decorator information downleveled to annotations, which supports either case.
Libraries (such as common) are compiled with ngc
internally in the ng_package
rule, and published to NPM with enough information to run in either JIT or AOT mode, as determined by the application at runtime. In an Ivy environment, a library must decide at build time whether it wants to be AOT compiled, or preserve decorator information (which will trigger JIT compilation of the library at runtime). This causes issues.
We want to eventually start testing Ivy in google3. The rollout plan has two distinct phases:
In this phase, ngc
runs using the same ng_module
and summary-based metadata system as the legacy compiler. It generates Ivy .js code as well as .ngfactory.js shims which can be used with the existing bootstrap API.
Activating this phase allows us to test the Ivy runtime and individual decorator compilation in g3. It's activated by setting enableIvy
to true
in the angularCompilerOptions
section of tsconfig.json
.
In this phase, the new ngtsc
compiler is used as a whole, instead of the ngc
global analysis based compiler. This gets rid of the global analysis pipeline and all Angular-specific metadata formats (summaries, factories, etc). Dependency between compilation units now rely solely on .d.ts
file information.
Activating this phase allows us to test our entire new compiler/metadata pipeline, with full locality, in g3. It's activated by setting enableIvy
to "ngtsc"
in angularCompilerOptions
.
Given an Angular compilation unit (TS Program), then, we need to be able to compile it in 4 ways:
- Via
ngc
legacy compilation, to support continued development of pre-Ivy Angular. - As a pure
ts_library
with no AOT compilation, to enable testing of JIT. - With
ngc
in Ivy mode, as required by Google3 phase 1. - With
ngc
inngtsc
mode, as required by Google3 phase 2.
We want to be able to run our tests in all 4 of these modes.
Some tests will pass in certain modes but not in others. We need a plan for how to exclude specific tests from specific modes.
Eventually, we want to test Material using Ivy, which (as Material uses Bazel) faces a similar set of challenges.
In this proposal, Bazel's --define
flag is used to modify the behavior of ngc
and the ng_module
rule.
- With
--define=ivy=false
, the legacy pipeline runs. - With
--define=ivy=jit
, theng_module
rule behaves likets_library
. - With
--define=ivy=global
, theng_module
rule invokesngc
in global analysis mode (as in g3 phase 1) - With
--define=ivy=local
, theng_module
rule invokesngc
inngtsc
mode (as in g3 phase 2).
The actual mechanism of switching behavior could be left to ngc
(for example, ngc
could pretend to be tsc
if it detects ivy=jit
. However, Bazel rules need to declare their outputs, so the ng_module
rule must be aware of whether generated files (e.g. summaries & factories) will be produced in whatever mode is being executed.
If Material were converted to depend on Angular via a Bazel source dependency instead of via NPM, it could benefit from the --define=ivy
flag in the same way.
We could have a Git branch for each mode above, and change the BUILD.bazel rules or ng_module
implementation in each branch to build things accordingly. Ivy code would have to be synced between the branches.
Google Doc version with discussion https://docs.google.com/document/d/1WTzNZcZTuXeZjBKq4cmzy3BTJr6iFXZHph8B5RDzaXM