question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Transition Angular from the whole-program compilation to localized compilation

See original GitHub issue

Note 1: This proposal has its origin in Out-of-band type-checking for TypeScript and Template compilation which mentions an “extreme” implementation option — this issue provides more details about this option.

Note 2: 📺 Check out the video recording of a community discussion about this proposal @ angularnation.net

The problem

As described in the Out-of-band type-checking for TypeScript and Template compilation proposal, making Angular’s compilation model highly parallelizable would result in major benefits for Angular developers by significantly lowering the interactive workflow latency (making the “edit & refresh” cycle faster).

This change would additionally improve the efficiency (in time, and CPU cost) of building Angular at scale in monorepos and on CI systems.

The current cost of compilation is significant, and becoming a hurdle affecting DX, and our ability to scale massive code bases.

Background info

The unit of parallelization of Angular’s compilation is the “program” as defined by the TypeScript compiler — the program is the smallest unit of code (a group of .ts, .d.ts, and .js files declared and configured by a single tsconfig.json file) that the TypeScript compiler can process independently of other code.

A typical Angular compilation unit is a program that is composed of several (often many) components, directives, pipes, services, NgModules, and generic TypeScript files.

A small application is commonly built as a single program, that often depends on 3rd party Angular libraries (that themselves were compiled as individual programs). Larger code bases, split up their app into multiple programs using Angular CLI workspaces, Bazel’s rules_nodejs (or the ng_module rule), or Nx.

These approaches to scaling help, but don’t solve the ultimate problem, which is that it is currently not possible to process a single Angular Component and transform it to runnable JS without processing a bigger chunk of the code base as well (the whole program). This prevents us from taking advantage of massive parallelization that modern hardware in combination with highly parallelizable transpilation engines like esbuild, (and Vite built on top of it) and Bazel offer. This results in wasted human and CPU time and ultimately in loss of focus, creativity, and high CI bills.

Proposed solution

Let’s transition the processing model of Angular code from the “whole world” compilation model (global optimization), to localized compilation (and as close to transpilation as possible). In an extreme example, this would mean that a single .ts file with an Angular component could be converted to .json its own without having to read or process any other files.

In practice, it’s more likely that we’ll need to read and process several files around this single .ts file because of references to external templates and style sheets, and we’ll need the imported NgModules and (upcoming “standalone components, directives, and pipes” — check out the RFC) to be at least read, if not compiled as well, because they represent transitive dependencies of the component being processed.

Aiming for purely syntactic transpilation of template and metadata to JavaScript code would most likely be a “bridge too far” (see “Alternatives considered” below), but getting as close as possible to that ideal is at the core of this proposal.

We don’t need purely syntactic transpilation to still reap most of the benefits of parallelization, localized compilation would result in a huge improvement already.

Implementation details

  • Out-of-band type-checking for TypeScript and Template compilation is a most likely a prerequisite for this project, because we need to decouple type-checking from code generation.
  • Moving away from NgModules, to module-less, a.k.a. “standalone” components, directives, and pipes would provide us with focused understand the context within which a component or a directive are being compiled — fortunately we want to execute on this project regardless of this proposal in order to reduce verbosity and complexity of Angular, so this prerequisite is well aligned with our ongoing work.
  • We’d need to rethink our compiler architecture, and investigate if building it on top of the TypeScript AST is the right approach or if we should consider more lightweight alternatives. A lot of research in this area needs to be done to make the right call, but considering that we absolutely want to keep TypeScript for type-checking, IDE/Language Service support, etc, it might make a lot of sense to stick with it and find ways to make it fast enough.
  • Once the compiler supports localized compilation, we’d need to build plugins for esbuild or its alternative like swc.
  • Angular CLI’s build pipeline would have to be revamped to support this processing model. We could even consider adopting Vite since they’ve done a lot of awesome work already 👍🏻.
  • The impact on this change on other parts of Angular, like the language-service, google3 integration, and others is less clear at the moment, and will need to be researched.

Trade offs

  • Switching to transpilation represents a radical shift for the design of both the Angular compiler and runtime, and has an impact on how type-checking should work. All of this represents a significant effort, which would take time.

Web ecosystem compatibility & Prior art

All of the following use transpilation or localized compilation approach:

What’s the goal of this issue?

The goal is to write down some of the ideas that have been made possible thanks to Ivy and start collecting early feedback that could be used as an input for future RFC.

Alternatives considered

  • We could switch over to an existing templating solution that is transpilable (JSX, lit), but these systems have fundamentally different component composition & change detection models, which would result in a major breaking change and regressions in runtime performance in some scenarios (due to our inability to perform certain optimizations). This change would not be worth the trouble.

  • We could modify the template syntax to make it more suitable for purely syntactic transpilation (for example by removing selector matching)— while this might be worth doing in the future (pending more exploration of costs vs benefits), we can achieve most of the goals of this proposal by changing how Angular works internally while preserving backwards compatibility for most of the code out there and the investment into these code bases.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:218
  • Comments:11 (9 by maintainers)

github_iconTop GitHub Comments

3reactions
yharaskrikcommented, Sep 1, 2021

Wow. This is amazing! I love it. 👍👍 Two thumbs up indeed! Compilers are not my forte so I don’t have a lot to contribute but I wholeheartedly support this improvement in the framework.

3reactions
alxhubcommented, Aug 16, 2021

What if we create a tool (or more precisely extract this from the existing tools), that builds an in-memory (and persisting on disk) repository of all the meta-data the angular compiler needs? This thing can be updated when a unit gets compiled. In our current version, we need to do a “full-cycle” to populate this, but going forward this can become more and more granular. Having this lib in the memory means we can compile and typecheck a component/pipe/directive/module/whatever in relative isolation.

This is pretty much how the compiler’s incremental build system (aka “watch mode”) which powers ng serve currently functions. The main source of complexity here is that changes in Angular code are frequently non-local - editing an @NgModule for example can affect the compilation of components in many other files, depending on how that @NgModule is imported/re-exported across the program. So to accurately and efficiently react to a change in its input, the compiler has not only an in-memory “database” of all of the metadata for each class, but also understands the dependencies between these artifacts and how a change to one will propagate through the others. This is extremely nontrivial, but is necessary for reasonable incremental performance.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Animation transitions and triggers
This guide goes into depth on special transition states such as the * wildcard and void . It shows how these special states...
Read more >
Steps toward Compiling Program Interfaces as Languages
Interface Compilation: Steps toward Compiling Program Interfaces as Languages. Dawson R. Engler. Computer Science Laboratory. Stanford University.
Read more >
Angular Vietnam | có vài topic mơiz khá là hóng
Transition Angular from the whole-program compilation to localized compilation · Issue #43165 ·. github.com. Transition Angular from the whole-program ...
Read more >
ng-japan OnAir vol.45 Monthly Angular 2021-08
ng-japan OnAirはAngular日本ユーザー会が主催するオンラインイベントです ... from the whole-program compilation to localized compilation ...
Read more >
Error Handling in Compiler Design
When an error is discovered, the parser performs local correction on the ... the parser analyzes the whole program and tries to find...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found