From 9ce2be4f247d118bc8cc572a69343093c12a7923 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Mon, 2 Oct 2023 12:59:36 +0200 Subject: [PATCH 1/2] fix(compiler): enable block syntax in the linker Adds some logic to enable parsing of block syntax in the linker. Note that the syntax is only enabled on code compiled with Angular v17 or later. --- .../linker/src/file_linker/file_linker.ts | 2 +- .../partial_component_linker_1.ts | 24 +++++++--- .../partial_linkers/partial_linker.ts | 4 +- .../partial_linker_selector.ts | 5 +- .../src/file_linker/partial_linkers/util.ts | 2 + .../test/file_linker/file_linker_spec.ts | 48 +++++++++++++++++-- 6 files changed, 71 insertions(+), 14 deletions(-) diff --git a/packages/compiler-cli/linker/src/file_linker/file_linker.ts b/packages/compiler-cli/linker/src/file_linker/file_linker.ts index d30f8f88855a..14c55d41e937 100644 --- a/packages/compiler-cli/linker/src/file_linker/file_linker.ts +++ b/packages/compiler-cli/linker/src/file_linker/file_linker.ts @@ -69,7 +69,7 @@ export class FileLinker { const minVersion = metaObj.getString('minVersion'); const version = metaObj.getString('version'); const linker = this.linkerSelector.getLinker(declarationFn, minVersion, version); - const definition = linker.linkPartialDeclaration(emitScope.constantPool, metaObj); + const definition = linker.linkPartialDeclaration(emitScope.constantPool, metaObj, version); return emitScope.translateDefinition(definition); } diff --git a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_component_linker_1.ts b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_component_linker_1.ts index 680f87ce376c..7bee7d196420 100644 --- a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_component_linker_1.ts +++ b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_component_linker_1.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {BoundTarget, ChangeDetectionStrategy, compileComponentFromMetadata, ConstantPool, DeclarationListEmitMode, DEFAULT_INTERPOLATION_CONFIG, ForwardRefHandling, InterpolationConfig, makeBindingParser, outputAst as o, parseTemplate, R3ComponentMetadata, R3DeclareComponentMetadata, R3DeclareDirectiveDependencyMetadata, R3DeclarePipeDependencyMetadata, R3DeferBlockMetadata, R3DirectiveDependencyMetadata, R3PartialDeclaration, R3TargetBinder, R3TemplateDependencyKind, R3TemplateDependencyMetadata, SelectorMatcher, TmplAstDeferredBlock, TmplAstDeferredBlockTriggers, TmplAstDeferredTrigger, TmplAstElement, ViewEncapsulation} from '@angular/compiler'; +import semver from 'semver'; import {AbsoluteFsPath} from '../../../../src/ngtsc/file_system'; import {Range} from '../../ast/ast_host'; @@ -15,7 +16,7 @@ import {GetSourceFileFn} from '../get_source_file'; import {toR3DirectiveMeta} from './partial_directive_linker_1'; import {LinkedDefinition, PartialLinker} from './partial_linker'; -import {extractForwardRef} from './util'; +import {extractForwardRef, PLACEHOLDER_VERSION} from './util'; function makeDirectiveMetadata( directiveExpr: AstObject, @@ -49,22 +50,27 @@ export class PartialComponentLinkerVersion1 implements private code: string) {} linkPartialDeclaration( - constantPool: ConstantPool, - metaObj: AstObject): LinkedDefinition { - const meta = this.toR3ComponentMeta(metaObj); + constantPool: ConstantPool, metaObj: AstObject, + version: string): LinkedDefinition { + const meta = this.toR3ComponentMeta(metaObj, version); return compileComponentFromMetadata(meta, constantPool, makeBindingParser()); } /** * This function derives the `R3ComponentMetadata` from the provided AST object. */ - private toR3ComponentMeta(metaObj: AstObject): - R3ComponentMetadata { + private toR3ComponentMeta( + metaObj: AstObject, + version: string): R3ComponentMetadata { const interpolation = parseInterpolationConfig(metaObj); const templateSource = metaObj.getValue('template'); const isInline = metaObj.has('isInline') ? metaObj.getBoolean('isInline') : false; const templateInfo = this.getTemplateInfo(templateSource, isInline); + // Enable the new block syntax if compiled with v17 and + // above, or when using the local placeholder version. + const supportsBlockSyntax = semver.major(version) >= 17 || version === PLACEHOLDER_VERSION; + const template = parseTemplate(templateInfo.code, templateInfo.sourceUrl, { escapedString: templateInfo.isEscaped, interpolationConfig: interpolation, @@ -74,6 +80,12 @@ export class PartialComponentLinkerVersion1 implements metaObj.has('preserveWhitespaces') ? metaObj.getBoolean('preserveWhitespaces') : false, // We normalize line endings if the template is was inline. i18nNormalizeLineEndingsInICUs: isInline, + + // TODO(crisbeto): hardcode the supported blocks for now. Before the final release + // `enabledBlockTypes` will be replaced with a boolean, at which point `supportsBlockSyntax` + // can be passed in directly here. + enabledBlockTypes: supportsBlockSyntax ? new Set(['if', 'switch', 'for', 'defer']) : + undefined, }); if (template.errors !== null) { const errors = template.errors.map(err => err.toString()).join('\n'); diff --git a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker.ts b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker.ts index 8baf54a19887..680db8a33eca 100644 --- a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker.ts +++ b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker.ts @@ -28,6 +28,6 @@ export interface PartialLinker { * `R3DeclareComponentMetadata` interfaces. */ linkPartialDeclaration( - constantPool: ConstantPool, - metaObj: AstObject): LinkedDefinition; + constantPool: ConstantPool, metaObj: AstObject, + version: string): LinkedDefinition; } diff --git a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker_selector.ts b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker_selector.ts index 8bfa59796fdb..03e0cbf9d33f 100644 --- a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker_selector.ts +++ b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_linker_selector.ts @@ -21,6 +21,7 @@ import {PartialInjectorLinkerVersion1} from './partial_injector_linker_1'; import {PartialLinker} from './partial_linker'; import {PartialNgModuleLinkerVersion1} from './partial_ng_module_linker_1'; import {PartialPipeLinkerVersion1} from './partial_pipe_linker_1'; +import {PLACEHOLDER_VERSION} from './util'; export const ɵɵngDeclareDirective = 'ɵɵngDeclareDirective'; export const ɵɵngDeclareClassMetadata = 'ɵɵngDeclareClassMetadata'; @@ -68,7 +69,7 @@ export function createLinkerMap( environment: LinkerEnvironment, sourceUrl: AbsoluteFsPath, code: string): Map[]> { const linkers = new Map[]>(); - const LATEST_VERSION_RANGE = getRange('<=', '0.0.0-PLACEHOLDER'); + const LATEST_VERSION_RANGE = getRange('<=', PLACEHOLDER_VERSION); linkers.set(ɵɵngDeclareDirective, [ {range: LATEST_VERSION_RANGE, linker: new PartialDirectiveLinkerVersion1(sourceUrl, code)}, @@ -143,7 +144,7 @@ export class PartialLinkerSelector { } const linkerRanges = this.linkers.get(functionName)!; - if (version === '0.0.0-PLACEHOLDER') { + if (version === PLACEHOLDER_VERSION) { // Special case if the `version` is the same as the current compiler version. // This helps with compliance tests where the version placeholders have not been replaced. return linkerRanges[linkerRanges.length - 1].linker; diff --git a/packages/compiler-cli/linker/src/file_linker/partial_linkers/util.ts b/packages/compiler-cli/linker/src/file_linker/partial_linkers/util.ts index c2e4e30c1b97..c7ae16836ad4 100644 --- a/packages/compiler-cli/linker/src/file_linker/partial_linkers/util.ts +++ b/packages/compiler-cli/linker/src/file_linker/partial_linkers/util.ts @@ -10,6 +10,8 @@ import {createMayBeForwardRefExpression, ForwardRefHandling, MaybeForwardRefExpr import {AstObject, AstValue} from '../../ast/ast_value'; import {FatalLinkerError} from '../../fatal_linker_error'; +export const PLACEHOLDER_VERSION = '0.0.0-PLACEHOLDER'; + export function wrapReference(wrapped: o.WrappedNodeExpr): R3Reference { return {value: wrapped, type: wrapped}; } diff --git a/packages/compiler-cli/linker/test/file_linker/file_linker_spec.ts b/packages/compiler-cli/linker/test/file_linker/file_linker_spec.ts index 4d761cb63067..3effd9c02af9 100644 --- a/packages/compiler-cli/linker/test/file_linker/file_linker_spec.ts +++ b/packages/compiler-cli/linker/test/file_linker/file_linker_spec.ts @@ -120,6 +120,48 @@ describe('FileLinker', () => { }); }); + describe('block syntax support', () => { + function linkComponentWithTemplate(version: string, template: string): string { + // Note that the `minVersion` is set to the placeholder, + // because that's what we have in the source code as well. + const source = ` + ɵɵngDeclareComponent({ + minVersion: "0.0.0-PLACEHOLDER", + version: "${version}", + ngImport: core, + template: \`${template}\`, + isInline: true, + type: SomeComp + }); + `; + + // We need to create a new source file here, because template parsing requires + // the template string to have offsets which synthetic nodes do not. + const {fileLinker} = createFileLinker(source); + const sourceFile = ts.createSourceFile('', source, ts.ScriptTarget.Latest, true); + const call = + (sourceFile.statements[0] as ts.ExpressionStatement).expression as ts.CallExpression; + const result = fileLinker.linkPartialDeclaration( + 'ɵɵngDeclareComponent', [call.arguments[0]], new MockDeclarationScope()); + return ts.createPrinter().printNode(ts.EmitHint.Unspecified, result, sourceFile); + } + + it('should enable block syntax if compiled with version 17 or above', () => { + for (const version of ['17.0.0', '17.0.1', '17.1.0', '17.0.0-next.0', '18.0.0']) { + expect(linkComponentWithTemplate(version, '@defer {}')).toContain('ɵɵdefer('); + } + }); + + it('should enable block syntax if compiled with a local version', () => { + expect(linkComponentWithTemplate('0.0.0-PLACEHOLDER', '@defer {}')).toContain('ɵɵdefer('); + }); + + it('should not enable block syntax if compiled with a version older than 17', () => { + expect(linkComponentWithTemplate('16.2.0', '@Input() is a decorator. This is a brace }')) + .toContain('@Input() is a decorator. This is a brace }'); + }); + }); + describe('getConstantStatements()', () => { it('should capture shared constant values', () => { const {fileLinker} = createFileLinker(); @@ -179,9 +221,9 @@ describe('FileLinker', () => { }); }); - function createFileLinker(): { + function createFileLinker(code = '// test code'): { host: AstHost, - fileLinker: FileLinker + fileLinker: FileLinker, } { const fs = new MockFileSystemNative(); const logger = new MockLogger(); @@ -189,7 +231,7 @@ describe('FileLinker', () => { fs, logger, new TypeScriptAstHost(), new TypeScriptAstFactory(/* annotateForClosureCompiler */ false), DEFAULT_LINKER_OPTIONS); const fileLinker = new FileLinker( - linkerEnvironment, fs.resolve('/test.js'), '// test code'); + linkerEnvironment, fs.resolve('/test.js'), code); return {host: linkerEnvironment.host, fileLinker}; } }); From 77ebf3261d52b8c8b41b4f10d1000edef9f79250 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Mon, 2 Oct 2023 13:46:06 +0200 Subject: [PATCH 2/2] fix(compiler): update the minVersion if component uses block syntax Increases the `minVersion` of component declarations that use bloks to v17 in order to indicate to users that they need to update if the library they're using is on the new syntax, while preserving backwards compatibility for libraries that do not use the syntax. --- .../GOLDEN_PARTIAL.js | 58 +++++++++---------- .../GOLDEN_PARTIAL.js | 26 ++++----- .../compiler/src/render3/partial/component.ts | 54 +++++++++++++++++ packages/compiler/src/render3/view/util.ts | 8 ++- 4 files changed, 103 insertions(+), 43 deletions(-) diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/GOLDEN_PARTIAL.js index 2345b87092e1..2aed3e46bb38 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_control_flow/GOLDEN_PARTIAL.js @@ -12,7 +12,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @switch (value()) { @@ -79,7 +79,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @switch (value()) { @@ -141,7 +141,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @switch (value()) { @@ -234,7 +234,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
{{message}} @switch (value() | test) { @@ -302,7 +302,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @if (value()) { @@ -347,7 +347,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @if (value()) { @@ -397,7 +397,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @if (value() === 1) { @@ -456,7 +456,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @if (val === 0) { @@ -541,7 +541,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
{{message}} @if ((val | test) === 1) { @@ -601,7 +601,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @if (value(); as alias) { @@ -645,7 +645,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` @if (value(); as root) { Root: {{value()}}/{{root}} @@ -699,7 +699,7 @@ export class MyApp { log(..._) { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` @if (value(); as root) { @@ -754,7 +754,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @for (item of items; track item) { @@ -801,7 +801,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @for (item of items; track item) { @@ -852,7 +852,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @for (item of items; track $index) { @@ -899,7 +899,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @for (item of items; track item.name[0].toUpperCase()) { @@ -950,7 +950,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @for (item of items; track item) { @@ -1004,7 +1004,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @for (item of items; track item) { @@ -1059,7 +1059,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @for (item of items; track item; let idx = $index, f = $first; let l = $last, ev = $even, o = $odd; let co = $count) { @@ -1118,7 +1118,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @for (item of items; track item; let outerCount = $count) { @@ -1175,7 +1175,7 @@ export class MyApp { log(..._) { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @for (item of items; track item; let ev = $even) { @@ -1220,7 +1220,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `@for (item of items; track item) { +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `@for (item of items; track item) { {{$odd + ''}} }`, isInline: true }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, decorators: [{ @@ -1255,7 +1255,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` @for (item of items; track item) { {{item}} @@ -1301,7 +1301,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` {{$index}} {{$count}} {{$first}} {{$last}} @for (item of items; track item) { @@ -1355,7 +1355,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @for (item of items; track trackFn($index, item)) {} @@ -1402,7 +1402,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @@ -1450,7 +1450,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` @for (item of items; track item.name[0].toUpperCase()) { {{item.name}} } @@ -1505,7 +1505,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` @for (item of items; track trackFn(item, message)) { {{item.name}} } @@ -1560,7 +1560,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` @for (item of items; track trackFn({foo: item, bar: item}, [item, item])) { {{item.name}} } diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/GOLDEN_PARTIAL.js index 1cd8d81f7cb5..2b6c76eba071 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_deferred/GOLDEN_PARTIAL.js @@ -9,7 +9,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @defer {Deferred content} @@ -51,7 +51,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: `
{{message}} @defer { @@ -104,7 +104,7 @@ import * as i0 from "@angular/core"; export class MyApp { } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` @defer { } @placeholder (minimum 2s) { @@ -141,7 +141,7 @@ import * as i0 from "@angular/core"; export class MyApp { } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` @defer { } @loading(minimum 2s; after 500ms) { @@ -202,7 +202,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE export class MyApp { } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
@defer { @@ -331,7 +331,7 @@ import * as i0 from "@angular/core"; export class MyApp { } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: `
@defer { @@ -383,7 +383,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` {{message}} @defer ( when isVisible() || isReady; @@ -442,7 +442,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` {{message}} @defer ( prefetch when isVisible() || isReady; @@ -512,7 +512,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, isStandalone: true, selector: "ng-component", ngImport: i0, template: ` {{message}} @defer (when isVisible() && (isReady | testPipe)) { Hello @@ -560,7 +560,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` {{message}} @defer (on interaction(button); prefetch on interaction(button)) {} @@ -611,7 +611,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` {{message}} @@ -662,7 +662,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` {{message}} @defer (on interaction(button); prefetch on interaction(button)) { Main @@ -713,7 +713,7 @@ export class MyApp { } } MyApp.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: MyApp, deps: [], target: i0.ɵɵFactoryTarget.Component }); -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "ng-component", ngImport: i0, template: ` @defer (on hover, interaction, viewport; prefetch on hover, interaction, viewport) { {{message}} } @placeholder { diff --git a/packages/compiler/src/render3/partial/component.ts b/packages/compiler/src/render3/partial/component.ts index 4f89d03625f3..23913ed141eb 100644 --- a/packages/compiler/src/render3/partial/component.ts +++ b/packages/compiler/src/render3/partial/component.ts @@ -9,6 +9,7 @@ import * as core from '../../core'; import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config'; import * as o from '../../output/output_ast'; import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../parse_util'; +import {RecursiveVisitor, visitAll} from '../r3_ast'; import {Identifiers as R3} from '../r3_identifiers'; import {generateForwardRef, R3CompiledExpression} from '../util'; import {DeclarationListEmitMode, R3ComponentMetadata, R3TemplateDependencyKind, R3TemplateDependencyMetadata} from '../view/api'; @@ -71,12 +72,21 @@ export function createComponentDefinitionMap( templateInfo: DeclareComponentTemplateInfo): DefinitionMap { const definitionMap: DefinitionMap = createDirectiveDefinitionMap(meta); + const blockVisitor = new BlockPresenceVisitor(); + visitAll(blockVisitor, template.nodes); definitionMap.set('template', getTemplateExpression(template, templateInfo)); + if (templateInfo.isInline) { definitionMap.set('isInline', o.literal(true)); } + // Set the minVersion to 17.0.0 if the component is using at least one block in its template. + // We don't do this for templates without blocks, in order to preserve backwards compatibility. + if (blockVisitor.hasBlocks) { + definitionMap.set('minVersion', o.literal('17.0.0')); + } + definitionMap.set('styles', toOptionalLiteralArray(meta.styles, o.literal)); definitionMap.set('dependencies', compileUsedDependenciesMetadata(meta)); definitionMap.set('viewProviders', meta.viewProviders); @@ -189,3 +199,47 @@ function compileUsedDependenciesMetadata(meta: R3ComponentMetadata { set(key: keyof T, value: o.Expression|null): void { if (value) { - this.values.push({key: key as string, value, quoted: false}); + const existing = this.values.find(value => value.key === key); + + if (existing) { + existing.value = value; + } else { + this.values.push({key: key as string, value, quoted: false}); + } } }