Skip to content

Commit

Permalink
feat(compiler): Add a TSConfig option useTemplatePipeline (#54057)
Browse files Browse the repository at this point in the history
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.

PR Close #54057
  • Loading branch information
dylhunn authored and thePunderWoman committed Jan 24, 2024
1 parent 0f5f45c commit 47e6e84
Show file tree
Hide file tree
Showing 14 changed files with 55 additions and 39 deletions.
1 change: 1 addition & 0 deletions goldens/public-api/compiler-cli/compiler_options.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export interface MiscOptions {
compileNonExportedClasses?: boolean;
disableTypeScriptVersionCheck?: boolean;
forbidOrphanComponents?: boolean;
useTemplatePipeline?: boolean;
}

// @public
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<TExpression>(
directiveExpr: AstObject<R3DeclareDirectiveDependencyMetadata, TExpression>,
Expand Down Expand Up @@ -196,6 +196,7 @@ export class PartialComponentLinkerVersion1<TStatement, TExpression> implements
relativeContextFilePath: this.sourceUrl,
i18nUseExternalIds: false,
declarations,
useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_LINKER,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -145,6 +145,7 @@ function toHostMetadata<TExpression>(metaObj: AstObject<R3DeclareDirectiveMetada
listeners: {},
properties: {},
specialAttributes: {},
useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_LINKER,
};
}

Expand All @@ -169,6 +170,7 @@ function toHostMetadata<TExpression>(metaObj: AstObject<R3DeclareDirectiveMetada
host.getObject('properties').toLiteral(value => value.getString()) :
{},
specialAttributes,
useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_LINKER,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<TExpression>(wrapped: o.WrappedNodeExpr<TExpression>): R3Reference {
return {value: wrapped, type: wrapped};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 ?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,41 +70,24 @@ function setup(
const resourceLoader = new StubResourceLoader();

const handler = new ComponentDecoratorHandler(
reflectionHost,
evaluator,
metaRegistry,
metaReader,
scopeRegistry,
dtsResolver,
scopeRegistry,
typeCheckScopeRegistry,
resourceRegistry,
reflectionHost, evaluator, metaRegistry, metaReader, scopeRegistry, dtsResolver,
scopeRegistry, typeCheckScopeRegistry, resourceRegistry,
/* isCore */ false,
/* strictCtorDeps */ false,
resourceLoader,
/* strictCtorDeps */ false, resourceLoader,
/* rootDirs */['/'],
/* defaultPreserveWhitespaces */ false,
/* i18nUseExternalIds */ true,
/* enableI18nLegacyMessageIdFormat */ false,
!!usePoisonedData,
/* i18nNormalizeLineEndingsInICUs */ false,
moduleResolver,
cycleAnalyzer,
CycleHandlingStrategy.UseRemoteScoping,
refEmitter,
referencesRegistry,
/* depTracker */ null,
injectableRegistry,
/* enableI18nLegacyMessageIdFormat */ false, !!usePoisonedData,
/* i18nNormalizeLineEndingsInICUs */ false, moduleResolver, cycleAnalyzer,
CycleHandlingStrategy.UseRemoteScoping, refEmitter, referencesRegistry,
/* depTracker */ null, injectableRegistry,
/* semanticDepGraphUpdater */ null,
/* annotateForClosureCompiler */ false,
NOOP_PERF_RECORDER,
hostDirectivesResolver,
true,
/* annotateForClosureCompiler */ false, NOOP_PERF_RECORDER, hostDirectivesResolver, true,
compilationMode,
new DeferredSymbolTracker(checker, /* onlyExplicitDeferDependencyImports */ false),
/* forbidOrphanRenderering */ false,
/* enableBlockSyntax */ true,
);
/* useTemplatePipeline */ true);
return {reflectionHost, handler, resourceLoader, metaRegistry};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function extractDirectiveMetadata(
clazz: ClassDeclaration, decorator: Readonly<Decorator>, 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<string, ts.Expression>,
metadata: R3DirectiveMetadata,
inputs: ClassPropertyMapping<InputMapping>,
Expand Down Expand Up @@ -220,7 +220,10 @@ export function extractDirectiveMetadata(
const metadata: R3DirectiveMetadata = {
name: clazz.name.text,
deps: ctorDeps,
host,
host: {
...host,
useTemplatePipeline,
},
lifecycle: {
usesOnChanges,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ runInEachFileSystem(() => {
/*strictCtorDeps*/ false,
/*semanticDepGraphUpdater*/ null,
/*annotateForClosureCompiler*/ false, NOOP_PERF_RECORDER, /*includeClassMetadata*/ true,
/*compilationMode */ CompilationMode.FULL);
/*compilationMode */ CompilationMode.FULL, /* useTemplatePipeline */ true);

const DirNode = getDeclaration(program, _('/entry.ts'), dirName, isNamedClassDeclaration);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
useTemplatePipeline?: boolean;
}
6 changes: 5 additions & 1 deletion packages/compiler-cli/src/ngtsc/core/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`).
Expand Down Expand Up @@ -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`).
Expand All @@ -1142,6 +1145,7 @@ export class NgCompiler {
this.closureCompilerEnabled,
this.delegatingPerfRecorder,
supportTestBed, compilationMode,
this.options.useTemplatePipeline ?? SHOULD_USE_TEMPLATE_PIPELINE,
) as Readonly<DecoratorHandler<unknown, unknown, SemanticSymbol | null,unknown>>,
// clang-format on
// Pipe handler must be before injectable handler in list so pipe factories are printed
Expand Down
10 changes: 9 additions & 1 deletion packages/compiler/src/jit_compiler_facade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import {ResourceLoader} from './resource_loader';
import {DomElementSchemaRegistry} from './schema/dom_element_schema_registry';
import {SelectorMatcher} from './selector';

export const SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT = false;

export class CompilerFacadeImpl implements CompilerFacade {
FactoryTarget = FactoryTarget;
ResourceLoader = ResourceLoader;
Expand Down Expand Up @@ -206,6 +208,7 @@ export class CompilerFacadeImpl implements CompilerFacade {
null,
relativeContextFilePath: '',
i18nUseExternalIds: true,
useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
};
const jitExpressionSourceMap = `ng:///${facade.name}.js`;
return this.compileComponentFromMeta(angularCoreEnv, jitExpressionSourceMap, meta);
Expand Down Expand Up @@ -355,7 +358,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: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
},
inputs: {...inputsFromMetadata, ...inputsFromType},
outputs: {...outputsFromMetadata, ...outputsFromType},
queries: facade.queries.map(convertToR3QueryMetadata),
Expand Down Expand Up @@ -402,6 +408,7 @@ function convertHostDeclarationToMetadata(host: R3DeclareDirectiveFacade['host']
classAttr: host.classAttribute,
styleAttr: host.styleAttribute,
},
useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
};
}

Expand Down Expand Up @@ -488,6 +495,7 @@ function convertDeclareComponentFacadeToMetadata(
declarationListEmitMode: DeclarationListEmitMode.ClosureResolved,
relativeContextFilePath: '',
i18nUseExternalIds: true,
useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
};
}

Expand Down
4 changes: 4 additions & 0 deletions packages/compiler/src/render3/view/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,8 @@ export interface R3ComponentMetadata<DeclarationT extends R3TemplateDependency>
* not be set. If component has empty array imports then this field is not set.
*/
rawImports?: o.Expression;

useTemplatePipeline: boolean;
}

/**
Expand Down Expand Up @@ -519,6 +521,8 @@ export interface R3HostMetadata {
properties: {[key: string]: string};

specialAttributes: {styleAttr?: string; classAttr?: string;};

useTemplatePipeline: boolean;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/compiler/src/render3/view/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ export function compileComponentFromMetadata(
}

// 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`.

Expand Down Expand Up @@ -506,7 +506,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
Expand Down

0 comments on commit 47e6e84

Please sign in to comment.