The Angular compatibility compiler in depth
Angular version 9 ships with the Angular compatibility compiler, commonly referred to as ngcc. This tool transforms View Engine-based Angular package dependencies into Ivy-compatible code, covering 3rd party libraries, internal libraries, and the Angular sub-packages themselves. The Angular team has not yet set a removal date for this compatibility compiler, but based on their published recommendations for library authors, we can expect it to remain available throughout Angular versions 10 and 11.
The official guidance from the Angular team for library maintainers is straightforward:
- Keep distributing View Engine AOT-compiled Angular libraries for Angular version 9.
- Transition to distributing AOT-compiled Ivy libraries starting with Angular version 10.
Validating Ivy compatibility
Working with the Angular compatibility compiler might introduce necessary adjustments to our libraries. To confirm that our library functions correctly with ngcc, we should add it to the Angular Ivy library compatibility validation project, which is maintained by the Angular team.
The readme file for this project provides instructions for adding our library's tests, which will then run against each new release of the compatibility compiler. This serves as the official endorsement for Angular libraries. By January 2020, a total of 185 libraries had been registered in this validation project.
Given that Angular version 9 marks the initial stable release—after numerous fixes arrived following the version 8 opt-in Ivy preview—and that the Ivy Instruction Set will remain in progress until the arrival of Angular version 10, we should anticipate encountering and resolving issues with the compatibility compiler throughout 2020.
Detecting Angular Ivy
For those of us maintaining libraries that support both Angular versions 9 and 10, and need to accommodate differences between View Engine and Ivy, implementing detection logic similar to that shown in Listing 1 becomes necessary.
import {
Type,
ɵNG_COMP_DEF,
ɵNG_DIR_DEF,
ɵNG_MOD_DEF,
ɵNG_PIPE_DEF,
} from '@angular/core';
function isIvy(): boolean {
const ng: any = ((self || global || window) as any).ng;
return ng === undefined
|| ng.getComponent !== undefined
|| ng.applyChanges !== undefined;
}
function isIvyComponent(componentType: Type<any>): boolean {
return (componentType as any)[ɵNG_COMP_DEF] !== undefined;
}
function isIvyDirective(directiveType: Type<any>): boolean {
return (directiveType as any)[ɵNG_DIR_DEF] !== undefined;
}
function isIvyModule(moduleType: Type<any>): boolean {
return (moduleType as any)[ɵNG_MOD_DEF] !== undefined;
}
function isIvyPipe(pipeType: Type<any>): boolean {
return (pipeType as any)[ɵNG_PIPE_DEF] !== undefined;
}
Listing 1. Angular Ivy detection logic.
Migration schematics now target libraries
Beginning with Angular CLI version 9, the migration schematics run by ng add and ng update now extend their reach to library projects within our workspaces. This is welcome news for those of us developing libraries.
Keeping TypeScript versions current
Angular version 9 supports TypeScript versions 3.6 and 3.7. It is essential that our Angular libraries maintain compatibility with these two versions. With the release of Angular version 9, support for TypeScript version 3.5 has been discontinued, as indicated in Table 2.
_Table 2. Angular CLI, Angular, Node.js and TypeScript compatibility table. Open in new tab.*
At this point, things become challenging because TypeScript does not adhere to semantic versioning. Every minor release carries the potential for breaking changes. This actually holds true for TypeScript version 3.6, which is particularly relevant for library authors.
As of TypeScript version 3.6, the emitted type declaration files (*.d.ts) now include class getters and setters. This breaks backward compatibility with earlier TypeScript releases. Since Angular applications generally lock onto the one or two TypeScript versions supported by their specific Angular CLI and Angular version—as shown in Table 2—supporting multiple Angular versions requires downleveling our output type declarations. The tool downlevel-dts created by Nathan Shively-Sanders handles this task.
This situation mirrors what happened with TypeScript version 2.1, which was addressed by the similarly named tool dts-downlevel from Christopher Thielen. Even today, libraries like Jasmine types distribute multiple versions of TypeScript declarations to accommodate the various breaking changes introduced across TypeScript versions.
A prudent practice is to target the lowest package dependency version you intend to support for your consumers. For this reason, I suggest using TypeScript version 3.6 in your Angular library, as it is the minimum version compatible with Angular CLI version 9.0.
Angular Ivy capabilities for UI libraries
Component harnesses
For libraries that qualify as UI libraries—meaning they expose directives, components, and pipes—implementing component harnesses through Angular CDK is worth considering, as this is a new feature in Angular version 9.
These harnesses serve well in our internal testing, including unit, integration, and end-to-end tests. Additionally, we can provide component harnesses to consumers, allowing them to use these in their tests without coupling those tests to our implementation specifics such as data binding APIs or DOM structure.
Angular Material version 9 leads the way as the first library to expose component harnesses for its components.
Eliminating entryComponents metadata
With Ivy, there's no longer a requirement to explicitly declare an array of entryComponents for dynamically rendered components. In Ivy, every component is considered a potential entry component. Consequently, the entryComponents metadata is deprecated for NgModule in Angular version 9, and it could be removed as early as Angular version 11, potentially arriving by late 2020 or early 2021.
Avoid relying on the Ivy Instruction Set for now
The Ivy Instruction Set resembles a form of assembly language or bytecode designed for handling DOM content and updates. This straightforward model will certainly enable several advanced use cases. In fact, it might permit using alternative template engines or crafting our own, such as NG-VDOM—which, incidentally, is also compatible with View Engine.
Although experimentation with the instruction set is possible, we should refrain from directly depending on it just yet, given that it won't be finalized until Angular version 10, as previously discussed.
Avoid depending on experimental APIs
New low-level API members including ɵrenderComponent, ɵmarkDirty, and ɵdetectChanges will make it easier to implement complex scenarios, yet the small theta symbol (ɵ) prefix indicates that these are private, experimental, or unstable.
Given that, we should avoid depending on them, at least while working with Angular version 9.
Angular Ivy features for service libraries
For libraries that expose services, Angular Ivy version 9 brings an intriguing new capability: additional provider scopes. Beyond the existing providedIn: 'root', we now have the option to add providers within the 'any' and 'platform' scopes.
When writing tests for our libraries, remember that TestBed.get has been superseded by the strongly typed TestBed.inject.
Building and shipping an Angular Ivy library
Beyond the considerations discussed in this article, you can still author libraries for Angular Ivy the same way you did for View Engine, using the Angular CLI with its library builder.
These are the resources I suggest you look into:
- The official Angular guide on authoring libraries
- Making your Angular 2 library statically analyzable for AoT
- The ultimate guide to set up your Angular library project
- How to Build a Component Library with Angular and Storybook
- How to compile your Angular components library into Web Components
Wrapping up
If you are involved in maintaining an Angular library, or you are planning to take one on, you now have a clear picture of what Angular Ivy requires:
- Continue shipping a View Engine AOT-compiled package for Angular version 9.
- Provide an Ivy AOT-compiled package for Angular version 10.
- Add your library to the Angular Ivy library compatibility validation project.
- Address any Ivy compatibility problems that come up.
- Handle the differences between View Engine and Ivy by relying on Ivy detection logic.
- Ensure you are at a minimum supporting and using TypeScript version 3.6.
Points 3 through 6 are actionable right away. There is no need to wait for Angular version 10 to start aligning your library with Ivy.
