mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
build(compiler): add a flag to conditionally build with the template pipeline (#48580)
This commit adds a Bazel flag which controls a constant in the compiler code. When this flag (`--//packages/compiler:use_template_pipeline`) is specified, the prototype template pipeline code is enabled. This is not used in any production workflows and only works in the local Angular repository. It will be used to develop the template pipeline against the existing compliance tests. PR Close #48580
This commit is contained in:
parent
cb7fa593c7
commit
4e41d127db
6 changed files with 133 additions and 42 deletions
|
|
@ -1,3 +1,4 @@
|
|||
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
|
||||
load("//tools:defaults.bzl", "ng_package", "ts_library", "tsec_test")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
|
@ -10,6 +11,9 @@ ts_library(
|
|||
"src/**/*.ts",
|
||||
],
|
||||
),
|
||||
deps = [
|
||||
"//packages/compiler/src/template/pipeline/switch",
|
||||
],
|
||||
)
|
||||
|
||||
tsec_test(
|
||||
|
|
@ -49,3 +53,19 @@ filegroup(
|
|||
"src/**/*.ts",
|
||||
]),
|
||||
)
|
||||
|
||||
# Pass this flag on the commandline during a build to enable the prototype
|
||||
# template pipeline:
|
||||
#
|
||||
# yarn bazel build //some:target --//packages/compiler:use_template_pipeline
|
||||
bool_flag(
|
||||
name = "use_template_pipeline",
|
||||
build_setting_default = False,
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "template_pipeline_enabled",
|
||||
flag_values = {
|
||||
":use_template_pipeline": "true",
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ import {ParseError, ParseSourceSpan, sanitizeIdentifier} from '../../parse_util'
|
|||
import {isIframeSecuritySensitiveAttr} from '../../schema/dom_security_schema';
|
||||
import {CssSelector} from '../../selector';
|
||||
import {ShadowCss} from '../../shadow_css';
|
||||
import {emitTemplateFn, transformTemplate} from '../../template/pipeline/src/emit';
|
||||
import {ingest} from '../../template/pipeline/src/ingest';
|
||||
import {USE_TEMPLATE_PIPELINE} from '../../template/pipeline/switch';
|
||||
import {BindingParser} from '../../template_parser/binding_parser';
|
||||
import {error} from '../../util';
|
||||
import {BoundEvent} from '../r3_ast';
|
||||
|
|
@ -173,42 +176,67 @@ export function compileComponentFromMetadata(
|
|||
|
||||
const changeDetection = meta.changeDetection;
|
||||
|
||||
const template = meta.template;
|
||||
const templateBuilder = new TemplateDefinitionBuilder(
|
||||
constantPool, BindingScope.createRootScope(), 0, templateTypeName, null, null, templateName,
|
||||
R3.namespaceHTML, meta.relativeContextFilePath, meta.i18nUseExternalIds);
|
||||
// Template compilation is currently conditional as we're in the process of rewriting it.
|
||||
if (!USE_TEMPLATE_PIPELINE) {
|
||||
// This is the main path currently used in compilation, which compiles the template with the
|
||||
// legacy `TemplateDefinitionBuilder`.
|
||||
|
||||
const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []);
|
||||
const template = meta.template;
|
||||
const templateBuilder = new TemplateDefinitionBuilder(
|
||||
constantPool, BindingScope.createRootScope(), 0, templateTypeName, null, null, templateName,
|
||||
R3.namespaceHTML, meta.relativeContextFilePath, meta.i18nUseExternalIds);
|
||||
|
||||
// We need to provide this so that dynamically generated components know what
|
||||
// projected content blocks to pass through to the component when it is instantiated.
|
||||
const ngContentSelectors = templateBuilder.getNgContentSelectors();
|
||||
if (ngContentSelectors) {
|
||||
definitionMap.set('ngContentSelectors', ngContentSelectors);
|
||||
}
|
||||
const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []);
|
||||
|
||||
// e.g. `decls: 2`
|
||||
definitionMap.set('decls', o.literal(templateBuilder.getConstCount()));
|
||||
|
||||
// e.g. `vars: 2`
|
||||
definitionMap.set('vars', o.literal(templateBuilder.getVarCount()));
|
||||
|
||||
// Generate `consts` section of ComponentDef:
|
||||
// - either as an array:
|
||||
// `consts: [['one', 'two'], ['three', 'four']]`
|
||||
// - or as a factory function in case additional statements are present (to support i18n):
|
||||
// `consts: function() { var i18n_0; if (ngI18nClosureMode) {...} else {...} return [i18n_0]; }`
|
||||
const {constExpressions, prepareStatements} = templateBuilder.getConsts();
|
||||
if (constExpressions.length > 0) {
|
||||
let constsExpr: o.LiteralArrayExpr|o.FunctionExpr = o.literalArr(constExpressions);
|
||||
// Prepare statements are present - turn `consts` into a function.
|
||||
if (prepareStatements.length > 0) {
|
||||
constsExpr = o.fn([], [...prepareStatements, new o.ReturnStatement(constsExpr)]);
|
||||
// We need to provide this so that dynamically generated components know what
|
||||
// projected content blocks to pass through to the component when it is
|
||||
// instantiated.
|
||||
const ngContentSelectors = templateBuilder.getNgContentSelectors();
|
||||
if (ngContentSelectors) {
|
||||
definitionMap.set('ngContentSelectors', ngContentSelectors);
|
||||
}
|
||||
definitionMap.set('consts', constsExpr);
|
||||
}
|
||||
|
||||
definitionMap.set('template', templateFunctionExpression);
|
||||
// e.g. `decls: 2`
|
||||
// definitionMap.set('decls', o.literal(tpl.root.decls!));
|
||||
definitionMap.set('decls', o.literal(templateBuilder.getConstCount()));
|
||||
|
||||
// e.g. `vars: 2`
|
||||
// definitionMap.set('vars', o.literal(tpl.root.vars!));
|
||||
definitionMap.set('vars', o.literal(templateBuilder.getVarCount()));
|
||||
|
||||
// Generate `consts` section of ComponentDef:
|
||||
// - either as an array:
|
||||
// `consts: [['one', 'two'], ['three', 'four']]`
|
||||
// - or as a factory function in case additional statements are present (to support i18n):
|
||||
// `consts: function() { var i18n_0; if (ngI18nClosureMode) {...} else {...} return [i18n_0];
|
||||
// }`
|
||||
const {constExpressions, prepareStatements} = templateBuilder.getConsts();
|
||||
if (constExpressions.length > 0) {
|
||||
let constsExpr: o.LiteralArrayExpr|o.FunctionExpr = o.literalArr(constExpressions);
|
||||
// Prepare statements are present - turn `consts` into a function.
|
||||
if (prepareStatements.length > 0) {
|
||||
constsExpr = o.fn([], [...prepareStatements, new o.ReturnStatement(constsExpr)]);
|
||||
}
|
||||
definitionMap.set('consts', constsExpr);
|
||||
}
|
||||
|
||||
definitionMap.set('template', templateFunctionExpression);
|
||||
} else {
|
||||
// This path compiles the template using the prototype template pipeline. First the template is
|
||||
// ingested into IR:
|
||||
const tpl = ingest(meta.name, meta.template.nodes);
|
||||
|
||||
// Then the IR is transformed to prepare it for cod egeneration.
|
||||
transformTemplate(tpl);
|
||||
|
||||
// Finally we emit the template function:
|
||||
const templateFn = emitTemplateFn(tpl, constantPool);
|
||||
|
||||
definitionMap.set('template', templateFn);
|
||||
definitionMap.set('decls', o.literal(tpl.root.decls as number));
|
||||
definitionMap.set('vars', o.literal(tpl.root.vars as number));
|
||||
definitionMap.set('consts', o.literalArr(tpl.consts));
|
||||
}
|
||||
|
||||
if (meta.declarations.length > 0) {
|
||||
definitionMap.set(
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ export abstract class ExpressionBase extends o.Expression {
|
|||
* Logical expression representing a lexical read of a variable name.
|
||||
*/
|
||||
export class LexicalReadExpr extends ExpressionBase {
|
||||
readonly kind = ExpressionKind.LexicalRead;
|
||||
override readonly kind = ExpressionKind.LexicalRead;
|
||||
|
||||
constructor(readonly name: string) {
|
||||
super();
|
||||
|
|
@ -79,7 +79,7 @@ export class LexicalReadExpr extends ExpressionBase {
|
|||
* Runtime operation to retrieve the value of a local reference.
|
||||
*/
|
||||
export class ReferenceExpr extends ExpressionBase implements UsesSlotIndexExprTrait {
|
||||
readonly kind = ExpressionKind.Reference;
|
||||
override readonly kind = ExpressionKind.Reference;
|
||||
|
||||
readonly[UsesSlotIndex] = true;
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ export class ReferenceExpr extends ExpressionBase implements UsesSlotIndexExprTr
|
|||
* A reference to the current view context (usually the `ctx` variable in a template function).
|
||||
*/
|
||||
export class ContextExpr extends ExpressionBase {
|
||||
readonly kind = ExpressionKind.Context;
|
||||
override readonly kind = ExpressionKind.Context;
|
||||
|
||||
constructor(readonly view: XrefId) {
|
||||
super();
|
||||
|
|
@ -129,7 +129,7 @@ export class ContextExpr extends ExpressionBase {
|
|||
* Runtime operation to navigate to the next view context in the view hierarchy.
|
||||
*/
|
||||
export class NextContextExpr extends ExpressionBase {
|
||||
readonly kind = ExpressionKind.NextContext;
|
||||
override readonly kind = ExpressionKind.NextContext;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
|
@ -155,7 +155,7 @@ export class NextContextExpr extends ExpressionBase {
|
|||
* operation.
|
||||
*/
|
||||
export class GetCurrentViewExpr extends ExpressionBase {
|
||||
readonly kind = ExpressionKind.GetCurrentView;
|
||||
override readonly kind = ExpressionKind.GetCurrentView;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
|
@ -178,7 +178,7 @@ export class GetCurrentViewExpr extends ExpressionBase {
|
|||
* Runtime operation to restore a snapshotted view.
|
||||
*/
|
||||
export class RestoreViewExpr extends ExpressionBase {
|
||||
readonly kind = ExpressionKind.RestoreView;
|
||||
override readonly kind = ExpressionKind.RestoreView;
|
||||
|
||||
constructor(public view: XrefId|o.Expression) {
|
||||
super();
|
||||
|
|
@ -217,7 +217,7 @@ export class RestoreViewExpr extends ExpressionBase {
|
|||
* Runtime operation to reset the current view context after `RestoreView`.
|
||||
*/
|
||||
export class ResetViewExpr extends ExpressionBase {
|
||||
readonly kind = ExpressionKind.ResetView;
|
||||
override readonly kind = ExpressionKind.ResetView;
|
||||
|
||||
constructor(public expr: o.Expression) {
|
||||
super();
|
||||
|
|
@ -244,19 +244,19 @@ export class ResetViewExpr extends ExpressionBase {
|
|||
* Read of a variable declared as an `ir.VariableOp` and referenced through its `ir.XrefId`.
|
||||
*/
|
||||
export class ReadVariableExpr extends ExpressionBase {
|
||||
readonly kind = ExpressionKind.ReadVariable;
|
||||
override readonly kind = ExpressionKind.ReadVariable;
|
||||
name: string|null = null;
|
||||
constructor(readonly xref: XrefId) {
|
||||
super();
|
||||
}
|
||||
|
||||
visitExpression(): void {}
|
||||
override visitExpression(): void {}
|
||||
|
||||
isEquivalent(other: o.Expression): boolean {
|
||||
override isEquivalent(other: o.Expression): boolean {
|
||||
return other instanceof ReadVariableExpr && other.xref === this.xref;
|
||||
}
|
||||
|
||||
isConstant(): boolean {
|
||||
override isConstant(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
21
packages/compiler/src/template/pipeline/switch/BUILD.bazel
Normal file
21
packages/compiler/src/template/pipeline/switch/BUILD.bazel
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
load("//tools:defaults.bzl", "ts_library")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
# Generate contents for `index.ts` depending on the value of the template
|
||||
# pipeline build flag.
|
||||
genrule(
|
||||
name = "gen_index_ts",
|
||||
outs = ["index.ts"],
|
||||
cmd = "echo 'export const USE_TEMPLATE_PIPELINE = " + select({
|
||||
"//packages/compiler:template_pipeline_enabled": "true",
|
||||
"//conditions:default": "false",
|
||||
}) + ";' > $@",
|
||||
)
|
||||
|
||||
ts_library(
|
||||
name = "switch",
|
||||
srcs = [
|
||||
":gen_index_ts",
|
||||
],
|
||||
)
|
||||
7
packages/compiler/src/template/pipeline/switch/README.md
Normal file
7
packages/compiler/src/template/pipeline/switch/README.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
The small `ts_library` defined in this directory defines a constant which determines whether the template pipeline should be used. This constant depends on the defined Bazel config flag `//packages/compiler:use_template_pipeline`. In other words:
|
||||
|
||||
```
|
||||
yarn bazel build //some:target --//packages/compiler:use_template_pipeline
|
||||
```
|
||||
|
||||
will enable the prototype template pipeline for this build.
|
||||
15
packages/compiler/src/template/pipeline/switch/index.ts
Normal file
15
packages/compiler/src/template/pipeline/switch/index.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google LLC All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
// During 3P builds, this file is replaced with a `genrule()` that conditionally sets the
|
||||
// `USE_TEMPLATE_PIPELINE` constant instead. In 1P builds, this file is read directly.
|
||||
|
||||
/**
|
||||
* Whether the prototype template pipeline should be enabled.
|
||||
*/
|
||||
export const USE_TEMPLATE_PIPELINE: boolean = false;
|
||||
Loading…
Reference in a new issue