Correct way to package an Angular library to support Angular 8, 9 and 10

Viewed 2401

With Angular 10 being released, I am updating the version of Angular used to build a library + demo application to version 10.

Generally this goes smoothly, and the library remains compatible with previous versions of Angular, but this doesn't seem to be the case with this release (prior version build against Angular 9 works fine with Angular 8).

The output typescript definition files include:

import * as ɵngcc0 from '@angular/core';

...

static ɵfac: ɵngcc0.ɵɵFactoryDef<QrCodeComponent, never>;
static ɵcmp: ɵngcc0.ɵɵComponentDefWithMeta<QrCodeComponent, "qr-code", never, { "value": "value"; "size": "size"; "errorCorrectionLevel": "errorCorrectionLevel"; }, {}, never, never>;

Which causes errors like this when consuming in an Angular 8 project:

 ERROR in node_modules/ng-qrcode/lib/qr-code.component.d.ts(7,25): error TS2694: Namespace '"/ngqrcode-ng8-test/node_modules/@angular/core/core"' has no exported member 'ɵɵFactoryDef'.
    node_modules/ng-qrcode/lib/qr-code.component.d.ts(8,18): error TS2314: Generic type 'ɵɵComponentDefWithMeta' requires 6 type argument(s).
    node_modules/ng-qrcode/lib/qr-code.directive.d.ts(13,25): error TS2694: Namespace '"/ngqrcode-ng8-test/node_modules/@angular/core/core"' has no exported member 'ɵɵFactoryDef'.

I created the test angular 8 project freshly using:

npx @angular/cli@^8 new ngqrcode-ng8-test

You can see my WIP pull request for this change here: https://github.com/mnahkies/ng-qrcode/pull/8

Note: I already have enableIvy false in my compiler options:

"angularCompilerOptions": {
    "enableIvy": false
  }

Is there a way to build a library using Angular v10 that maintains compatibility with Angular v8?

2 Answers

There are 2 big breaking changes in angular 10:

  1. 10 requires tslib 2.0.0 vs. <10 requires tslib < 2.0.0,
  2. 10 uses typescript 3.9 vs. < 10 requires typescript < 3.9

I don't think you can package a lib for 8, 9, and 10 in one single package :-)

Basically, this is the matter of a build engine using on the library side and on the consumer application end. The Ivy replaces the ViewEngine in the Angular applications started from Angular v9, and the transition is going to be completed with Angular v13, which is already here in the end of 2021. Speaking of Angular v8 support, I'd say, you need to build your library with Ivy turned off, which means the ViewEngine is on (tsconfig.json):

  "angularCompilerOptions": {
    "enableIvy": false,
    ...
  }

That's because ViewEngine is the default option for Angular v8 applications, and Ivy-compiled libraries don't work with ViewEngine-compiled apps. So the second part of the issue is how the app is compiled. There is an engine compatibility table for 9-11 (unfortunately, I didn't find 8-12 table):

enter image description here

As an example of Angular 7-12 lib compatibility I may refer to my own library, ngx-ui-scroll v2: https://github.com/dhilt/ngx-ui-scroll/tree/v2.3.1. In v2 it uses custom rollup-based build process with enableIvy: false. In v3 I think I'll switch to Ivy and drop Angular ViewEngine-apps support. More information for libraries in Angular v13 can be taken from the official blog: Upcoming improvements to Angular library distribution.

Another part of the support story is TypeScript versions compatibility. Angular v8 app may use TS v3.4, Angular v13 may use TS 4.5, and, believe me, there are not only v3 -> v4 breaking changes (for example look into this 3.6 -> 3.7 break). The app must have skipLibCheck option set to true to ignore such issues during compilation (tsconfig.json):

  "compilerOptions": {
    "skipLibCheck": true,
    ...
  }
Related