fix(compiler-cli): enforce a minimum version to be used when a library uses input transform (#51413)

Angular 16.1 introduced the input transform feature, requiring the partial compilation output to be extended
with a reference to the input transform function. This has resulted in a subtle breaking change, where older
versions of the Angular linker can no longer consume libraries that have started to use this feature.

We do try to support using a 16.1 library from an Angular 16.0 application, but if a library actually
adopts a new feature then this is no longer possible. In such cases, it is desirable to report a message
telling the user that their version of the Angular compiler is too old, as determined by the `"minVersion"`
property that is present in each partial declaration. This version would still indicate that the declaration
required at least Angular 14.0 to be compiled, but this is not accurate once input transforms are being
used. Consequently, this error would not be reported, causing a less informative error once the input transform
was being observed.

Fixes #51411

PR Close #51413
This commit is contained in:
JoostK 2023-08-17 22:14:38 +02:00 committed by Andrew Kushnir
parent 4663a33a0d
commit 5bd9fbd2c3
2 changed files with 10 additions and 3 deletions

View file

@ -111,7 +111,7 @@ function toNumber(value) {
export class MyDirective {
}
MyDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
MyDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyDirective, selector: "[my-directive]", inputs: { functionDeclarationInput: ["functionDeclarationInput", "functionDeclarationInput", toNumber], inlineFunctionInput: ["inlineFunctionInput", "inlineFunctionInput", (value, _) => value ? 1 : 0] }, ngImport: i0 });
MyDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "16.1.0", version: "0.0.0-PLACEHOLDER", type: MyDirective, selector: "[my-directive]", inputs: { functionDeclarationInput: ["functionDeclarationInput", "functionDeclarationInput", toNumber], inlineFunctionInput: ["inlineFunctionInput", "inlineFunctionInput", (value, _) => value ? 1 : 0] }, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyDirective, decorators: [{
type: Directive,
args: [{ selector: '[my-directive]' }]

View file

@ -22,7 +22,7 @@ import {toOptionalLiteralMap} from './util';
*
* Do not include any prerelease in these versions as they are ignored.
*/
const MINIMUM_PARTIAL_LINKER_VERSION = '14.0.0';
const MINIMUM_PARTIAL_LINKER_VERSION = '16.1.0';
/**
* Compile a directive declaration defined by the `R3DirectiveMetadata`.
@ -45,7 +45,14 @@ export function createDirectiveDefinitionMap(meta: R3DirectiveMetadata):
DefinitionMap<R3DeclareDirectiveMetadata> {
const definitionMap = new DefinitionMap<R3DeclareDirectiveMetadata>();
definitionMap.set('minVersion', o.literal(MINIMUM_PARTIAL_LINKER_VERSION));
const hasTransformFunctions =
Object.values(meta.inputs).some(input => input.transformFunction !== null);
// Note: in order to allow consuming Angular libraries that have been compiled with 16.1+ in
// Angular 16.0, we only force a minimum version of 16.1 if input transform feature as introduced
// in 16.1 is actually used.
const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION : '14.0.0';
definitionMap.set('minVersion', o.literal(minVersion));
definitionMap.set('version', o.literal('0.0.0-PLACEHOLDER'));
// e.g. `type: MyDirective`