From e321cacc602dc3f1ff77c36f5f8ab9bbb65d9e29 Mon Sep 17 00:00:00 2001 From: Dylan Hunn Date: Wed, 24 Jan 2024 11:36:12 -0800 Subject: [PATCH] feat(compiler): Add a TSConfig option `useTemplatePipeline` The Template Pipeline is a brand new backend for the Angular compiler, replacing `TemplateDefinitionBuilder`. It generates the Ivy instructions corresponding to an input template (or host binding). The Template Pipeline has an all-new design based on an intermediate representation compiled over many phases, which will allow us to experiment with compiler changes more easily in the future. With this commit, the template pipeline can now be enabled in any project via the `useTemplatePipeline` TSConfig option. However, it is still disabled by default. --- .../partial_linkers/partial_component_linker_1.ts | 3 ++- .../partial_linkers/partial_directive_linker_1.ts | 4 +++- .../linker/src/file_linker/partial_linkers/util.ts | 2 ++ .../src/ngtsc/annotations/component/src/handler.ts | 7 ++++--- .../src/ngtsc/annotations/directive/src/handler.ts | 4 +++- .../src/ngtsc/annotations/directive/src/shared.ts | 7 +++++-- .../compiler-cli/src/ngtsc/core/api/src/public_options.ts | 5 +++++ packages/compiler-cli/src/ngtsc/core/src/compiler.ts | 6 +++++- packages/compiler/src/jit_compiler_facade.ts | 8 +++++++- packages/compiler/src/render3/view/api.ts | 4 ++++ packages/compiler/src/render3/view/compiler.ts | 4 ++-- 11 files changed, 42 insertions(+), 12 deletions(-) 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 025b52ee951b20..87f46eee98f94e 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 @@ -16,7 +16,7 @@ import {GetSourceFileFn} from '../get_source_file'; import {toR3DirectiveMeta} from './partial_directive_linker_1'; import {LinkedDefinition, PartialLinker} from './partial_linker'; -import {extractForwardRef, PLACEHOLDER_VERSION} from './util'; +import {extractForwardRef, PLACEHOLDER_VERSION, SHOULD_USE_TEMPLATE_PIPELINE_FOR_LINKER} from './util'; function makeDirectiveMetadata( directiveExpr: AstObject, @@ -196,6 +196,7 @@ export class PartialComponentLinkerVersion1 implements relativeContextFilePath: this.sourceUrl, i18nUseExternalIds: false, declarations, + useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_LINKER, }; } diff --git a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_directive_linker_1.ts b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_directive_linker_1.ts index 334ec65c527bfa..8c025d0c0bfd40 100644 --- a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_directive_linker_1.ts +++ b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_directive_linker_1.ts @@ -13,7 +13,7 @@ import {AstObject, AstValue} from '../../ast/ast_value'; import {FatalLinkerError} from '../../fatal_linker_error'; import {LinkedDefinition, PartialLinker} from './partial_linker'; -import {extractForwardRef, wrapReference} from './util'; +import {extractForwardRef, SHOULD_USE_TEMPLATE_PIPELINE_FOR_LINKER, wrapReference} from './util'; /** * A `PartialLinker` that is designed to process `ɵɵngDeclareDirective()` call expressions. @@ -145,6 +145,7 @@ function toHostMetadata(metaObj: AstObject(metaObj: AstObject value.getString()) : {}, specialAttributes, + useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_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 c7ae16836ad493..3ffd31520084b6 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 @@ -12,6 +12,8 @@ import {FatalLinkerError} from '../../fatal_linker_error'; export const PLACEHOLDER_VERSION = '0.0.0-PLACEHOLDER'; +export const SHOULD_USE_TEMPLATE_PIPELINE_FOR_LINKER = false; + export function wrapReference(wrapped: o.WrappedNodeExpr): R3Reference { return {value: wrapped, type: wrapped}; } diff --git a/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts b/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts index fef220e37bfae2..79948544289d8f 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts @@ -86,8 +86,8 @@ export class ComponentDecoratorHandler implements private hostDirectivesResolver: HostDirectivesResolver, private includeClassMetadata: boolean, private readonly compilationMode: CompilationMode, private readonly deferredSymbolTracker: DeferredSymbolTracker, - private readonly forbidOrphanRendering: boolean, - private readonly enableBlockSyntax: boolean) { + private readonly forbidOrphanRendering: boolean, private readonly enableBlockSyntax: boolean, + private readonly useTemplatePipeline: boolean) { this.extractTemplateOptions = { enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat, i18nNormalizeLineEndingsInICUs: this.i18nNormalizeLineEndingsInICUs, @@ -225,7 +225,7 @@ export class ComponentDecoratorHandler implements const directiveResult = extractDirectiveMetadata( node, decorator, this.reflector, this.evaluator, this.refEmitter, this.referencesRegistry, this.isCore, this.annotateForClosureCompiler, this.compilationMode, - this.elementSchemaRegistry.getDefaultComponentElementName()); + this.elementSchemaRegistry.getDefaultComponentElementName(), this.useTemplatePipeline); if (directiveResult === undefined) { // `extractDirectiveMetadata` returns undefined when the @Directive has `jit: true`. In this // case, compilation of the decorator is skipped. Returning an empty object signifies @@ -514,6 +514,7 @@ export class ComponentDecoratorHandler implements i18nUseExternalIds: this.i18nUseExternalIds, relativeContextFilePath, rawImports: rawImports !== null ? new WrappedNodeExpr(rawImports) : undefined, + useTemplatePipeline: this.useTemplatePipeline, }, typeCheckMeta: extractDirectiveTypeCheckMeta(node, inputs, this.reflector), classMetadata: this.includeClassMetadata ? diff --git a/packages/compiler-cli/src/ngtsc/annotations/directive/src/handler.ts b/packages/compiler-cli/src/ngtsc/annotations/directive/src/handler.ts index 694209d1401169..017be711633b09 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/directive/src/handler.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/directive/src/handler.ts @@ -64,6 +64,7 @@ export class DirectiveDecoratorHandler implements private perf: PerfRecorder, private includeClassMetadata: boolean, private readonly compilationMode: CompilationMode, + private readonly useTemplatePipeline: boolean, ) {} readonly precedence = HandlerPrecedence.PRIMARY; @@ -103,7 +104,8 @@ export class DirectiveDecoratorHandler implements const directiveResult = extractDirectiveMetadata( node, decorator, this.reflector, this.evaluator, this.refEmitter, this.referencesRegistry, - this.isCore, this.annotateForClosureCompiler, this.compilationMode); + this.isCore, this.annotateForClosureCompiler, this.compilationMode, + /* defaultSelector */ null, this.useTemplatePipeline); if (directiveResult === undefined) { return {}; } diff --git a/packages/compiler-cli/src/ngtsc/annotations/directive/src/shared.ts b/packages/compiler-cli/src/ngtsc/annotations/directive/src/shared.ts index 0040c42bb4f778..ff612648cca784 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/directive/src/shared.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/directive/src/shared.ts @@ -36,7 +36,7 @@ export function extractDirectiveMetadata( clazz: ClassDeclaration, decorator: Readonly, reflector: ReflectionHost, evaluator: PartialEvaluator, refEmitter: ReferenceEmitter, referencesRegistry: ReferencesRegistry, isCore: boolean, annotateForClosureCompiler: boolean, - compilationMode: CompilationMode, defaultSelector: string|null = null): { + compilationMode: CompilationMode, defaultSelector: string|null, useTemplatePipeline: boolean): { decorator: Map, metadata: R3DirectiveMetadata, inputs: ClassPropertyMapping, @@ -204,7 +204,10 @@ export function extractDirectiveMetadata( const metadata: R3DirectiveMetadata = { name: clazz.name.text, deps: ctorDeps, - host, + host: { + ...host, + useTemplatePipeline, + }, lifecycle: { usesOnChanges, }, diff --git a/packages/compiler-cli/src/ngtsc/core/api/src/public_options.ts b/packages/compiler-cli/src/ngtsc/core/api/src/public_options.ts index 7eb9b780afe3a1..42128a308c34f7 100644 --- a/packages/compiler-cli/src/ngtsc/core/api/src/public_options.ts +++ b/packages/compiler-cli/src/ngtsc/core/api/src/public_options.ts @@ -431,4 +431,9 @@ export interface MiscOptions { * another library without option set will not issue error if rendered in orphan way. */ forbidOrphanComponents?: boolean; + + /** + * Whether to use TemplateDefinitionBuilder as the code generator, or Template Pipeline.hand + */ + useTemplatePipeline?: boolean; } diff --git a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts index e4fd849109a52a..e48918b9bf9bab 100644 --- a/packages/compiler-cli/src/ngtsc/core/src/compiler.ts +++ b/packages/compiler-cli/src/ngtsc/core/src/compiler.ts @@ -37,6 +37,8 @@ import {getSourceFileOrNull, isDtsPath, toUnredirectedSourceFile} from '../../ut import {Xi18nContext} from '../../xi18n'; import {DiagnosticCategoryLabel, NgCompilerAdapter, NgCompilerOptions} from '../api'; +const SHOULD_USE_TEMPLATE_PIPELINE = false; + /** * State information about a compilation which is only generated once some data is requested from * the `NgCompiler` (for example, by calling `getDiagnostics`). @@ -1131,7 +1133,8 @@ export class NgCompiler { this.incrementalCompilation.depGraph, injectableRegistry, semanticDepGraphUpdater, this.closureCompilerEnabled, this.delegatingPerfRecorder, hostDirectivesResolver, supportTestBed, compilationMode, deferredSymbolsTracker, - !!this.options.forbidOrphanComponents, this.enableBlockSyntax), + !!this.options.forbidOrphanComponents, this.enableBlockSyntax, + this.options.useTemplatePipeline ?? SHOULD_USE_TEMPLATE_PIPELINE), // TODO(alxhub): understand why the cast here is necessary (something to do with `null` // not being assignable to `unknown` when wrapped in `Readonly`). @@ -1142,6 +1145,7 @@ export class NgCompiler { this.closureCompilerEnabled, this.delegatingPerfRecorder, supportTestBed, compilationMode, + this.options.useTemplatePipeline ?? SHOULD_USE_TEMPLATE_PIPELINE, ) as Readonly>, // clang-format on // Pipe handler must be before injectable handler in list so pipe factories are printed diff --git a/packages/compiler/src/jit_compiler_facade.ts b/packages/compiler/src/jit_compiler_facade.ts index c1cf8bbc57637c..c8634cc718abb1 100644 --- a/packages/compiler/src/jit_compiler_facade.ts +++ b/packages/compiler/src/jit_compiler_facade.ts @@ -206,6 +206,7 @@ export class CompilerFacadeImpl implements CompilerFacade { null, relativeContextFilePath: '', i18nUseExternalIds: true, + useTemplatePipeline: false, }; const jitExpressionSourceMap = `ng:///${facade.name}.js`; return this.compileComponentFromMeta(angularCoreEnv, jitExpressionSourceMap, meta); @@ -356,7 +357,10 @@ function convertDirectiveFacadeToMetadata(facade: R3DirectiveMetadataFacade): R3 typeSourceSpan: facade.typeSourceSpan, type: wrapReference(facade.type), deps: null, - host: extractHostBindings(facade.propMetadata, facade.typeSourceSpan, facade.host), + host: { + ...extractHostBindings(facade.propMetadata, facade.typeSourceSpan, facade.host), + useTemplatePipeline: false, + }, inputs: {...inputsFromMetadata, ...inputsFromType}, outputs: {...outputsFromMetadata, ...outputsFromType}, queries: facade.queries.map(convertToR3QueryMetadata), @@ -403,6 +407,7 @@ function convertHostDeclarationToMetadata(host: R3DeclareDirectiveFacade['host'] classAttr: host.classAttribute, styleAttr: host.styleAttribute, }, + useTemplatePipeline: false, }; } @@ -489,6 +494,7 @@ function convertDeclareComponentFacadeToMetadata( declarationListEmitMode: DeclarationListEmitMode.ClosureResolved, relativeContextFilePath: '', i18nUseExternalIds: true, + useTemplatePipeline: false, }; } diff --git a/packages/compiler/src/render3/view/api.ts b/packages/compiler/src/render3/view/api.ts index cdf7c9dfd12289..a5cf236c54427f 100644 --- a/packages/compiler/src/render3/view/api.ts +++ b/packages/compiler/src/render3/view/api.ts @@ -347,6 +347,8 @@ export interface R3ComponentMetadata * not be set. If component has empty array imports then this field is not set. */ rawImports?: o.Expression; + + useTemplatePipeline: boolean; } /** @@ -519,6 +521,8 @@ export interface R3HostMetadata { properties: {[key: string]: string}; specialAttributes: {styleAttr?: string; classAttr?: string;}; + + useTemplatePipeline: boolean; } /** diff --git a/packages/compiler/src/render3/view/compiler.ts b/packages/compiler/src/render3/view/compiler.ts index afca3cf59138fc..ea5ef73b23bdfa 100644 --- a/packages/compiler/src/render3/view/compiler.ts +++ b/packages/compiler/src/render3/view/compiler.ts @@ -216,7 +216,7 @@ export function compileComponentFromMetadata( const templateName = templateTypeName ? `${templateTypeName}_Template` : null; // Template compilation is currently conditional as we're in the process of rewriting it. - if (!USE_TEMPLATE_PIPELINE) { + if (!USE_TEMPLATE_PIPELINE && !meta.useTemplatePipeline) { // This is the main path currently used in compilation, which compiles the template with the // legacy `TemplateDefinitionBuilder`. @@ -594,7 +594,7 @@ function createHostBindingsFunction( const eventBindings = bindingParser.createDirectiveHostEventAsts(hostBindingsMetadata.listeners, typeSourceSpan); - if (USE_TEMPLATE_PIPELINE) { + if (USE_TEMPLATE_PIPELINE || hostBindingsMetadata.useTemplatePipeline) { // The parser for host bindings treats class and style attributes specially -- they are // extracted into these separate fields. This is not the case for templates, so the compiler can // actually already handle these special attributes internally. Therefore, we just drop them