@angular-eslint/bundled-angular-compiler 17.2.2-alpha.5 → 17.2.2-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2751 -776
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @license Angular v17.
|
|
4
|
+
* @license Angular v17.3.0
|
|
5
5
|
* (c) 2010-2022 Google LLC. https://angular.io/
|
|
6
6
|
* License: MIT
|
|
7
7
|
*/
|
|
@@ -2238,6 +2238,13 @@ class ConstantPool {
|
|
|
2238
2238
|
this.literals = new Map();
|
|
2239
2239
|
this.literalFactories = new Map();
|
|
2240
2240
|
this.sharedConstants = new Map();
|
|
2241
|
+
/**
|
|
2242
|
+
* Constant pool also tracks claimed names from {@link uniqueName}.
|
|
2243
|
+
* This is useful to avoid collisions if variables are intended to be
|
|
2244
|
+
* named a certain way- but may conflict. We wouldn't want to always suffix
|
|
2245
|
+
* them with unique numbers.
|
|
2246
|
+
*/
|
|
2247
|
+
this._claimedNames = new Map();
|
|
2241
2248
|
this.nextNameIndex = 0;
|
|
2242
2249
|
}
|
|
2243
2250
|
getConstLiteral(literal, forceShared) {
|
|
@@ -2360,14 +2367,17 @@ class ConstantPool {
|
|
|
2360
2367
|
return { literalFactory, literalFactoryArguments };
|
|
2361
2368
|
}
|
|
2362
2369
|
/**
|
|
2363
|
-
* Produce a unique name.
|
|
2370
|
+
* Produce a unique name in the context of this pool.
|
|
2364
2371
|
*
|
|
2365
2372
|
* The name might be unique among different prefixes if any of the prefixes end in
|
|
2366
2373
|
* a digit so the prefix should be a constant string (not based on user input) and
|
|
2367
2374
|
* must not end in a digit.
|
|
2368
2375
|
*/
|
|
2369
|
-
uniqueName(
|
|
2370
|
-
|
|
2376
|
+
uniqueName(name, alwaysIncludeSuffix = true) {
|
|
2377
|
+
const count = this._claimedNames.get(name) ?? 0;
|
|
2378
|
+
const result = count === 0 && !alwaysIncludeSuffix ? `${name}` : `${name}${count}`;
|
|
2379
|
+
this._claimedNames.set(name, count + 1);
|
|
2380
|
+
return result;
|
|
2371
2381
|
}
|
|
2372
2382
|
freshName() {
|
|
2373
2383
|
return this.uniqueName(CONSTANT_PREFIX);
|
|
@@ -2630,6 +2640,14 @@ class Identifiers {
|
|
|
2630
2640
|
static { this.viewQuery = { name: 'ɵɵviewQuery', moduleName: CORE }; }
|
|
2631
2641
|
static { this.loadQuery = { name: 'ɵɵloadQuery', moduleName: CORE }; }
|
|
2632
2642
|
static { this.contentQuery = { name: 'ɵɵcontentQuery', moduleName: CORE }; }
|
|
2643
|
+
// Signal queries
|
|
2644
|
+
static { this.viewQuerySignal = { name: 'ɵɵviewQuerySignal', moduleName: CORE }; }
|
|
2645
|
+
static { this.contentQuerySignal = { name: 'ɵɵcontentQuerySignal', moduleName: CORE }; }
|
|
2646
|
+
static { this.queryAdvance = { name: 'ɵɵqueryAdvance', moduleName: CORE }; }
|
|
2647
|
+
// Two-way bindings
|
|
2648
|
+
static { this.twoWayProperty = { name: 'ɵɵtwoWayProperty', moduleName: CORE }; }
|
|
2649
|
+
static { this.twoWayBindingSet = { name: 'ɵɵtwoWayBindingSet', moduleName: CORE }; }
|
|
2650
|
+
static { this.twoWayListener = { name: 'ɵɵtwoWayListener', moduleName: CORE }; }
|
|
2633
2651
|
static { this.NgOnChangesFeature = { name: 'ɵɵNgOnChangesFeature', moduleName: CORE }; }
|
|
2634
2652
|
static { this.InheritDefinitionFeature = { name: 'ɵɵInheritDefinitionFeature', moduleName: CORE }; }
|
|
2635
2653
|
static { this.CopyDefinitionFeature = { name: 'ɵɵCopyDefinitionFeature', moduleName: CORE }; }
|
|
@@ -2659,6 +2677,7 @@ class Identifiers {
|
|
|
2659
2677
|
// type-checking
|
|
2660
2678
|
static { this.InputSignalBrandWriteType = { name: 'ɵINPUT_SIGNAL_BRAND_WRITE_TYPE', moduleName: CORE }; }
|
|
2661
2679
|
static { this.UnwrapDirectiveSignalInputs = { name: 'ɵUnwrapDirectiveSignalInputs', moduleName: CORE }; }
|
|
2680
|
+
static { this.unwrapWritableSignal = { name: 'ɵunwrapWritableSignal', moduleName: CORE }; }
|
|
2662
2681
|
}
|
|
2663
2682
|
|
|
2664
2683
|
const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
|
|
@@ -4948,6 +4967,8 @@ const CHAINABLE_INSTRUCTIONS = new Set([
|
|
|
4948
4967
|
Identifiers.textInterpolate8,
|
|
4949
4968
|
Identifiers.textInterpolateV,
|
|
4950
4969
|
Identifiers.templateCreate,
|
|
4970
|
+
Identifiers.twoWayProperty,
|
|
4971
|
+
Identifiers.twoWayListener,
|
|
4951
4972
|
]);
|
|
4952
4973
|
/** Generates a call to a single instruction. */
|
|
4953
4974
|
function invokeInstruction(span, reference, params) {
|
|
@@ -4958,11 +4979,11 @@ function invokeInstruction(span, reference, params) {
|
|
|
4958
4979
|
*
|
|
4959
4980
|
* A variable declaration is added to the statements the first time the allocator is invoked.
|
|
4960
4981
|
*/
|
|
4961
|
-
function temporaryAllocator(
|
|
4982
|
+
function temporaryAllocator(pushStatement, name) {
|
|
4962
4983
|
let temp = null;
|
|
4963
4984
|
return () => {
|
|
4964
4985
|
if (!temp) {
|
|
4965
|
-
|
|
4986
|
+
pushStatement(new DeclareVarStmt(TEMPORARY_NAME, undefined, DYNAMIC_TYPE));
|
|
4966
4987
|
temp = variable(name);
|
|
4967
4988
|
}
|
|
4968
4989
|
return temp;
|
|
@@ -5060,29 +5081,6 @@ function trimTrailingNulls(parameters) {
|
|
|
5060
5081
|
}
|
|
5061
5082
|
return parameters;
|
|
5062
5083
|
}
|
|
5063
|
-
function getQueryPredicate(query, constantPool) {
|
|
5064
|
-
if (Array.isArray(query.predicate)) {
|
|
5065
|
-
let predicate = [];
|
|
5066
|
-
query.predicate.forEach((selector) => {
|
|
5067
|
-
// Each item in predicates array may contain strings with comma-separated refs
|
|
5068
|
-
// (for ex. 'ref, ref1, ..., refN'), thus we extract individual refs and store them
|
|
5069
|
-
// as separate array entities
|
|
5070
|
-
const selectors = selector.split(',').map(token => literal(token.trim()));
|
|
5071
|
-
predicate.push(...selectors);
|
|
5072
|
-
});
|
|
5073
|
-
return constantPool.getConstLiteral(literalArr(predicate), true);
|
|
5074
|
-
}
|
|
5075
|
-
else {
|
|
5076
|
-
// The original predicate may have been wrapped in a `forwardRef()` call.
|
|
5077
|
-
switch (query.predicate.forwardRef) {
|
|
5078
|
-
case 0 /* ForwardRefHandling.None */:
|
|
5079
|
-
case 2 /* ForwardRefHandling.Unwrapped */:
|
|
5080
|
-
return query.predicate.expression;
|
|
5081
|
-
case 1 /* ForwardRefHandling.Wrapped */:
|
|
5082
|
-
return importExpr(Identifiers.resolveForwardRef).callFn([query.predicate.expression]);
|
|
5083
|
-
}
|
|
5084
|
-
}
|
|
5085
|
-
}
|
|
5086
5084
|
/**
|
|
5087
5085
|
* A representation for an object literal used during codegen of definition objects. The generic
|
|
5088
5086
|
* type `T` allows to reference a documented type of the generated structure, such that the
|
|
@@ -5148,7 +5146,7 @@ function getAttrsForDirectiveMatching(elOrTpl) {
|
|
|
5148
5146
|
}
|
|
5149
5147
|
});
|
|
5150
5148
|
elOrTpl.inputs.forEach(i => {
|
|
5151
|
-
if (i.type === 0 /* BindingType.Property */) {
|
|
5149
|
+
if (i.type === 0 /* BindingType.Property */ || i.type === 5 /* BindingType.TwoWay */) {
|
|
5152
5150
|
attributesMap[i.name] = '';
|
|
5153
5151
|
}
|
|
5154
5152
|
});
|
|
@@ -6897,10 +6895,9 @@ exports.ParsedPropertyType = void 0;
|
|
|
6897
6895
|
ParsedPropertyType[ParsedPropertyType["DEFAULT"] = 0] = "DEFAULT";
|
|
6898
6896
|
ParsedPropertyType[ParsedPropertyType["LITERAL_ATTR"] = 1] = "LITERAL_ATTR";
|
|
6899
6897
|
ParsedPropertyType[ParsedPropertyType["ANIMATION"] = 2] = "ANIMATION";
|
|
6898
|
+
ParsedPropertyType[ParsedPropertyType["TWO_WAY"] = 3] = "TWO_WAY";
|
|
6900
6899
|
})(exports.ParsedPropertyType || (exports.ParsedPropertyType = {}));
|
|
6901
6900
|
class ParsedEvent {
|
|
6902
|
-
// Regular events have a target
|
|
6903
|
-
// Animation events have a phase
|
|
6904
6901
|
constructor(name, targetOrPhase, type, handler, sourceSpan, handlerSpan, keySpan) {
|
|
6905
6902
|
this.name = name;
|
|
6906
6903
|
this.targetOrPhase = targetOrPhase;
|
|
@@ -6944,32 +6941,10 @@ class EventHandlerVars {
|
|
|
6944
6941
|
* used in an action binding (e.g. an event handler).
|
|
6945
6942
|
*/
|
|
6946
6943
|
function convertActionBinding(localResolver, implicitReceiver, action, bindingId, baseSourceSpan, implicitReceiverAccesses, globals) {
|
|
6947
|
-
|
|
6948
|
-
localResolver = new DefaultLocalResolver(globals);
|
|
6949
|
-
}
|
|
6950
|
-
const actionWithoutBuiltins = convertPropertyBindingBuiltins({
|
|
6951
|
-
createLiteralArrayConverter: (argCount) => {
|
|
6952
|
-
// Note: no caching for literal arrays in actions.
|
|
6953
|
-
return (args) => literalArr(args);
|
|
6954
|
-
},
|
|
6955
|
-
createLiteralMapConverter: (keys) => {
|
|
6956
|
-
// Note: no caching for literal maps in actions.
|
|
6957
|
-
return (values) => {
|
|
6958
|
-
const entries = keys.map((k, i) => ({
|
|
6959
|
-
key: k.key,
|
|
6960
|
-
value: values[i],
|
|
6961
|
-
quoted: k.quoted,
|
|
6962
|
-
}));
|
|
6963
|
-
return literalMap(entries);
|
|
6964
|
-
};
|
|
6965
|
-
},
|
|
6966
|
-
createPipeConverter: (name) => {
|
|
6967
|
-
throw new Error(`Illegal State: Actions are not allowed to contain pipes. Pipe: ${name}`);
|
|
6968
|
-
}
|
|
6969
|
-
}, action);
|
|
6944
|
+
localResolver ??= new DefaultLocalResolver(globals);
|
|
6970
6945
|
const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, /* supportsInterpolation */ false, baseSourceSpan, implicitReceiverAccesses);
|
|
6971
6946
|
const actionStmts = [];
|
|
6972
|
-
flattenStatements(
|
|
6947
|
+
flattenStatements(convertActionBuiltins(action).visit(visitor, _Mode.Statement), actionStmts);
|
|
6973
6948
|
prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
|
|
6974
6949
|
if (visitor.usesImplicitReceiver) {
|
|
6975
6950
|
localResolver.notifyImplicitReceiverUse();
|
|
@@ -6984,6 +6959,77 @@ function convertActionBinding(localResolver, implicitReceiver, action, bindingId
|
|
|
6984
6959
|
}
|
|
6985
6960
|
return actionStmts;
|
|
6986
6961
|
}
|
|
6962
|
+
function convertAssignmentActionBinding(localResolver, implicitReceiver, action, bindingId, baseSourceSpan, implicitReceiverAccesses, globals) {
|
|
6963
|
+
localResolver ??= new DefaultLocalResolver(globals);
|
|
6964
|
+
const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, /* supportsInterpolation */ false, baseSourceSpan, implicitReceiverAccesses);
|
|
6965
|
+
let convertedAction = convertActionBuiltins(action).visit(visitor, _Mode.Statement);
|
|
6966
|
+
// This should already have been asserted in the parser, but we verify it here just in case.
|
|
6967
|
+
if (!(convertedAction instanceof ExpressionStatement)) {
|
|
6968
|
+
throw new Error(`Illegal state: unsupported expression in two-way action binding.`);
|
|
6969
|
+
}
|
|
6970
|
+
// Converts `[(ngModel)]="name"` to `twoWayBindingSet(ctx.name, $event) || (ctx.name = $event)`.
|
|
6971
|
+
convertedAction = wrapAssignmentAction(convertedAction.expr).toStmt();
|
|
6972
|
+
const actionStmts = [];
|
|
6973
|
+
flattenStatements(convertedAction, actionStmts);
|
|
6974
|
+
prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
|
|
6975
|
+
// Assignment events always return `$event`.
|
|
6976
|
+
actionStmts.push(new ReturnStatement(EventHandlerVars.event));
|
|
6977
|
+
implicitReceiverAccesses?.add(EventHandlerVars.event.name);
|
|
6978
|
+
if (visitor.usesImplicitReceiver) {
|
|
6979
|
+
localResolver.notifyImplicitReceiverUse();
|
|
6980
|
+
}
|
|
6981
|
+
return actionStmts;
|
|
6982
|
+
}
|
|
6983
|
+
function wrapAssignmentReadExpression(ast) {
|
|
6984
|
+
return new ExternalExpr(Identifiers.twoWayBindingSet)
|
|
6985
|
+
.callFn([ast, EventHandlerVars.event])
|
|
6986
|
+
.or(ast.set(EventHandlerVars.event));
|
|
6987
|
+
}
|
|
6988
|
+
function isReadExpression$1(value) {
|
|
6989
|
+
return value instanceof ReadPropExpr || value instanceof ReadKeyExpr;
|
|
6990
|
+
}
|
|
6991
|
+
function wrapAssignmentAction(ast) {
|
|
6992
|
+
// The only officially supported expressions inside of a two-way binding are read expressions.
|
|
6993
|
+
if (isReadExpression$1(ast)) {
|
|
6994
|
+
return wrapAssignmentReadExpression(ast);
|
|
6995
|
+
}
|
|
6996
|
+
// However, historically the expression parser was handling two-way events by appending `=$event`
|
|
6997
|
+
// to the raw string before attempting to parse it. This has led to bugs over the years (see
|
|
6998
|
+
// #37809) and to unintentionally supporting unassignable events in the two-way binding. The
|
|
6999
|
+
// logic below aims to emulate the old behavior while still supporting the new output format
|
|
7000
|
+
// which uses `twoWayBindingSet`. Note that the generated code doesn't necessarily make sense
|
|
7001
|
+
// based on what the user wrote, for example the event binding for `[(value)]="a ? b : c"`
|
|
7002
|
+
// would produce `ctx.a ? ctx.b : ctx.c = $event`. We aim to reproduce what the parser used
|
|
7003
|
+
// to generate before #54154.
|
|
7004
|
+
if (ast instanceof BinaryOperatorExpr && isReadExpression$1(ast.rhs)) {
|
|
7005
|
+
// `a && b` -> `ctx.a && twoWayBindingSet(ctx.b, $event) || (ctx.b = $event)`
|
|
7006
|
+
return new BinaryOperatorExpr(ast.operator, ast.lhs, wrapAssignmentReadExpression(ast.rhs));
|
|
7007
|
+
}
|
|
7008
|
+
// Note: this also supports nullish coalescing expressions which
|
|
7009
|
+
// would've been downleveled to ternary expressions by this point.
|
|
7010
|
+
if (ast instanceof ConditionalExpr && isReadExpression$1(ast.falseCase)) {
|
|
7011
|
+
// `a ? b : c` -> `ctx.a ? ctx.b : twoWayBindingSet(ctx.c, $event) || (ctx.c = $event)`
|
|
7012
|
+
return new ConditionalExpr(ast.condition, ast.trueCase, wrapAssignmentReadExpression(ast.falseCase));
|
|
7013
|
+
}
|
|
7014
|
+
// `!!a` -> `twoWayBindingSet(ctx.a, $event) || (ctx.a = $event)`
|
|
7015
|
+
// Note: previously we'd actually produce `!!(ctx.a = $event)`, but the wrapping
|
|
7016
|
+
// node doesn't affect the result so we don't need to carry it over.
|
|
7017
|
+
if (ast instanceof NotExpr) {
|
|
7018
|
+
let expr = ast.condition;
|
|
7019
|
+
while (true) {
|
|
7020
|
+
if (expr instanceof NotExpr) {
|
|
7021
|
+
expr = expr.condition;
|
|
7022
|
+
}
|
|
7023
|
+
else {
|
|
7024
|
+
if (isReadExpression$1(expr)) {
|
|
7025
|
+
return wrapAssignmentReadExpression(expr);
|
|
7026
|
+
}
|
|
7027
|
+
break;
|
|
7028
|
+
}
|
|
7029
|
+
}
|
|
7030
|
+
}
|
|
7031
|
+
throw new Error(`Illegal state: unsupported expression in two-way action binding.`);
|
|
7032
|
+
}
|
|
6987
7033
|
function convertPropertyBindingBuiltins(converterFactory, ast) {
|
|
6988
7034
|
return convertBuiltins(converterFactory, ast);
|
|
6989
7035
|
}
|
|
@@ -7067,6 +7113,29 @@ function convertBuiltins(converterFactory, ast) {
|
|
|
7067
7113
|
const visitor = new _BuiltinAstConverter(converterFactory);
|
|
7068
7114
|
return ast.visit(visitor);
|
|
7069
7115
|
}
|
|
7116
|
+
function convertActionBuiltins(action) {
|
|
7117
|
+
const converterFactory = {
|
|
7118
|
+
createLiteralArrayConverter: () => {
|
|
7119
|
+
// Note: no caching for literal arrays in actions.
|
|
7120
|
+
return (args) => literalArr(args);
|
|
7121
|
+
},
|
|
7122
|
+
createLiteralMapConverter: (keys) => {
|
|
7123
|
+
// Note: no caching for literal maps in actions.
|
|
7124
|
+
return (values) => {
|
|
7125
|
+
const entries = keys.map((k, i) => ({
|
|
7126
|
+
key: k.key,
|
|
7127
|
+
value: values[i],
|
|
7128
|
+
quoted: k.quoted,
|
|
7129
|
+
}));
|
|
7130
|
+
return literalMap(entries);
|
|
7131
|
+
};
|
|
7132
|
+
},
|
|
7133
|
+
createPipeConverter: (name) => {
|
|
7134
|
+
throw new Error(`Illegal State: Actions are not allowed to contain pipes. Pipe: ${name}`);
|
|
7135
|
+
}
|
|
7136
|
+
};
|
|
7137
|
+
return convertPropertyBindingBuiltins(converterFactory, action);
|
|
7138
|
+
}
|
|
7070
7139
|
function temporaryName(bindingId, temporaryNumber) {
|
|
7071
7140
|
return `tmp_${bindingId}_${temporaryNumber}`;
|
|
7072
7141
|
}
|
|
@@ -7809,6 +7878,10 @@ const animationKeywords = new Set([
|
|
|
7809
7878
|
// `steps()` function
|
|
7810
7879
|
'end', 'jump-both', 'jump-end', 'jump-none', 'jump-start', 'start'
|
|
7811
7880
|
]);
|
|
7881
|
+
/**
|
|
7882
|
+
* The following array contains all of the CSS at-rule identifiers which are scoped.
|
|
7883
|
+
*/
|
|
7884
|
+
const scopedAtRuleIdentifiers = ['@media', '@supports', '@document', '@layer', '@container', '@scope', '@starting-style'];
|
|
7812
7885
|
/**
|
|
7813
7886
|
* The following class has its origin from a port of shadowCSS from webcomponents.js to TypeScript.
|
|
7814
7887
|
* It has since diverge in many ways to tailor Angular's needs.
|
|
@@ -8296,9 +8369,7 @@ class ShadowCss {
|
|
|
8296
8369
|
if (rule.selector[0] !== '@') {
|
|
8297
8370
|
selector = this._scopeSelector(rule.selector, scopeSelector, hostSelector);
|
|
8298
8371
|
}
|
|
8299
|
-
else if (
|
|
8300
|
-
rule.selector.startsWith('@document') || rule.selector.startsWith('@layer') ||
|
|
8301
|
-
rule.selector.startsWith('@container') || rule.selector.startsWith('@scope')) {
|
|
8372
|
+
else if (scopedAtRuleIdentifiers.some(atRule => rule.selector.startsWith(atRule))) {
|
|
8302
8373
|
content = this._scopeSelectors(rule.content, scopeSelector, hostSelector);
|
|
8303
8374
|
}
|
|
8304
8375
|
else if (rule.selector.startsWith('@font-face') || rule.selector.startsWith('@page')) {
|
|
@@ -8948,46 +9019,54 @@ var OpKind;
|
|
|
8948
9019
|
* An update up for a repeater.
|
|
8949
9020
|
*/
|
|
8950
9021
|
OpKind[OpKind["Repeater"] = 35] = "Repeater";
|
|
9022
|
+
/**
|
|
9023
|
+
* An operation to bind an expression to the property side of a two-way binding.
|
|
9024
|
+
*/
|
|
9025
|
+
OpKind[OpKind["TwoWayProperty"] = 36] = "TwoWayProperty";
|
|
9026
|
+
/**
|
|
9027
|
+
* An operation declaring the event side of a two-way binding.
|
|
9028
|
+
*/
|
|
9029
|
+
OpKind[OpKind["TwoWayListener"] = 37] = "TwoWayListener";
|
|
8951
9030
|
/**
|
|
8952
9031
|
* The start of an i18n block.
|
|
8953
9032
|
*/
|
|
8954
|
-
OpKind[OpKind["I18nStart"] =
|
|
9033
|
+
OpKind[OpKind["I18nStart"] = 38] = "I18nStart";
|
|
8955
9034
|
/**
|
|
8956
9035
|
* A self-closing i18n on a single element.
|
|
8957
9036
|
*/
|
|
8958
|
-
OpKind[OpKind["I18n"] =
|
|
9037
|
+
OpKind[OpKind["I18n"] = 39] = "I18n";
|
|
8959
9038
|
/**
|
|
8960
9039
|
* The end of an i18n block.
|
|
8961
9040
|
*/
|
|
8962
|
-
OpKind[OpKind["I18nEnd"] =
|
|
9041
|
+
OpKind[OpKind["I18nEnd"] = 40] = "I18nEnd";
|
|
8963
9042
|
/**
|
|
8964
9043
|
* An expression in an i18n message.
|
|
8965
9044
|
*/
|
|
8966
|
-
OpKind[OpKind["I18nExpression"] =
|
|
9045
|
+
OpKind[OpKind["I18nExpression"] = 41] = "I18nExpression";
|
|
8967
9046
|
/**
|
|
8968
9047
|
* An instruction that applies a set of i18n expressions.
|
|
8969
9048
|
*/
|
|
8970
|
-
OpKind[OpKind["I18nApply"] =
|
|
9049
|
+
OpKind[OpKind["I18nApply"] = 42] = "I18nApply";
|
|
8971
9050
|
/**
|
|
8972
9051
|
* An instruction to create an ICU expression.
|
|
8973
9052
|
*/
|
|
8974
|
-
OpKind[OpKind["IcuStart"] =
|
|
9053
|
+
OpKind[OpKind["IcuStart"] = 43] = "IcuStart";
|
|
8975
9054
|
/**
|
|
8976
9055
|
* An instruction to update an ICU expression.
|
|
8977
9056
|
*/
|
|
8978
|
-
OpKind[OpKind["IcuEnd"] =
|
|
9057
|
+
OpKind[OpKind["IcuEnd"] = 44] = "IcuEnd";
|
|
8979
9058
|
/**
|
|
8980
9059
|
* An instruction representing a placeholder in an ICU expression.
|
|
8981
9060
|
*/
|
|
8982
|
-
OpKind[OpKind["IcuPlaceholder"] =
|
|
9061
|
+
OpKind[OpKind["IcuPlaceholder"] = 45] = "IcuPlaceholder";
|
|
8983
9062
|
/**
|
|
8984
9063
|
* An i18n context containing information needed to generate an i18n message.
|
|
8985
9064
|
*/
|
|
8986
|
-
OpKind[OpKind["I18nContext"] =
|
|
9065
|
+
OpKind[OpKind["I18nContext"] = 46] = "I18nContext";
|
|
8987
9066
|
/**
|
|
8988
9067
|
* A creation op that corresponds to i18n attributes on an element.
|
|
8989
9068
|
*/
|
|
8990
|
-
OpKind[OpKind["I18nAttributes"] =
|
|
9069
|
+
OpKind[OpKind["I18nAttributes"] = 47] = "I18nAttributes";
|
|
8991
9070
|
})(OpKind || (OpKind = {}));
|
|
8992
9071
|
/**
|
|
8993
9072
|
* Distinguishes different kinds of IR expressions.
|
|
@@ -9074,31 +9153,22 @@ var ExpressionKind;
|
|
|
9074
9153
|
* A reference to a temporary variable.
|
|
9075
9154
|
*/
|
|
9076
9155
|
ExpressionKind[ExpressionKind["ReadTemporaryExpr"] = 19] = "ReadTemporaryExpr";
|
|
9077
|
-
/**
|
|
9078
|
-
* An expression representing a sanitizer function.
|
|
9079
|
-
*/
|
|
9080
|
-
ExpressionKind[ExpressionKind["SanitizerExpr"] = 20] = "SanitizerExpr";
|
|
9081
|
-
/**
|
|
9082
|
-
* An expression representing a function to create trusted values.
|
|
9083
|
-
*/
|
|
9084
|
-
ExpressionKind[ExpressionKind["TrustedValueFnExpr"] = 21] = "TrustedValueFnExpr";
|
|
9085
9156
|
/**
|
|
9086
9157
|
* An expression that will cause a literal slot index to be emitted.
|
|
9087
9158
|
*/
|
|
9088
|
-
ExpressionKind[ExpressionKind["SlotLiteralExpr"] =
|
|
9159
|
+
ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 20] = "SlotLiteralExpr";
|
|
9089
9160
|
/**
|
|
9090
9161
|
* A test expression for a conditional op.
|
|
9091
9162
|
*/
|
|
9092
|
-
ExpressionKind[ExpressionKind["ConditionalCase"] =
|
|
9163
|
+
ExpressionKind[ExpressionKind["ConditionalCase"] = 21] = "ConditionalCase";
|
|
9093
9164
|
/**
|
|
9094
|
-
*
|
|
9095
|
-
* properties ($even, $first, etc.).
|
|
9165
|
+
* An expression that will be automatically extracted to the component const array.
|
|
9096
9166
|
*/
|
|
9097
|
-
ExpressionKind[ExpressionKind["
|
|
9167
|
+
ExpressionKind[ExpressionKind["ConstCollected"] = 22] = "ConstCollected";
|
|
9098
9168
|
/**
|
|
9099
|
-
*
|
|
9169
|
+
* Operation that sets the value of a two-way binding.
|
|
9100
9170
|
*/
|
|
9101
|
-
ExpressionKind[ExpressionKind["
|
|
9171
|
+
ExpressionKind[ExpressionKind["TwoWayBindingSet"] = 23] = "TwoWayBindingSet";
|
|
9102
9172
|
})(ExpressionKind || (ExpressionKind = {}));
|
|
9103
9173
|
var VariableFlags;
|
|
9104
9174
|
(function (VariableFlags) {
|
|
@@ -9142,15 +9212,6 @@ var CompatibilityMode;
|
|
|
9142
9212
|
CompatibilityMode[CompatibilityMode["Normal"] = 0] = "Normal";
|
|
9143
9213
|
CompatibilityMode[CompatibilityMode["TemplateDefinitionBuilder"] = 1] = "TemplateDefinitionBuilder";
|
|
9144
9214
|
})(CompatibilityMode || (CompatibilityMode = {}));
|
|
9145
|
-
/**
|
|
9146
|
-
* Enumeration of the different kinds of `@defer` secondary blocks.
|
|
9147
|
-
*/
|
|
9148
|
-
var DeferSecondaryKind;
|
|
9149
|
-
(function (DeferSecondaryKind) {
|
|
9150
|
-
DeferSecondaryKind[DeferSecondaryKind["Loading"] = 0] = "Loading";
|
|
9151
|
-
DeferSecondaryKind[DeferSecondaryKind["Placeholder"] = 1] = "Placeholder";
|
|
9152
|
-
DeferSecondaryKind[DeferSecondaryKind["Error"] = 2] = "Error";
|
|
9153
|
-
})(DeferSecondaryKind || (DeferSecondaryKind = {}));
|
|
9154
9215
|
/**
|
|
9155
9216
|
* Enumeration of the types of attributes which can be applied to an element.
|
|
9156
9217
|
*/
|
|
@@ -9184,6 +9245,10 @@ var BindingKind;
|
|
|
9184
9245
|
* Animation property bindings.
|
|
9185
9246
|
*/
|
|
9186
9247
|
BindingKind[BindingKind["Animation"] = 6] = "Animation";
|
|
9248
|
+
/**
|
|
9249
|
+
* Property side of a two-way binding.
|
|
9250
|
+
*/
|
|
9251
|
+
BindingKind[BindingKind["TwoWayProperty"] = 7] = "TwoWayProperty";
|
|
9187
9252
|
})(BindingKind || (BindingKind = {}));
|
|
9188
9253
|
/**
|
|
9189
9254
|
* Enumeration of possible times i18n params can be resolved.
|
|
@@ -9196,7 +9261,7 @@ var I18nParamResolutionTime;
|
|
|
9196
9261
|
*/
|
|
9197
9262
|
I18nParamResolutionTime[I18nParamResolutionTime["Creation"] = 0] = "Creation";
|
|
9198
9263
|
/**
|
|
9199
|
-
* Param is resolved during post-processing. This should be used for params
|
|
9264
|
+
* Param is resolved during post-processing. This should be used for params whose value comes from
|
|
9200
9265
|
* an ICU.
|
|
9201
9266
|
*/
|
|
9202
9267
|
I18nParamResolutionTime[I18nParamResolutionTime["Postproccessing"] = 1] = "Postproccessing";
|
|
@@ -9223,7 +9288,7 @@ var I18nParamValueFlags;
|
|
|
9223
9288
|
(function (I18nParamValueFlags) {
|
|
9224
9289
|
I18nParamValueFlags[I18nParamValueFlags["None"] = 0] = "None";
|
|
9225
9290
|
/**
|
|
9226
|
-
* This value
|
|
9291
|
+
* This value represents an element tag.
|
|
9227
9292
|
*/
|
|
9228
9293
|
I18nParamValueFlags[I18nParamValueFlags["ElementTag"] = 1] = "ElementTag";
|
|
9229
9294
|
/**
|
|
@@ -9374,6 +9439,21 @@ const NEW_OP = {
|
|
|
9374
9439
|
prev: null,
|
|
9375
9440
|
next: null,
|
|
9376
9441
|
};
|
|
9442
|
+
|
|
9443
|
+
/**
|
|
9444
|
+
* Create an `InterpolationTextOp`.
|
|
9445
|
+
*/
|
|
9446
|
+
function createInterpolateTextOp(xref, interpolation, sourceSpan) {
|
|
9447
|
+
return {
|
|
9448
|
+
kind: OpKind.InterpolateText,
|
|
9449
|
+
target: xref,
|
|
9450
|
+
interpolation,
|
|
9451
|
+
sourceSpan,
|
|
9452
|
+
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9453
|
+
...TRAIT_CONSUMES_VARS,
|
|
9454
|
+
...NEW_OP,
|
|
9455
|
+
};
|
|
9456
|
+
}
|
|
9377
9457
|
class Interpolation {
|
|
9378
9458
|
constructor(strings, expressions, i18nPlaceholders) {
|
|
9379
9459
|
this.strings = strings;
|
|
@@ -9384,6 +9464,27 @@ class Interpolation {
|
|
|
9384
9464
|
}
|
|
9385
9465
|
}
|
|
9386
9466
|
}
|
|
9467
|
+
/**
|
|
9468
|
+
* Create a `BindingOp`, not yet transformed into a particular type of binding.
|
|
9469
|
+
*/
|
|
9470
|
+
function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
|
|
9471
|
+
return {
|
|
9472
|
+
kind: OpKind.Binding,
|
|
9473
|
+
bindingKind: kind,
|
|
9474
|
+
target,
|
|
9475
|
+
name,
|
|
9476
|
+
expression,
|
|
9477
|
+
unit,
|
|
9478
|
+
securityContext,
|
|
9479
|
+
isTextAttribute,
|
|
9480
|
+
isStructuralTemplateAttribute,
|
|
9481
|
+
templateKind,
|
|
9482
|
+
i18nContext: null,
|
|
9483
|
+
i18nMessage,
|
|
9484
|
+
sourceSpan,
|
|
9485
|
+
...NEW_OP,
|
|
9486
|
+
};
|
|
9487
|
+
}
|
|
9387
9488
|
/**
|
|
9388
9489
|
* Create a `PropertyOp`.
|
|
9389
9490
|
*/
|
|
@@ -9406,6 +9507,27 @@ function createPropertyOp(target, name, expression, isAnimationTrigger, security
|
|
|
9406
9507
|
...NEW_OP,
|
|
9407
9508
|
};
|
|
9408
9509
|
}
|
|
9510
|
+
/**
|
|
9511
|
+
* Create a `TwoWayPropertyOp`.
|
|
9512
|
+
*/
|
|
9513
|
+
function createTwoWayPropertyOp(target, name, expression, securityContext, isStructuralTemplateAttribute, templateKind, i18nContext, i18nMessage, sourceSpan) {
|
|
9514
|
+
return {
|
|
9515
|
+
kind: OpKind.TwoWayProperty,
|
|
9516
|
+
target,
|
|
9517
|
+
name,
|
|
9518
|
+
expression,
|
|
9519
|
+
securityContext,
|
|
9520
|
+
sanitizer: null,
|
|
9521
|
+
isStructuralTemplateAttribute,
|
|
9522
|
+
templateKind,
|
|
9523
|
+
i18nContext,
|
|
9524
|
+
i18nMessage,
|
|
9525
|
+
sourceSpan,
|
|
9526
|
+
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9527
|
+
...TRAIT_CONSUMES_VARS,
|
|
9528
|
+
...NEW_OP,
|
|
9529
|
+
};
|
|
9530
|
+
}
|
|
9409
9531
|
/** Create a `StylePropOp`. */
|
|
9410
9532
|
function createStylePropOp(xref, name, expression, unit, sourceSpan) {
|
|
9411
9533
|
return {
|
|
@@ -9495,6 +9617,47 @@ function createAdvanceOp(delta, sourceSpan) {
|
|
|
9495
9617
|
...NEW_OP,
|
|
9496
9618
|
};
|
|
9497
9619
|
}
|
|
9620
|
+
/**
|
|
9621
|
+
* Create a conditional op, which will display an embedded view according to a condtion.
|
|
9622
|
+
*/
|
|
9623
|
+
function createConditionalOp(target, targetSlot, test, conditions, sourceSpan) {
|
|
9624
|
+
return {
|
|
9625
|
+
kind: OpKind.Conditional,
|
|
9626
|
+
target,
|
|
9627
|
+
targetSlot,
|
|
9628
|
+
test,
|
|
9629
|
+
conditions,
|
|
9630
|
+
processed: null,
|
|
9631
|
+
sourceSpan,
|
|
9632
|
+
contextValue: null,
|
|
9633
|
+
...NEW_OP,
|
|
9634
|
+
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9635
|
+
...TRAIT_CONSUMES_VARS,
|
|
9636
|
+
};
|
|
9637
|
+
}
|
|
9638
|
+
function createRepeaterOp(repeaterCreate, targetSlot, collection, sourceSpan) {
|
|
9639
|
+
return {
|
|
9640
|
+
kind: OpKind.Repeater,
|
|
9641
|
+
target: repeaterCreate,
|
|
9642
|
+
targetSlot,
|
|
9643
|
+
collection,
|
|
9644
|
+
sourceSpan,
|
|
9645
|
+
...NEW_OP,
|
|
9646
|
+
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9647
|
+
};
|
|
9648
|
+
}
|
|
9649
|
+
function createDeferWhenOp(target, expr, prefetch, sourceSpan) {
|
|
9650
|
+
return {
|
|
9651
|
+
kind: OpKind.DeferWhen,
|
|
9652
|
+
target,
|
|
9653
|
+
expr,
|
|
9654
|
+
prefetch,
|
|
9655
|
+
sourceSpan,
|
|
9656
|
+
...NEW_OP,
|
|
9657
|
+
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9658
|
+
...TRAIT_CONSUMES_VARS,
|
|
9659
|
+
};
|
|
9660
|
+
}
|
|
9498
9661
|
/**
|
|
9499
9662
|
* Create an i18n expression op.
|
|
9500
9663
|
*/
|
|
@@ -9742,6 +9905,31 @@ class ResetViewExpr extends ExpressionBase {
|
|
|
9742
9905
|
return new ResetViewExpr(this.expr.clone());
|
|
9743
9906
|
}
|
|
9744
9907
|
}
|
|
9908
|
+
class TwoWayBindingSetExpr extends ExpressionBase {
|
|
9909
|
+
constructor(target, value) {
|
|
9910
|
+
super();
|
|
9911
|
+
this.target = target;
|
|
9912
|
+
this.value = value;
|
|
9913
|
+
this.kind = ExpressionKind.TwoWayBindingSet;
|
|
9914
|
+
}
|
|
9915
|
+
visitExpression(visitor, context) {
|
|
9916
|
+
this.target.visitExpression(visitor, context);
|
|
9917
|
+
this.value.visitExpression(visitor, context);
|
|
9918
|
+
}
|
|
9919
|
+
isEquivalent(other) {
|
|
9920
|
+
return this.target.isEquivalent(other.target) && this.value.isEquivalent(other.value);
|
|
9921
|
+
}
|
|
9922
|
+
isConstant() {
|
|
9923
|
+
return false;
|
|
9924
|
+
}
|
|
9925
|
+
transformInternalExpressions(transform, flags) {
|
|
9926
|
+
this.target = transformExpressionsInExpression(this.target, transform, flags);
|
|
9927
|
+
this.value = transformExpressionsInExpression(this.value, transform, flags);
|
|
9928
|
+
}
|
|
9929
|
+
clone() {
|
|
9930
|
+
return new TwoWayBindingSetExpr(this.target, this.value);
|
|
9931
|
+
}
|
|
9932
|
+
}
|
|
9745
9933
|
/**
|
|
9746
9934
|
* Read of a variable declared as an `ir.VariableOp` and referenced through its `ir.XrefId`.
|
|
9747
9935
|
*/
|
|
@@ -10090,6 +10278,40 @@ class SlotLiteralExpr extends ExpressionBase {
|
|
|
10090
10278
|
}
|
|
10091
10279
|
transformInternalExpressions() { }
|
|
10092
10280
|
}
|
|
10281
|
+
class ConditionalCaseExpr extends ExpressionBase {
|
|
10282
|
+
/**
|
|
10283
|
+
* Create an expression for one branch of a conditional.
|
|
10284
|
+
* @param expr The expression to be tested for this case. Might be null, as in an `else` case.
|
|
10285
|
+
* @param target The Xref of the view to be displayed if this condition is true.
|
|
10286
|
+
*/
|
|
10287
|
+
constructor(expr, target, targetSlot, alias = null) {
|
|
10288
|
+
super();
|
|
10289
|
+
this.expr = expr;
|
|
10290
|
+
this.target = target;
|
|
10291
|
+
this.targetSlot = targetSlot;
|
|
10292
|
+
this.alias = alias;
|
|
10293
|
+
this.kind = ExpressionKind.ConditionalCase;
|
|
10294
|
+
}
|
|
10295
|
+
visitExpression(visitor, context) {
|
|
10296
|
+
if (this.expr !== null) {
|
|
10297
|
+
this.expr.visitExpression(visitor, context);
|
|
10298
|
+
}
|
|
10299
|
+
}
|
|
10300
|
+
isEquivalent(e) {
|
|
10301
|
+
return e instanceof ConditionalCaseExpr && e.expr === this.expr;
|
|
10302
|
+
}
|
|
10303
|
+
isConstant() {
|
|
10304
|
+
return true;
|
|
10305
|
+
}
|
|
10306
|
+
clone() {
|
|
10307
|
+
return new ConditionalCaseExpr(this.expr, this.target, this.targetSlot);
|
|
10308
|
+
}
|
|
10309
|
+
transformInternalExpressions(transform, flags) {
|
|
10310
|
+
if (this.expr !== null) {
|
|
10311
|
+
this.expr = transformExpressionsInExpression(this.expr, transform, flags);
|
|
10312
|
+
}
|
|
10313
|
+
}
|
|
10314
|
+
}
|
|
10093
10315
|
class ConstCollectedExpr extends ExpressionBase {
|
|
10094
10316
|
constructor(expr) {
|
|
10095
10317
|
super();
|
|
@@ -10167,6 +10389,11 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10167
10389
|
op.sanitizer =
|
|
10168
10390
|
op.sanitizer && transformExpressionsInExpression(op.sanitizer, transform, flags);
|
|
10169
10391
|
break;
|
|
10392
|
+
case OpKind.TwoWayProperty:
|
|
10393
|
+
op.expression = transformExpressionsInExpression(op.expression, transform, flags);
|
|
10394
|
+
op.sanitizer =
|
|
10395
|
+
op.sanitizer && transformExpressionsInExpression(op.sanitizer, transform, flags);
|
|
10396
|
+
break;
|
|
10170
10397
|
case OpKind.I18nExpression:
|
|
10171
10398
|
op.expression = transformExpressionsInExpression(op.expression, transform, flags);
|
|
10172
10399
|
break;
|
|
@@ -10195,6 +10422,7 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10195
10422
|
}
|
|
10196
10423
|
break;
|
|
10197
10424
|
case OpKind.Listener:
|
|
10425
|
+
case OpKind.TwoWayListener:
|
|
10198
10426
|
for (const innerOp of op.handlerOps) {
|
|
10199
10427
|
transformExpressionsInOp(innerOp, transform, flags | VisitorContextFlag.InChildOperation);
|
|
10200
10428
|
}
|
|
@@ -10672,6 +10900,91 @@ const elementContainerOpKinds = new Set([
|
|
|
10672
10900
|
function isElementOrContainerOp(op) {
|
|
10673
10901
|
return elementContainerOpKinds.has(op.kind);
|
|
10674
10902
|
}
|
|
10903
|
+
/**
|
|
10904
|
+
* Create an `ElementStartOp`.
|
|
10905
|
+
*/
|
|
10906
|
+
function createElementStartOp(tag, xref, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) {
|
|
10907
|
+
return {
|
|
10908
|
+
kind: OpKind.ElementStart,
|
|
10909
|
+
xref,
|
|
10910
|
+
tag,
|
|
10911
|
+
handle: new SlotHandle(),
|
|
10912
|
+
attributes: null,
|
|
10913
|
+
localRefs: [],
|
|
10914
|
+
nonBindable: false,
|
|
10915
|
+
namespace,
|
|
10916
|
+
i18nPlaceholder,
|
|
10917
|
+
startSourceSpan,
|
|
10918
|
+
wholeSourceSpan,
|
|
10919
|
+
...TRAIT_CONSUMES_SLOT,
|
|
10920
|
+
...NEW_OP,
|
|
10921
|
+
};
|
|
10922
|
+
}
|
|
10923
|
+
/**
|
|
10924
|
+
* Create a `TemplateOp`.
|
|
10925
|
+
*/
|
|
10926
|
+
function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) {
|
|
10927
|
+
return {
|
|
10928
|
+
kind: OpKind.Template,
|
|
10929
|
+
xref,
|
|
10930
|
+
templateKind,
|
|
10931
|
+
attributes: null,
|
|
10932
|
+
tag,
|
|
10933
|
+
handle: new SlotHandle(),
|
|
10934
|
+
functionNameSuffix,
|
|
10935
|
+
decls: null,
|
|
10936
|
+
vars: null,
|
|
10937
|
+
localRefs: [],
|
|
10938
|
+
nonBindable: false,
|
|
10939
|
+
namespace,
|
|
10940
|
+
i18nPlaceholder,
|
|
10941
|
+
startSourceSpan,
|
|
10942
|
+
wholeSourceSpan,
|
|
10943
|
+
...TRAIT_CONSUMES_SLOT,
|
|
10944
|
+
...NEW_OP,
|
|
10945
|
+
};
|
|
10946
|
+
}
|
|
10947
|
+
function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, emptyTag, i18nPlaceholder, emptyI18nPlaceholder, startSourceSpan, wholeSourceSpan) {
|
|
10948
|
+
return {
|
|
10949
|
+
kind: OpKind.RepeaterCreate,
|
|
10950
|
+
attributes: null,
|
|
10951
|
+
xref: primaryView,
|
|
10952
|
+
handle: new SlotHandle(),
|
|
10953
|
+
emptyView,
|
|
10954
|
+
track,
|
|
10955
|
+
trackByFn: null,
|
|
10956
|
+
tag,
|
|
10957
|
+
emptyTag,
|
|
10958
|
+
emptyAttributes: null,
|
|
10959
|
+
functionNameSuffix: 'For',
|
|
10960
|
+
namespace: Namespace.HTML,
|
|
10961
|
+
nonBindable: false,
|
|
10962
|
+
localRefs: [],
|
|
10963
|
+
decls: null,
|
|
10964
|
+
vars: null,
|
|
10965
|
+
varNames,
|
|
10966
|
+
usesComponentInstance: false,
|
|
10967
|
+
i18nPlaceholder,
|
|
10968
|
+
emptyI18nPlaceholder,
|
|
10969
|
+
startSourceSpan,
|
|
10970
|
+
wholeSourceSpan,
|
|
10971
|
+
...TRAIT_CONSUMES_SLOT,
|
|
10972
|
+
...NEW_OP,
|
|
10973
|
+
...TRAIT_CONSUMES_VARS,
|
|
10974
|
+
numSlotsUsed: emptyView === null ? 2 : 3,
|
|
10975
|
+
};
|
|
10976
|
+
}
|
|
10977
|
+
/**
|
|
10978
|
+
* Create an `ElementEndOp`.
|
|
10979
|
+
*/
|
|
10980
|
+
function createElementEndOp(xref, sourceSpan) {
|
|
10981
|
+
return {
|
|
10982
|
+
kind: OpKind.ElementEnd,
|
|
10983
|
+
xref,
|
|
10984
|
+
sourceSpan,
|
|
10985
|
+
...NEW_OP,
|
|
10986
|
+
};
|
|
10987
|
+
}
|
|
10675
10988
|
function createDisableBindingsOp(xref) {
|
|
10676
10989
|
return {
|
|
10677
10990
|
kind: OpKind.DisableBindings,
|
|
@@ -10686,6 +10999,62 @@ function createEnableBindingsOp(xref) {
|
|
|
10686
10999
|
...NEW_OP,
|
|
10687
11000
|
};
|
|
10688
11001
|
}
|
|
11002
|
+
/**
|
|
11003
|
+
* Create a `TextOp`.
|
|
11004
|
+
*/
|
|
11005
|
+
function createTextOp(xref, initialValue, icuPlaceholder, sourceSpan) {
|
|
11006
|
+
return {
|
|
11007
|
+
kind: OpKind.Text,
|
|
11008
|
+
xref,
|
|
11009
|
+
handle: new SlotHandle(),
|
|
11010
|
+
initialValue,
|
|
11011
|
+
icuPlaceholder,
|
|
11012
|
+
sourceSpan,
|
|
11013
|
+
...TRAIT_CONSUMES_SLOT,
|
|
11014
|
+
...NEW_OP,
|
|
11015
|
+
};
|
|
11016
|
+
}
|
|
11017
|
+
/**
|
|
11018
|
+
* Create a `ListenerOp`. Host bindings reuse all the listener logic.
|
|
11019
|
+
*/
|
|
11020
|
+
function createListenerOp(target, targetSlot, name, tag, handlerOps, animationPhase, eventTarget, hostListener, sourceSpan) {
|
|
11021
|
+
const handlerList = new OpList();
|
|
11022
|
+
handlerList.push(handlerOps);
|
|
11023
|
+
return {
|
|
11024
|
+
kind: OpKind.Listener,
|
|
11025
|
+
target,
|
|
11026
|
+
targetSlot,
|
|
11027
|
+
tag,
|
|
11028
|
+
hostListener,
|
|
11029
|
+
name,
|
|
11030
|
+
handlerOps: handlerList,
|
|
11031
|
+
handlerFnName: null,
|
|
11032
|
+
consumesDollarEvent: false,
|
|
11033
|
+
isAnimationListener: animationPhase !== null,
|
|
11034
|
+
animationPhase,
|
|
11035
|
+
eventTarget,
|
|
11036
|
+
sourceSpan,
|
|
11037
|
+
...NEW_OP,
|
|
11038
|
+
};
|
|
11039
|
+
}
|
|
11040
|
+
/**
|
|
11041
|
+
* Create a `TwoWayListenerOp`.
|
|
11042
|
+
*/
|
|
11043
|
+
function createTwoWayListenerOp(target, targetSlot, name, tag, handlerOps, sourceSpan) {
|
|
11044
|
+
const handlerList = new OpList();
|
|
11045
|
+
handlerList.push(handlerOps);
|
|
11046
|
+
return {
|
|
11047
|
+
kind: OpKind.TwoWayListener,
|
|
11048
|
+
target,
|
|
11049
|
+
targetSlot,
|
|
11050
|
+
tag,
|
|
11051
|
+
name,
|
|
11052
|
+
handlerOps: handlerList,
|
|
11053
|
+
handlerFnName: null,
|
|
11054
|
+
sourceSpan,
|
|
11055
|
+
...NEW_OP,
|
|
11056
|
+
};
|
|
11057
|
+
}
|
|
10689
11058
|
function createPipeOp(xref, slot, name) {
|
|
10690
11059
|
return {
|
|
10691
11060
|
kind: OpKind.Pipe,
|
|
@@ -10710,6 +11079,21 @@ function createProjectionDefOp(def) {
|
|
|
10710
11079
|
...NEW_OP,
|
|
10711
11080
|
};
|
|
10712
11081
|
}
|
|
11082
|
+
function createProjectionOp(xref, selector, i18nPlaceholder, sourceSpan) {
|
|
11083
|
+
return {
|
|
11084
|
+
kind: OpKind.Projection,
|
|
11085
|
+
xref,
|
|
11086
|
+
handle: new SlotHandle(),
|
|
11087
|
+
selector,
|
|
11088
|
+
i18nPlaceholder,
|
|
11089
|
+
projectionSlotIndex: 0,
|
|
11090
|
+
attributes: null,
|
|
11091
|
+
localRefs: [],
|
|
11092
|
+
sourceSpan,
|
|
11093
|
+
...NEW_OP,
|
|
11094
|
+
...TRAIT_CONSUMES_SLOT,
|
|
11095
|
+
};
|
|
11096
|
+
}
|
|
10713
11097
|
/**
|
|
10714
11098
|
* Create an `ExtractedAttributeOp`.
|
|
10715
11099
|
*/
|
|
@@ -10728,6 +11112,42 @@ function createExtractedAttributeOp(target, bindingKind, namespace, name, expres
|
|
|
10728
11112
|
...NEW_OP,
|
|
10729
11113
|
};
|
|
10730
11114
|
}
|
|
11115
|
+
function createDeferOp(xref, main, mainSlot, metadata, resolverFn, sourceSpan) {
|
|
11116
|
+
return {
|
|
11117
|
+
kind: OpKind.Defer,
|
|
11118
|
+
xref,
|
|
11119
|
+
handle: new SlotHandle(),
|
|
11120
|
+
mainView: main,
|
|
11121
|
+
mainSlot,
|
|
11122
|
+
loadingView: null,
|
|
11123
|
+
loadingSlot: null,
|
|
11124
|
+
loadingConfig: null,
|
|
11125
|
+
loadingMinimumTime: null,
|
|
11126
|
+
loadingAfterTime: null,
|
|
11127
|
+
placeholderView: null,
|
|
11128
|
+
placeholderSlot: null,
|
|
11129
|
+
placeholderConfig: null,
|
|
11130
|
+
placeholderMinimumTime: null,
|
|
11131
|
+
errorView: null,
|
|
11132
|
+
errorSlot: null,
|
|
11133
|
+
metadata,
|
|
11134
|
+
resolverFn,
|
|
11135
|
+
sourceSpan,
|
|
11136
|
+
...NEW_OP,
|
|
11137
|
+
...TRAIT_CONSUMES_SLOT,
|
|
11138
|
+
numSlotsUsed: 2,
|
|
11139
|
+
};
|
|
11140
|
+
}
|
|
11141
|
+
function createDeferOnOp(defer, trigger, prefetch, sourceSpan) {
|
|
11142
|
+
return {
|
|
11143
|
+
kind: OpKind.DeferOn,
|
|
11144
|
+
defer,
|
|
11145
|
+
trigger,
|
|
11146
|
+
prefetch,
|
|
11147
|
+
sourceSpan,
|
|
11148
|
+
...NEW_OP,
|
|
11149
|
+
};
|
|
11150
|
+
}
|
|
10731
11151
|
/**
|
|
10732
11152
|
* Create an `ExtractedMessageOp`.
|
|
10733
11153
|
*/
|
|
@@ -10775,6 +11195,30 @@ function createI18nEndOp(xref, sourceSpan) {
|
|
|
10775
11195
|
...NEW_OP,
|
|
10776
11196
|
};
|
|
10777
11197
|
}
|
|
11198
|
+
/**
|
|
11199
|
+
* Creates an ICU start op.
|
|
11200
|
+
*/
|
|
11201
|
+
function createIcuStartOp(xref, message, messagePlaceholder, sourceSpan) {
|
|
11202
|
+
return {
|
|
11203
|
+
kind: OpKind.IcuStart,
|
|
11204
|
+
xref,
|
|
11205
|
+
message,
|
|
11206
|
+
messagePlaceholder,
|
|
11207
|
+
context: null,
|
|
11208
|
+
sourceSpan,
|
|
11209
|
+
...NEW_OP,
|
|
11210
|
+
};
|
|
11211
|
+
}
|
|
11212
|
+
/**
|
|
11213
|
+
* Creates an ICU end op.
|
|
11214
|
+
*/
|
|
11215
|
+
function createIcuEndOp(xref) {
|
|
11216
|
+
return {
|
|
11217
|
+
kind: OpKind.IcuEnd,
|
|
11218
|
+
xref,
|
|
11219
|
+
...NEW_OP,
|
|
11220
|
+
};
|
|
11221
|
+
}
|
|
10778
11222
|
/**
|
|
10779
11223
|
* Creates an ICU placeholder op.
|
|
10780
11224
|
*/
|
|
@@ -10804,6 +11248,17 @@ function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan)
|
|
|
10804
11248
|
...NEW_OP,
|
|
10805
11249
|
};
|
|
10806
11250
|
}
|
|
11251
|
+
function createI18nAttributesOp(xref, handle, target) {
|
|
11252
|
+
return {
|
|
11253
|
+
kind: OpKind.I18nAttributes,
|
|
11254
|
+
xref,
|
|
11255
|
+
handle,
|
|
11256
|
+
target,
|
|
11257
|
+
i18nAttributesConfig: null,
|
|
11258
|
+
...NEW_OP,
|
|
11259
|
+
...TRAIT_CONSUMES_SLOT,
|
|
11260
|
+
};
|
|
11261
|
+
}
|
|
10807
11262
|
|
|
10808
11263
|
function createHostPropertyOp(name, expression, isAnimationTrigger, i18nContext, securityContext, sourceSpan) {
|
|
10809
11264
|
return {
|
|
@@ -10859,11 +11314,12 @@ class CompilationJob {
|
|
|
10859
11314
|
* embedded views or host bindings.
|
|
10860
11315
|
*/
|
|
10861
11316
|
class ComponentCompilationJob extends CompilationJob {
|
|
10862
|
-
constructor(componentName, pool, compatibility, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta) {
|
|
11317
|
+
constructor(componentName, pool, compatibility, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta, allDeferrableDepsFn) {
|
|
10863
11318
|
super(componentName, pool, compatibility);
|
|
10864
11319
|
this.relativeContextFilePath = relativeContextFilePath;
|
|
10865
11320
|
this.i18nUseExternalIds = i18nUseExternalIds;
|
|
10866
11321
|
this.deferBlocksMeta = deferBlocksMeta;
|
|
11322
|
+
this.allDeferrableDepsFn = allDeferrableDepsFn;
|
|
10867
11323
|
this.kind = CompilationJobKind.Tmpl;
|
|
10868
11324
|
this.fnSuffix = 'Template';
|
|
10869
11325
|
this.views = new Map();
|
|
@@ -10950,7 +11406,7 @@ class CompilationUnit {
|
|
|
10950
11406
|
*ops() {
|
|
10951
11407
|
for (const op of this.create) {
|
|
10952
11408
|
yield op;
|
|
10953
|
-
if (op.kind === OpKind.Listener) {
|
|
11409
|
+
if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
|
|
10954
11410
|
for (const listenerOp of op.handlerOps) {
|
|
10955
11411
|
yield listenerOp;
|
|
10956
11412
|
}
|
|
@@ -11190,12 +11646,17 @@ function extractAttributes(job) {
|
|
|
11190
11646
|
bindingKind = BindingKind.Property;
|
|
11191
11647
|
}
|
|
11192
11648
|
OpList.insertBefore(
|
|
11193
|
-
//
|
|
11649
|
+
// Deliberately null i18nMessage value
|
|
11194
11650
|
createExtractedAttributeOp(op.target, bindingKind, null, op.name, /* expression */ null,
|
|
11195
11651
|
/* i18nContext */ null,
|
|
11196
11652
|
/* i18nMessage */ null, op.securityContext), lookupElement$2(elements, op.target));
|
|
11197
11653
|
}
|
|
11198
11654
|
break;
|
|
11655
|
+
case OpKind.TwoWayProperty:
|
|
11656
|
+
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.TwoWayProperty, null, op.name, /* expression */ null,
|
|
11657
|
+
/* i18nContext */ null,
|
|
11658
|
+
/* i18nMessage */ null, op.securityContext), lookupElement$2(elements, op.target));
|
|
11659
|
+
break;
|
|
11199
11660
|
case OpKind.StyleProp:
|
|
11200
11661
|
case OpKind.ClassProp:
|
|
11201
11662
|
// TODO: Can style or class bindings be i18n attributes?
|
|
@@ -11229,6 +11690,15 @@ function extractAttributes(job) {
|
|
|
11229
11690
|
}
|
|
11230
11691
|
}
|
|
11231
11692
|
break;
|
|
11693
|
+
case OpKind.TwoWayListener:
|
|
11694
|
+
// Two-way listeners aren't supported in host bindings.
|
|
11695
|
+
if (job.kind !== CompilationJobKind.Host) {
|
|
11696
|
+
const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, null, op.name, /* expression */ null,
|
|
11697
|
+
/* i18nContext */ null,
|
|
11698
|
+
/* i18nMessage */ null, SecurityContext.NONE);
|
|
11699
|
+
OpList.insertBefore(extractedAttributeOp, lookupElement$2(elements, op.target));
|
|
11700
|
+
}
|
|
11701
|
+
break;
|
|
11232
11702
|
}
|
|
11233
11703
|
}
|
|
11234
11704
|
}
|
|
@@ -11317,6 +11787,15 @@ function specializeBindings(job) {
|
|
|
11317
11787
|
OpList.replace(op, createPropertyOp(op.target, op.name, op.expression, op.bindingKind === BindingKind.Animation, op.securityContext, op.isStructuralTemplateAttribute, op.templateKind, op.i18nContext, op.i18nMessage, op.sourceSpan));
|
|
11318
11788
|
}
|
|
11319
11789
|
break;
|
|
11790
|
+
case BindingKind.TwoWayProperty:
|
|
11791
|
+
if (!(op.expression instanceof Expression)) {
|
|
11792
|
+
// We shouldn't be able to hit this code path since interpolations in two-way bindings
|
|
11793
|
+
// result in a parser error. We assert here so that downstream we can assume that
|
|
11794
|
+
// the value is always an expression.
|
|
11795
|
+
throw new Error(`Expected value of two-way property binding "${op.name}" to be an expression`);
|
|
11796
|
+
}
|
|
11797
|
+
OpList.replace(op, createTwoWayPropertyOp(op.target, op.name, op.expression, op.securityContext, op.isStructuralTemplateAttribute, op.templateKind, op.i18nContext, op.i18nMessage, op.sourceSpan));
|
|
11798
|
+
break;
|
|
11320
11799
|
case BindingKind.I18n:
|
|
11321
11800
|
case BindingKind.ClassName:
|
|
11322
11801
|
case BindingKind.StyleProperty:
|
|
@@ -11353,6 +11832,8 @@ const CHAINABLE = new Set([
|
|
|
11353
11832
|
Identifiers.syntheticHostListener,
|
|
11354
11833
|
Identifiers.syntheticHostProperty,
|
|
11355
11834
|
Identifiers.templateCreate,
|
|
11835
|
+
Identifiers.twoWayProperty,
|
|
11836
|
+
Identifiers.twoWayListener,
|
|
11356
11837
|
]);
|
|
11357
11838
|
/**
|
|
11358
11839
|
* Post-process a reified view compilation and convert sequential calls to chainable instructions
|
|
@@ -11491,7 +11972,7 @@ function generateConditionalExpressions(job) {
|
|
|
11491
11972
|
}
|
|
11492
11973
|
}
|
|
11493
11974
|
|
|
11494
|
-
new Map([
|
|
11975
|
+
const BINARY_OPERATORS = new Map([
|
|
11495
11976
|
['&&', exports.BinaryOperator.And],
|
|
11496
11977
|
['>', exports.BinaryOperator.Bigger],
|
|
11497
11978
|
['>=', exports.BinaryOperator.BiggerEquals],
|
|
@@ -11511,6 +11992,28 @@ new Map([
|
|
|
11511
11992
|
['||', exports.BinaryOperator.Or],
|
|
11512
11993
|
['+', exports.BinaryOperator.Plus],
|
|
11513
11994
|
]);
|
|
11995
|
+
function namespaceForKey(namespacePrefixKey) {
|
|
11996
|
+
const NAMESPACES = new Map([['svg', Namespace.SVG], ['math', Namespace.Math]]);
|
|
11997
|
+
if (namespacePrefixKey === null) {
|
|
11998
|
+
return Namespace.HTML;
|
|
11999
|
+
}
|
|
12000
|
+
return NAMESPACES.get(namespacePrefixKey) ?? Namespace.HTML;
|
|
12001
|
+
}
|
|
12002
|
+
function keyForNamespace(namespace) {
|
|
12003
|
+
const NAMESPACES = new Map([['svg', Namespace.SVG], ['math', Namespace.Math]]);
|
|
12004
|
+
for (const [k, n] of NAMESPACES.entries()) {
|
|
12005
|
+
if (n === namespace) {
|
|
12006
|
+
return k;
|
|
12007
|
+
}
|
|
12008
|
+
}
|
|
12009
|
+
return null; // No namespace prefix for HTML
|
|
12010
|
+
}
|
|
12011
|
+
function prefixWithNamespace(strippedTag, namespace) {
|
|
12012
|
+
if (namespace === Namespace.HTML) {
|
|
12013
|
+
return strippedTag;
|
|
12014
|
+
}
|
|
12015
|
+
return `:${keyForNamespace(namespace)}:${strippedTag}`;
|
|
12016
|
+
}
|
|
11514
12017
|
function literalOrArrayLiteral(value) {
|
|
11515
12018
|
if (Array.isArray(value)) {
|
|
11516
12019
|
return literalArr(value.map(literalOrArrayLiteral));
|
|
@@ -11604,7 +12107,7 @@ class ElementAttributes {
|
|
|
11604
12107
|
return this.byKind.get(BindingKind.StyleProperty) ?? FLYWEIGHT_ARRAY;
|
|
11605
12108
|
}
|
|
11606
12109
|
get bindings() {
|
|
11607
|
-
return this.
|
|
12110
|
+
return this.propertyBindings ?? FLYWEIGHT_ARRAY;
|
|
11608
12111
|
}
|
|
11609
12112
|
get template() {
|
|
11610
12113
|
return this.byKind.get(BindingKind.Template) ?? FLYWEIGHT_ARRAY;
|
|
@@ -11616,9 +12119,10 @@ class ElementAttributes {
|
|
|
11616
12119
|
this.compatibility = compatibility;
|
|
11617
12120
|
this.known = new Map();
|
|
11618
12121
|
this.byKind = new Map;
|
|
12122
|
+
this.propertyBindings = null;
|
|
11619
12123
|
this.projectAs = null;
|
|
11620
12124
|
}
|
|
11621
|
-
isKnown(kind, name
|
|
12125
|
+
isKnown(kind, name) {
|
|
11622
12126
|
const nameToValue = this.known.get(kind) ?? new Set();
|
|
11623
12127
|
this.known.set(kind, nameToValue);
|
|
11624
12128
|
if (nameToValue.has(name)) {
|
|
@@ -11634,7 +12138,7 @@ class ElementAttributes {
|
|
|
11634
12138
|
const allowDuplicates = this.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
|
|
11635
12139
|
(kind === BindingKind.Attribute || kind === BindingKind.ClassName ||
|
|
11636
12140
|
kind === BindingKind.StyleProperty);
|
|
11637
|
-
if (!allowDuplicates && this.isKnown(kind, name
|
|
12141
|
+
if (!allowDuplicates && this.isKnown(kind, name)) {
|
|
11638
12142
|
return;
|
|
11639
12143
|
}
|
|
11640
12144
|
// TODO: Can this be its own phase
|
|
@@ -11665,10 +12169,16 @@ class ElementAttributes {
|
|
|
11665
12169
|
}
|
|
11666
12170
|
}
|
|
11667
12171
|
arrayFor(kind) {
|
|
11668
|
-
if (
|
|
11669
|
-
this.
|
|
12172
|
+
if (kind === BindingKind.Property || kind === BindingKind.TwoWayProperty) {
|
|
12173
|
+
this.propertyBindings ??= [];
|
|
12174
|
+
return this.propertyBindings;
|
|
12175
|
+
}
|
|
12176
|
+
else {
|
|
12177
|
+
if (!this.byKind.has(kind)) {
|
|
12178
|
+
this.byKind.set(kind, []);
|
|
12179
|
+
}
|
|
12180
|
+
return this.byKind.get(kind);
|
|
11670
12181
|
}
|
|
11671
|
-
return this.byKind.get(kind);
|
|
11672
12182
|
}
|
|
11673
12183
|
}
|
|
11674
12184
|
/**
|
|
@@ -11764,11 +12274,16 @@ function createDeferDepsFns(job) {
|
|
|
11764
12274
|
if (op.metadata.deps.length === 0) {
|
|
11765
12275
|
continue;
|
|
11766
12276
|
}
|
|
12277
|
+
if (op.resolverFn !== null) {
|
|
12278
|
+
continue;
|
|
12279
|
+
}
|
|
11767
12280
|
const dependencies = [];
|
|
11768
12281
|
for (const dep of op.metadata.deps) {
|
|
11769
12282
|
if (dep.isDeferrable) {
|
|
11770
12283
|
// Callback function, e.g. `m () => m.MyCmp;`.
|
|
11771
|
-
const innerFn = arrowFn(
|
|
12284
|
+
const innerFn = arrowFn(
|
|
12285
|
+
// Default imports are always accessed through the `default` property.
|
|
12286
|
+
[new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(dep.isDefaultImport ? 'default' : dep.symbolName));
|
|
11772
12287
|
// Dynamic import, e.g. `import('./a').then(...)`.
|
|
11773
12288
|
const importExpr = (new DynamicImportExpr(dep.importPath)).prop('then').callFn([innerFn]);
|
|
11774
12289
|
dependencies.push(importExpr);
|
|
@@ -11782,7 +12297,8 @@ function createDeferDepsFns(job) {
|
|
|
11782
12297
|
if (op.handle.slot === null) {
|
|
11783
12298
|
throw new Error('AssertionError: slot must be assigned bfore extracting defer deps functions');
|
|
11784
12299
|
}
|
|
11785
|
-
|
|
12300
|
+
const fullPathName = unit.fnName?.replace(`_Template`, ``);
|
|
12301
|
+
op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${fullPathName}_Defer_${op.handle.slot}_DepsFn`,
|
|
11786
12302
|
/* Don't use unique names for TDB compatibility */ false);
|
|
11787
12303
|
}
|
|
11788
12304
|
}
|
|
@@ -11870,7 +12386,7 @@ function createI18nContexts(job) {
|
|
|
11870
12386
|
if (op.message.id !== currentI18nOp.message.id) {
|
|
11871
12387
|
// This ICU is a sub-message inside its parent i18n block message. We need to give it
|
|
11872
12388
|
// its own context.
|
|
11873
|
-
const contextOp = createI18nContextOp(I18nContextKind.Icu, job.allocateXrefId(), currentI18nOp.
|
|
12389
|
+
const contextOp = createI18nContextOp(I18nContextKind.Icu, job.allocateXrefId(), currentI18nOp.root, op.message, null);
|
|
11874
12390
|
unit.create.push(contextOp);
|
|
11875
12391
|
op.context = contextOp.xref;
|
|
11876
12392
|
}
|
|
@@ -12374,7 +12890,7 @@ function createI18nMessage(job, context, messagePlaceholder) {
|
|
|
12374
12890
|
*/
|
|
12375
12891
|
function formatIcuPlaceholder(op) {
|
|
12376
12892
|
if (op.strings.length !== op.expressionPlaceholders.length + 1) {
|
|
12377
|
-
throw Error(`
|
|
12893
|
+
throw Error(`AssertionError: Invalid ICU placeholder with ${op.strings.length} strings and ${op.expressionPlaceholders.length} expressions`);
|
|
12378
12894
|
}
|
|
12379
12895
|
const values = op.expressionPlaceholders.map(formatValue);
|
|
12380
12896
|
return op.strings.flatMap((str, i) => [str, values[i] || '']).join('');
|
|
@@ -12589,6 +13105,7 @@ function recursivelyProcessView(view, parentScope) {
|
|
|
12589
13105
|
}
|
|
12590
13106
|
break;
|
|
12591
13107
|
case OpKind.Listener:
|
|
13108
|
+
case OpKind.TwoWayListener:
|
|
12592
13109
|
// Prepend variables to listener handler functions.
|
|
12593
13110
|
op.handlerOps.prepend(generateVariablesInScopeForView(view, scope));
|
|
12594
13111
|
break;
|
|
@@ -13246,15 +13763,12 @@ class Parser$1 {
|
|
|
13246
13763
|
this._lexer = _lexer;
|
|
13247
13764
|
this.errors = [];
|
|
13248
13765
|
}
|
|
13249
|
-
parseAction(input,
|
|
13766
|
+
parseAction(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
|
13250
13767
|
this._checkNoInterpolation(input, location, interpolationConfig);
|
|
13251
13768
|
const sourceToLex = this._stripComments(input);
|
|
13252
13769
|
const tokens = this._lexer.tokenize(sourceToLex);
|
|
13253
|
-
|
|
13254
|
-
|
|
13255
|
-
flags |= 2 /* ParseFlags.AssignmentEvent */;
|
|
13256
|
-
}
|
|
13257
|
-
const ast = new _ParseAST(input, location, absoluteOffset, tokens, flags, this.errors, 0).parseChain();
|
|
13770
|
+
const ast = new _ParseAST(input, location, absoluteOffset, tokens, 1 /* ParseFlags.Action */, this.errors, 0)
|
|
13771
|
+
.parseChain();
|
|
13258
13772
|
return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
|
|
13259
13773
|
}
|
|
13260
13774
|
parseBinding(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
|
@@ -13731,7 +14245,7 @@ class _ParseAST {
|
|
|
13731
14245
|
let result = this.parseExpression();
|
|
13732
14246
|
if (this.consumeOptionalOperator('|')) {
|
|
13733
14247
|
if (this.parseFlags & 1 /* ParseFlags.Action */) {
|
|
13734
|
-
this.error(
|
|
14248
|
+
this.error(`Cannot have a pipe in an action expression`);
|
|
13735
14249
|
}
|
|
13736
14250
|
do {
|
|
13737
14251
|
const nameStart = this.inputIndex;
|
|
@@ -14073,7 +14587,7 @@ class _ParseAST {
|
|
|
14073
14587
|
const nameSpan = this.sourceSpan(nameStart);
|
|
14074
14588
|
let receiver;
|
|
14075
14589
|
if (isSafe) {
|
|
14076
|
-
if (this.
|
|
14590
|
+
if (this.consumeOptionalOperator('=')) {
|
|
14077
14591
|
this.error('The \'?.\' operator cannot be used in the assignment');
|
|
14078
14592
|
receiver = new EmptyExpr$1(this.span(start), this.sourceSpan(start));
|
|
14079
14593
|
}
|
|
@@ -14082,7 +14596,7 @@ class _ParseAST {
|
|
|
14082
14596
|
}
|
|
14083
14597
|
}
|
|
14084
14598
|
else {
|
|
14085
|
-
if (this.
|
|
14599
|
+
if (this.consumeOptionalOperator('=')) {
|
|
14086
14600
|
if (!(this.parseFlags & 1 /* ParseFlags.Action */)) {
|
|
14087
14601
|
this.error('Bindings cannot contain assignments');
|
|
14088
14602
|
return new EmptyExpr$1(this.span(start), this.sourceSpan(start));
|
|
@@ -14109,22 +14623,6 @@ class _ParseAST {
|
|
|
14109
14623
|
return isSafe ? new SafeCall(span, sourceSpan, receiver, args, argumentSpan) :
|
|
14110
14624
|
new Call(span, sourceSpan, receiver, args, argumentSpan);
|
|
14111
14625
|
}
|
|
14112
|
-
consumeOptionalAssignment() {
|
|
14113
|
-
// When parsing assignment events (originating from two-way-binding aka banana-in-a-box syntax),
|
|
14114
|
-
// it is valid for the primary expression to be terminated by the non-null operator. This
|
|
14115
|
-
// primary expression is substituted as LHS of the assignment operator to achieve
|
|
14116
|
-
// two-way-binding, such that the LHS could be the non-null operator. The grammar doesn't
|
|
14117
|
-
// naturally allow for this syntax, so assignment events are parsed specially.
|
|
14118
|
-
if ((this.parseFlags & 2 /* ParseFlags.AssignmentEvent */) && this.next.isOperator('!') &&
|
|
14119
|
-
this.peek(1).isOperator('=')) {
|
|
14120
|
-
// First skip over the ! operator.
|
|
14121
|
-
this.advance();
|
|
14122
|
-
// Then skip over the = operator, to fully consume the optional assignment operator.
|
|
14123
|
-
this.advance();
|
|
14124
|
-
return true;
|
|
14125
|
-
}
|
|
14126
|
-
return this.consumeOptionalOperator('=');
|
|
14127
|
-
}
|
|
14128
14626
|
parseCallArguments() {
|
|
14129
14627
|
if (this.next.isCharacter($RPAREN))
|
|
14130
14628
|
return [];
|
|
@@ -14665,7 +15163,7 @@ const SCHEMA = [
|
|
|
14665
15163
|
'[Element]|textContent,%ariaAtomic,%ariaAutoComplete,%ariaBusy,%ariaChecked,%ariaColCount,%ariaColIndex,%ariaColSpan,%ariaCurrent,%ariaDescription,%ariaDisabled,%ariaExpanded,%ariaHasPopup,%ariaHidden,%ariaKeyShortcuts,%ariaLabel,%ariaLevel,%ariaLive,%ariaModal,%ariaMultiLine,%ariaMultiSelectable,%ariaOrientation,%ariaPlaceholder,%ariaPosInSet,%ariaPressed,%ariaReadOnly,%ariaRelevant,%ariaRequired,%ariaRoleDescription,%ariaRowCount,%ariaRowIndex,%ariaRowSpan,%ariaSelected,%ariaSetSize,%ariaSort,%ariaValueMax,%ariaValueMin,%ariaValueNow,%ariaValueText,%classList,className,elementTiming,id,innerHTML,*beforecopy,*beforecut,*beforepaste,*fullscreenchange,*fullscreenerror,*search,*webkitfullscreenchange,*webkitfullscreenerror,outerHTML,%part,#scrollLeft,#scrollTop,slot' +
|
|
14666
15164
|
/* added manually to avoid breaking changes */
|
|
14667
15165
|
',*message,*mozfullscreenchange,*mozfullscreenerror,*mozpointerlockchange,*mozpointerlockerror,*webglcontextcreationerror,*webglcontextlost,*webglcontextrestored',
|
|
14668
|
-
'[HTMLElement]^[Element]|accessKey,autocapitalize,!autofocus,contentEditable,dir,!draggable,enterKeyHint,!hidden,innerText,inputMode,lang,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,outerText,!spellcheck,%style,#tabIndex,title,!translate,virtualKeyboardPolicy',
|
|
15166
|
+
'[HTMLElement]^[Element]|accessKey,autocapitalize,!autofocus,contentEditable,dir,!draggable,enterKeyHint,!hidden,!inert,innerText,inputMode,lang,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,outerText,!spellcheck,%style,#tabIndex,title,!translate,virtualKeyboardPolicy',
|
|
14669
15167
|
'abbr,address,article,aside,b,bdi,bdo,cite,content,code,dd,dfn,dt,em,figcaption,figure,footer,header,hgroup,i,kbd,main,mark,nav,noscript,rb,rp,rt,rtc,ruby,s,samp,section,small,strong,sub,sup,u,var,wbr^[HTMLElement]|accessKey,autocapitalize,!autofocus,contentEditable,dir,!draggable,enterKeyHint,!hidden,innerText,inputMode,lang,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,outerText,!spellcheck,%style,#tabIndex,title,!translate,virtualKeyboardPolicy',
|
|
14670
15168
|
'media^[HTMLElement]|!autoplay,!controls,%controlsList,%crossOrigin,#currentTime,!defaultMuted,#defaultPlaybackRate,!disableRemotePlayback,!loop,!muted,*encrypted,*waitingforkey,#playbackRate,preload,!preservesPitch,src,%srcObject,#volume',
|
|
14671
15169
|
':svg:^[HTMLElement]|!autofocus,nonce,*abort,*animationend,*animationiteration,*animationstart,*auxclick,*beforexrselect,*blur,*cancel,*canplay,*canplaythrough,*change,*click,*close,*contextmenu,*copy,*cuechange,*cut,*dblclick,*drag,*dragend,*dragenter,*dragleave,*dragover,*dragstart,*drop,*durationchange,*emptied,*ended,*error,*focus,*formdata,*gotpointercapture,*input,*invalid,*keydown,*keypress,*keyup,*load,*loadeddata,*loadedmetadata,*loadstart,*lostpointercapture,*mousedown,*mouseenter,*mouseleave,*mousemove,*mouseout,*mouseover,*mouseup,*mousewheel,*paste,*pause,*play,*playing,*pointercancel,*pointerdown,*pointerenter,*pointerleave,*pointermove,*pointerout,*pointerover,*pointerrawupdate,*pointerup,*progress,*ratechange,*reset,*resize,*scroll,*securitypolicyviolation,*seeked,*seeking,*select,*selectionchange,*selectstart,*slotchange,*stalled,*submit,*suspend,*timeupdate,*toggle,*transitioncancel,*transitionend,*transitionrun,*transitionstart,*volumechange,*waiting,*webkitanimationend,*webkitanimationiteration,*webkitanimationstart,*webkittransitionend,*wheel,%style,#tabIndex',
|
|
@@ -20202,7 +20700,10 @@ function nameFunctionsAndVariables(job) {
|
|
|
20202
20700
|
}
|
|
20203
20701
|
function addNamesToView(unit, baseName, state, compatibility) {
|
|
20204
20702
|
if (unit.fnName === null) {
|
|
20205
|
-
|
|
20703
|
+
// Ensure unique names for view units. This is necessary because there might be multiple
|
|
20704
|
+
// components with same names in the context of the same pool. Only add the suffix
|
|
20705
|
+
// if really needed.
|
|
20706
|
+
unit.fnName = unit.job.pool.uniqueName(sanitizeIdentifier(`${baseName}_${unit.job.fnSuffix}`), /* alwaysIncludeSuffix */ false);
|
|
20206
20707
|
}
|
|
20207
20708
|
// Keep track of the names we assign to variables in the view. We'll need to propagate these
|
|
20208
20709
|
// into reads of those variables afterwards.
|
|
@@ -20235,6 +20736,15 @@ function addNamesToView(unit, baseName, state, compatibility) {
|
|
|
20235
20736
|
}
|
|
20236
20737
|
op.handlerFnName = sanitizeIdentifier(op.handlerFnName);
|
|
20237
20738
|
break;
|
|
20739
|
+
case OpKind.TwoWayListener:
|
|
20740
|
+
if (op.handlerFnName !== null) {
|
|
20741
|
+
break;
|
|
20742
|
+
}
|
|
20743
|
+
if (op.targetSlot.slot === null) {
|
|
20744
|
+
throw new Error(`Expected a slot to be assigned`);
|
|
20745
|
+
}
|
|
20746
|
+
op.handlerFnName = sanitizeIdentifier(`${unit.fnName}_${op.tag.replace('-', '_')}_${op.name}_${op.targetSlot.slot}_listener`);
|
|
20747
|
+
break;
|
|
20238
20748
|
case OpKind.Variable:
|
|
20239
20749
|
varNames.set(op.xref, getVariableName(unit, op.variable, state));
|
|
20240
20750
|
break;
|
|
@@ -20248,7 +20758,7 @@ function addNamesToView(unit, baseName, state, compatibility) {
|
|
|
20248
20758
|
if (op.emptyView !== null) {
|
|
20249
20759
|
const emptyView = unit.job.views.get(op.emptyView);
|
|
20250
20760
|
// Repeater empty view function is at slot +2 (metadata is in the first slot).
|
|
20251
|
-
addNamesToView(emptyView, `${baseName}_${
|
|
20761
|
+
addNamesToView(emptyView, `${baseName}_${op.functionNameSuffix}Empty_${op.handle.slot + 2}`, state, compatibility);
|
|
20252
20762
|
}
|
|
20253
20763
|
// Repeater primary view function is at slot +1 (metadata is in the first slot).
|
|
20254
20764
|
addNamesToView(unit.job.views.get(op.xref), `${baseName}_${op.functionNameSuffix}_${op.handle.slot + 1}`, state, compatibility);
|
|
@@ -20349,7 +20859,7 @@ function stripImportant(name) {
|
|
|
20349
20859
|
function mergeNextContextExpressions(job) {
|
|
20350
20860
|
for (const unit of job.units) {
|
|
20351
20861
|
for (const op of unit.create) {
|
|
20352
|
-
if (op.kind === OpKind.Listener) {
|
|
20862
|
+
if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
|
|
20353
20863
|
mergeNextContextsInOps(op.handlerOps);
|
|
20354
20864
|
}
|
|
20355
20865
|
}
|
|
@@ -20490,6 +21000,14 @@ function kindWithInterpolationTest(kind, interpolation) {
|
|
|
20490
21000
|
return op.kind === kind && interpolation === op.expression instanceof Interpolation;
|
|
20491
21001
|
};
|
|
20492
21002
|
}
|
|
21003
|
+
function basicListenerKindTest(op) {
|
|
21004
|
+
return (op.kind === OpKind.Listener && !(op.hostListener && op.isAnimationListener)) ||
|
|
21005
|
+
op.kind === OpKind.TwoWayListener;
|
|
21006
|
+
}
|
|
21007
|
+
function nonInterpolationPropertyKindTest(op) {
|
|
21008
|
+
return (op.kind === OpKind.Property || op.kind === OpKind.TwoWayProperty) &&
|
|
21009
|
+
!(op.expression instanceof Interpolation);
|
|
21010
|
+
}
|
|
20493
21011
|
/**
|
|
20494
21012
|
* Defines the groups based on `OpKind` that ops will be divided into, for the various create
|
|
20495
21013
|
* op kinds. Ops will be collected into groups, then optionally transformed, before recombining
|
|
@@ -20497,7 +21015,7 @@ function kindWithInterpolationTest(kind, interpolation) {
|
|
|
20497
21015
|
*/
|
|
20498
21016
|
const CREATE_ORDERING = [
|
|
20499
21017
|
{ test: op => op.kind === OpKind.Listener && op.hostListener && op.isAnimationListener },
|
|
20500
|
-
{ test:
|
|
21018
|
+
{ test: basicListenerKindTest },
|
|
20501
21019
|
];
|
|
20502
21020
|
/**
|
|
20503
21021
|
* Defines the groups based on `OpKind` that ops will be divided into, for the various update
|
|
@@ -20510,7 +21028,7 @@ const UPDATE_ORDERING = [
|
|
|
20510
21028
|
{ test: kindTest(OpKind.ClassProp) },
|
|
20511
21029
|
{ test: kindWithInterpolationTest(OpKind.Attribute, true) },
|
|
20512
21030
|
{ test: kindWithInterpolationTest(OpKind.Property, true) },
|
|
20513
|
-
{ test:
|
|
21031
|
+
{ test: nonInterpolationPropertyKindTest },
|
|
20514
21032
|
{ test: kindWithInterpolationTest(OpKind.Attribute, false) },
|
|
20515
21033
|
];
|
|
20516
21034
|
/**
|
|
@@ -20529,8 +21047,9 @@ const UPDATE_HOST_ORDERING = [
|
|
|
20529
21047
|
* The set of all op kinds we handle in the reordering phase.
|
|
20530
21048
|
*/
|
|
20531
21049
|
const handledOpKinds = new Set([
|
|
20532
|
-
OpKind.Listener, OpKind.
|
|
20533
|
-
OpKind.
|
|
21050
|
+
OpKind.Listener, OpKind.TwoWayListener, OpKind.StyleMap, OpKind.ClassMap,
|
|
21051
|
+
OpKind.StyleProp, OpKind.ClassProp, OpKind.Property, OpKind.TwoWayProperty,
|
|
21052
|
+
OpKind.HostProperty, OpKind.Attribute
|
|
20534
21053
|
]);
|
|
20535
21054
|
/**
|
|
20536
21055
|
* Many type of operations have ordering constraints that must be respected. For example, a
|
|
@@ -20794,8 +21313,9 @@ function propagateI18nBlocksToTemplates(unit, subTemplateIndex) {
|
|
|
20794
21313
|
break;
|
|
20795
21314
|
case OpKind.RepeaterCreate:
|
|
20796
21315
|
// Propagate i18n blocks to the @for template.
|
|
20797
|
-
unit.job.views.get(op.xref);
|
|
20798
|
-
subTemplateIndex =
|
|
21316
|
+
const forView = unit.job.views.get(op.xref);
|
|
21317
|
+
subTemplateIndex =
|
|
21318
|
+
propagateI18nBlocksForView(forView, i18nBlock, op.i18nPlaceholder, subTemplateIndex);
|
|
20799
21319
|
// Then if there's an @empty template, propagate the i18n blocks for it as well.
|
|
20800
21320
|
if (op.emptyView !== null) {
|
|
20801
21321
|
subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.emptyView), i18nBlock, op.emptyI18nPlaceholder, subTemplateIndex);
|
|
@@ -20996,6 +21516,12 @@ function listener(name, handlerFn, eventTargetResolver, syntheticHost, sourceSpa
|
|
|
20996
21516
|
}
|
|
20997
21517
|
return call(syntheticHost ? Identifiers.syntheticHostListener : Identifiers.listener, args, sourceSpan);
|
|
20998
21518
|
}
|
|
21519
|
+
function twoWayBindingSet(target, value) {
|
|
21520
|
+
return importExpr(Identifiers.twoWayBindingSet).callFn([target, value]);
|
|
21521
|
+
}
|
|
21522
|
+
function twoWayListener(name, handlerFn, sourceSpan) {
|
|
21523
|
+
return call(Identifiers.twoWayListener, [literal(name), handlerFn], sourceSpan);
|
|
21524
|
+
}
|
|
20999
21525
|
function pipe(slot, name) {
|
|
21000
21526
|
return call(Identifiers.pipe, [
|
|
21001
21527
|
literal(slot),
|
|
@@ -21156,6 +21682,13 @@ function property(name, expression, sanitizer, sourceSpan) {
|
|
|
21156
21682
|
}
|
|
21157
21683
|
return call(Identifiers.property, args, sourceSpan);
|
|
21158
21684
|
}
|
|
21685
|
+
function twoWayProperty(name, expression, sanitizer, sourceSpan) {
|
|
21686
|
+
const args = [literal(name), expression];
|
|
21687
|
+
if (sanitizer !== null) {
|
|
21688
|
+
args.push(sanitizer);
|
|
21689
|
+
}
|
|
21690
|
+
return call(Identifiers.twoWayProperty, args, sourceSpan);
|
|
21691
|
+
}
|
|
21159
21692
|
function attribute(name, expression, sanitizer, namespace) {
|
|
21160
21693
|
const args = [literal(name), expression];
|
|
21161
21694
|
if (sanitizer !== null || namespace !== null) {
|
|
@@ -21573,6 +22106,9 @@ function reifyCreateOperations(unit, ops) {
|
|
|
21573
22106
|
}
|
|
21574
22107
|
OpList.replace(op, listener(op.name, listenerFn, eventTargetResolver, op.hostListener && op.isAnimationListener, op.sourceSpan));
|
|
21575
22108
|
break;
|
|
22109
|
+
case OpKind.TwoWayListener:
|
|
22110
|
+
OpList.replace(op, twoWayListener(op.name, reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, true), op.sourceSpan));
|
|
22111
|
+
break;
|
|
21576
22112
|
case OpKind.Variable:
|
|
21577
22113
|
if (op.variable.name === null) {
|
|
21578
22114
|
throw new Error(`AssertionError: unnamed variable ${op.xref}`);
|
|
@@ -21681,6 +22217,9 @@ function reifyUpdateOperations(_unit, ops) {
|
|
|
21681
22217
|
OpList.replace(op, property(op.name, op.expression, op.sanitizer, op.sourceSpan));
|
|
21682
22218
|
}
|
|
21683
22219
|
break;
|
|
22220
|
+
case OpKind.TwoWayProperty:
|
|
22221
|
+
OpList.replace(op, twoWayProperty(op.name, op.expression, op.sanitizer, op.sourceSpan));
|
|
22222
|
+
break;
|
|
21684
22223
|
case OpKind.StyleProp:
|
|
21685
22224
|
if (op.expression instanceof Interpolation) {
|
|
21686
22225
|
OpList.replace(op, stylePropInterpolate(op.name, op.expression.strings, op.expression.expressions, op.unit, op.sourceSpan));
|
|
@@ -21778,6 +22317,8 @@ function reifyIrExpression(expr) {
|
|
|
21778
22317
|
return reference(expr.targetSlot.slot + 1 + expr.offset);
|
|
21779
22318
|
case ExpressionKind.LexicalRead:
|
|
21780
22319
|
throw new Error(`AssertionError: unresolved LexicalRead of ${expr.name}`);
|
|
22320
|
+
case ExpressionKind.TwoWayBindingSet:
|
|
22321
|
+
throw new Error(`AssertionError: unresolved TwoWayBindingSet`);
|
|
21781
22322
|
case ExpressionKind.RestoreView:
|
|
21782
22323
|
if (typeof expr.view === 'number') {
|
|
21783
22324
|
throw new Error(`AssertionError: unresolved RestoreView`);
|
|
@@ -21845,7 +22386,7 @@ function reifyListenerHandler(unit, name, handlerOps, consumesDollarEvent) {
|
|
|
21845
22386
|
}
|
|
21846
22387
|
|
|
21847
22388
|
/**
|
|
21848
|
-
*
|
|
22389
|
+
* Binding with no content can be safely deleted.
|
|
21849
22390
|
*/
|
|
21850
22391
|
function removeEmptyBindings(job) {
|
|
21851
22392
|
for (const unit of job.units) {
|
|
@@ -21938,6 +22479,7 @@ function processLexicalScope$1(view, ops) {
|
|
|
21938
22479
|
}
|
|
21939
22480
|
break;
|
|
21940
22481
|
case OpKind.Listener:
|
|
22482
|
+
case OpKind.TwoWayListener:
|
|
21941
22483
|
processLexicalScope$1(view, op.handlerOps);
|
|
21942
22484
|
break;
|
|
21943
22485
|
}
|
|
@@ -21967,16 +22509,19 @@ function processLexicalScope$1(view, ops) {
|
|
|
21967
22509
|
*/
|
|
21968
22510
|
function resolveDollarEvent(job) {
|
|
21969
22511
|
for (const unit of job.units) {
|
|
21970
|
-
transformDollarEvent(unit
|
|
21971
|
-
transformDollarEvent(unit
|
|
22512
|
+
transformDollarEvent(unit.create);
|
|
22513
|
+
transformDollarEvent(unit.update);
|
|
21972
22514
|
}
|
|
21973
22515
|
}
|
|
21974
|
-
function transformDollarEvent(
|
|
22516
|
+
function transformDollarEvent(ops) {
|
|
21975
22517
|
for (const op of ops) {
|
|
21976
|
-
if (op.kind === OpKind.Listener) {
|
|
22518
|
+
if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
|
|
21977
22519
|
transformExpressionsInOp(op, (expr) => {
|
|
21978
22520
|
if (expr instanceof LexicalReadExpr && expr.name === '$event') {
|
|
21979
|
-
|
|
22521
|
+
// Two-way listeners always consume `$event` so they omit this field.
|
|
22522
|
+
if (op.kind === OpKind.Listener) {
|
|
22523
|
+
op.consumesDollarEvent = true;
|
|
22524
|
+
}
|
|
21980
22525
|
return new ReadVarExpr(expr.name);
|
|
21981
22526
|
}
|
|
21982
22527
|
return expr;
|
|
@@ -22206,7 +22751,7 @@ function recordTemplateStart(job, view, slot, i18nPlaceholder, i18nContext, i18n
|
|
|
22206
22751
|
* Records an i18n param value for the closing of a template.
|
|
22207
22752
|
*/
|
|
22208
22753
|
function recordTemplateClose(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) {
|
|
22209
|
-
const {
|
|
22754
|
+
const { closeName } = i18nPlaceholder;
|
|
22210
22755
|
const flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag;
|
|
22211
22756
|
// Self-closing tags don't have a closing tag placeholder, instead the template's closing is
|
|
22212
22757
|
// recorded via an additional flag on the template start value.
|
|
@@ -22248,14 +22793,14 @@ function addParam(params, placeholder, value, subTemplateIndex, flags) {
|
|
|
22248
22793
|
*/
|
|
22249
22794
|
function resolveI18nExpressionPlaceholders(job) {
|
|
22250
22795
|
// Record all of the i18n context ops, and the sub-template index for each i18n op.
|
|
22251
|
-
const
|
|
22796
|
+
const subTemplateIndices = new Map();
|
|
22252
22797
|
const i18nContexts = new Map();
|
|
22253
22798
|
const icuPlaceholders = new Map();
|
|
22254
22799
|
for (const unit of job.units) {
|
|
22255
22800
|
for (const op of unit.create) {
|
|
22256
22801
|
switch (op.kind) {
|
|
22257
22802
|
case OpKind.I18nStart:
|
|
22258
|
-
|
|
22803
|
+
subTemplateIndices.set(op.xref, op.subTemplateIndex);
|
|
22259
22804
|
break;
|
|
22260
22805
|
case OpKind.I18nContext:
|
|
22261
22806
|
i18nContexts.set(op.xref, op);
|
|
@@ -22277,7 +22822,7 @@ function resolveI18nExpressionPlaceholders(job) {
|
|
|
22277
22822
|
for (const op of unit.update) {
|
|
22278
22823
|
if (op.kind === OpKind.I18nExpression) {
|
|
22279
22824
|
const index = expressionIndices.get(referenceIndex(op)) || 0;
|
|
22280
|
-
const subTemplateIndex =
|
|
22825
|
+
const subTemplateIndex = subTemplateIndices.get(op.i18nOwner) ?? null;
|
|
22281
22826
|
const value = {
|
|
22282
22827
|
value: index,
|
|
22283
22828
|
subTemplateIndex: subTemplateIndex,
|
|
@@ -22351,6 +22896,7 @@ function processLexicalScope(unit, ops, savedView) {
|
|
|
22351
22896
|
}
|
|
22352
22897
|
break;
|
|
22353
22898
|
case OpKind.Listener:
|
|
22899
|
+
case OpKind.TwoWayListener:
|
|
22354
22900
|
// Listener functions have separate variable declarations, so process them as a separate
|
|
22355
22901
|
// lexical scope.
|
|
22356
22902
|
processLexicalScope(unit, op.handlerOps, savedView);
|
|
@@ -22361,11 +22907,11 @@ function processLexicalScope(unit, ops, savedView) {
|
|
|
22361
22907
|
// scope. Also, look for `ir.RestoreViewExpr`s and match them with the snapshotted view context
|
|
22362
22908
|
// variable.
|
|
22363
22909
|
for (const op of ops) {
|
|
22364
|
-
if (op.kind == OpKind.Listener) {
|
|
22910
|
+
if (op.kind == OpKind.Listener || op.kind === OpKind.TwoWayListener) {
|
|
22365
22911
|
// Listeners were already processed above with their own scopes.
|
|
22366
22912
|
continue;
|
|
22367
22913
|
}
|
|
22368
|
-
transformExpressionsInOp(op, (expr
|
|
22914
|
+
transformExpressionsInOp(op, (expr) => {
|
|
22369
22915
|
if (expr instanceof LexicalReadExpr) {
|
|
22370
22916
|
// `expr` is a read of a name within the lexical scope of this view.
|
|
22371
22917
|
// Either that name is defined within the current view, or it represents a property from the
|
|
@@ -22510,6 +23056,84 @@ function getOnlySecurityContext(securityContext) {
|
|
|
22510
23056
|
return securityContext;
|
|
22511
23057
|
}
|
|
22512
23058
|
|
|
23059
|
+
/**
|
|
23060
|
+
* Transforms a `TwoWayBindingSet` expression into an expression that either
|
|
23061
|
+
* sets a value through the `twoWayBindingSet` instruction or falls back to setting
|
|
23062
|
+
* the value directly. E.g. the expression `TwoWayBindingSet(target, value)` becomes:
|
|
23063
|
+
* `ng.twoWayBindingSet(target, value) || (target = value)`.
|
|
23064
|
+
*/
|
|
23065
|
+
function transformTwoWayBindingSet(job) {
|
|
23066
|
+
for (const unit of job.units) {
|
|
23067
|
+
for (const op of unit.create) {
|
|
23068
|
+
if (op.kind === OpKind.TwoWayListener) {
|
|
23069
|
+
transformExpressionsInOp(op, (expr) => {
|
|
23070
|
+
if (expr instanceof TwoWayBindingSetExpr) {
|
|
23071
|
+
return wrapAction(expr.target, expr.value);
|
|
23072
|
+
}
|
|
23073
|
+
return expr;
|
|
23074
|
+
}, VisitorContextFlag.InChildOperation);
|
|
23075
|
+
}
|
|
23076
|
+
}
|
|
23077
|
+
}
|
|
23078
|
+
}
|
|
23079
|
+
function wrapSetOperation(target, value) {
|
|
23080
|
+
// ASSUMPTION: here we're assuming that `ReadVariableExpr` will be a reference
|
|
23081
|
+
// to a local template variable. This appears to be the case at the time of writing.
|
|
23082
|
+
// If the expression is targeting a variable read, we only emit the `twoWayBindingSet` since
|
|
23083
|
+
// the fallback would be attempting to write into a constant. Invalid usages will be flagged
|
|
23084
|
+
// during template type checking.
|
|
23085
|
+
if (target instanceof ReadVariableExpr) {
|
|
23086
|
+
return twoWayBindingSet(target, value);
|
|
23087
|
+
}
|
|
23088
|
+
return twoWayBindingSet(target, value).or(target.set(value));
|
|
23089
|
+
}
|
|
23090
|
+
function isReadExpression(value) {
|
|
23091
|
+
return value instanceof ReadPropExpr || value instanceof ReadKeyExpr ||
|
|
23092
|
+
value instanceof ReadVariableExpr;
|
|
23093
|
+
}
|
|
23094
|
+
function wrapAction(target, value) {
|
|
23095
|
+
// The only officially supported expressions inside of a two-way binding are read expressions.
|
|
23096
|
+
if (isReadExpression(target)) {
|
|
23097
|
+
return wrapSetOperation(target, value);
|
|
23098
|
+
}
|
|
23099
|
+
// However, historically the expression parser was handling two-way events by appending `=$event`
|
|
23100
|
+
// to the raw string before attempting to parse it. This has led to bugs over the years (see
|
|
23101
|
+
// #37809) and to unintentionally supporting unassignable events in the two-way binding. The
|
|
23102
|
+
// logic below aims to emulate the old behavior while still supporting the new output format
|
|
23103
|
+
// which uses `twoWayBindingSet`. Note that the generated code doesn't necessarily make sense
|
|
23104
|
+
// based on what the user wrote, for example the event binding for `[(value)]="a ? b : c"`
|
|
23105
|
+
// would produce `ctx.a ? ctx.b : ctx.c = $event`. We aim to reproduce what the parser used
|
|
23106
|
+
// to generate before #54154.
|
|
23107
|
+
if (target instanceof BinaryOperatorExpr && isReadExpression(target.rhs)) {
|
|
23108
|
+
// `a && b` -> `ctx.a && twoWayBindingSet(ctx.b, $event) || (ctx.b = $event)`
|
|
23109
|
+
return new BinaryOperatorExpr(target.operator, target.lhs, wrapSetOperation(target.rhs, value));
|
|
23110
|
+
}
|
|
23111
|
+
// Note: this also supports nullish coalescing expressions which
|
|
23112
|
+
// would've been downleveled to ternary expressions by this point.
|
|
23113
|
+
if (target instanceof ConditionalExpr && isReadExpression(target.falseCase)) {
|
|
23114
|
+
// `a ? b : c` -> `ctx.a ? ctx.b : twoWayBindingSet(ctx.c, $event) || (ctx.c = $event)`
|
|
23115
|
+
return new ConditionalExpr(target.condition, target.trueCase, wrapSetOperation(target.falseCase, value));
|
|
23116
|
+
}
|
|
23117
|
+
// `!!a` -> `twoWayBindingSet(ctx.a, $event) || (ctx.a = $event)`
|
|
23118
|
+
// Note: previously we'd actually produce `!!(ctx.a = $event)`, but the wrapping
|
|
23119
|
+
// node doesn't affect the result so we don't need to carry it over.
|
|
23120
|
+
if (target instanceof NotExpr) {
|
|
23121
|
+
let expr = target.condition;
|
|
23122
|
+
while (true) {
|
|
23123
|
+
if (expr instanceof NotExpr) {
|
|
23124
|
+
expr = expr.condition;
|
|
23125
|
+
}
|
|
23126
|
+
else {
|
|
23127
|
+
if (isReadExpression(expr)) {
|
|
23128
|
+
return wrapSetOperation(expr, value);
|
|
23129
|
+
}
|
|
23130
|
+
break;
|
|
23131
|
+
}
|
|
23132
|
+
}
|
|
23133
|
+
}
|
|
23134
|
+
throw new Error(`Unsupported expression in two-way action binding.`);
|
|
23135
|
+
}
|
|
23136
|
+
|
|
22513
23137
|
/**
|
|
22514
23138
|
* When inside of a listener, we may need access to one or more enclosing views. Therefore, each
|
|
22515
23139
|
* view should save the current view, and each listener must have the ability to restore the
|
|
@@ -22525,7 +23149,7 @@ function saveAndRestoreView(job) {
|
|
|
22525
23149
|
}, new GetCurrentViewExpr(), VariableFlags.None),
|
|
22526
23150
|
]);
|
|
22527
23151
|
for (const op of unit.create) {
|
|
22528
|
-
if (op.kind !== OpKind.Listener) {
|
|
23152
|
+
if (op.kind !== OpKind.Listener && op.kind !== OpKind.TwoWayListener) {
|
|
22529
23153
|
continue;
|
|
22530
23154
|
}
|
|
22531
23155
|
// Embedded views always need the save/restore view operation.
|
|
@@ -22715,7 +23339,7 @@ function generateTemporaries(ops) {
|
|
|
22715
23339
|
generatedStatements.push(...Array.from(new Set(defs.values()))
|
|
22716
23340
|
.map(name => createStatementOp(new DeclareVarStmt(name))));
|
|
22717
23341
|
opCount++;
|
|
22718
|
-
if (op.kind === OpKind.Listener) {
|
|
23342
|
+
if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
|
|
22719
23343
|
op.handlerOps.prepend(generateTemporaries(op.handlerOps));
|
|
22720
23344
|
}
|
|
22721
23345
|
}
|
|
@@ -22911,7 +23535,7 @@ function countVariables(job) {
|
|
|
22911
23535
|
}
|
|
22912
23536
|
});
|
|
22913
23537
|
}
|
|
22914
|
-
//
|
|
23538
|
+
// Compatibility mode pass for pure function offsets (as explained above).
|
|
22915
23539
|
if (job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
|
|
22916
23540
|
for (const op of unit.ops()) {
|
|
22917
23541
|
visitExpressionsInOp(op, expr => {
|
|
@@ -22963,6 +23587,9 @@ function varsUsedByOp(op) {
|
|
|
22963
23587
|
slots += op.expression.expressions.length;
|
|
22964
23588
|
}
|
|
22965
23589
|
return slots;
|
|
23590
|
+
case OpKind.TwoWayProperty:
|
|
23591
|
+
// Two-way properties can only have expressions so they only need one variable slot.
|
|
23592
|
+
return 1;
|
|
22966
23593
|
case OpKind.StyleProp:
|
|
22967
23594
|
case OpKind.ClassProp:
|
|
22968
23595
|
case OpKind.StyleMap:
|
|
@@ -23036,14 +23663,14 @@ function optimizeVariables(job) {
|
|
|
23036
23663
|
inlineAlwaysInlineVariables(unit.create);
|
|
23037
23664
|
inlineAlwaysInlineVariables(unit.update);
|
|
23038
23665
|
for (const op of unit.create) {
|
|
23039
|
-
if (op.kind === OpKind.Listener) {
|
|
23666
|
+
if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
|
|
23040
23667
|
inlineAlwaysInlineVariables(op.handlerOps);
|
|
23041
23668
|
}
|
|
23042
23669
|
}
|
|
23043
23670
|
optimizeVariablesInOpList(unit.create, job.compatibility);
|
|
23044
23671
|
optimizeVariablesInOpList(unit.update, job.compatibility);
|
|
23045
23672
|
for (const op of unit.create) {
|
|
23046
|
-
if (op.kind === OpKind.Listener) {
|
|
23673
|
+
if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
|
|
23047
23674
|
optimizeVariablesInOpList(op.handlerOps, job.compatibility);
|
|
23048
23675
|
}
|
|
23049
23676
|
}
|
|
@@ -23176,7 +23803,6 @@ function optimizeVariablesInOpList(ops, compatibility) {
|
|
|
23176
23803
|
const toInline = [];
|
|
23177
23804
|
for (const [id, count] of varUsages) {
|
|
23178
23805
|
const decl = varDecls.get(id);
|
|
23179
|
-
opMap.get(decl);
|
|
23180
23806
|
// We can inline variables that:
|
|
23181
23807
|
// - are used exactly once, and
|
|
23182
23808
|
// - are not used remotely
|
|
@@ -23465,7 +24091,7 @@ function wrapI18nIcus(job) {
|
|
|
23465
24091
|
* Use of this source code is governed by an MIT-style license that can be
|
|
23466
24092
|
* found in the LICENSE file at https://angular.io/license
|
|
23467
24093
|
*/
|
|
23468
|
-
[
|
|
24094
|
+
const phases = [
|
|
23469
24095
|
{ kind: CompilationJobKind.Tmpl, fn: removeContentSelectors },
|
|
23470
24096
|
{ kind: CompilationJobKind.Host, fn: parseHostStyleProperties },
|
|
23471
24097
|
{ kind: CompilationJobKind.Tmpl, fn: emitNamespaceChanges },
|
|
@@ -23498,6 +24124,7 @@ function wrapI18nIcus(job) {
|
|
|
23498
24124
|
{ kind: CompilationJobKind.Tmpl, fn: generateTrackVariables },
|
|
23499
24125
|
{ kind: CompilationJobKind.Both, fn: resolveNames },
|
|
23500
24126
|
{ kind: CompilationJobKind.Tmpl, fn: resolveDeferTargetNames },
|
|
24127
|
+
{ kind: CompilationJobKind.Tmpl, fn: transformTwoWayBindingSet },
|
|
23501
24128
|
{ kind: CompilationJobKind.Tmpl, fn: optimizeTrackFns },
|
|
23502
24129
|
{ kind: CompilationJobKind.Both, fn: resolveContexts },
|
|
23503
24130
|
{ kind: CompilationJobKind.Both, fn: resolveSanitizers },
|
|
@@ -23506,7 +24133,6 @@ function wrapI18nIcus(job) {
|
|
|
23506
24133
|
{ kind: CompilationJobKind.Both, fn: expandSafeReads },
|
|
23507
24134
|
{ kind: CompilationJobKind.Both, fn: generateTemporaryVariables },
|
|
23508
24135
|
{ kind: CompilationJobKind.Tmpl, fn: allocateSlots },
|
|
23509
|
-
{ kind: CompilationJobKind.Tmpl, fn: createDeferDepsFns },
|
|
23510
24136
|
{ kind: CompilationJobKind.Tmpl, fn: resolveI18nElementPlaceholders },
|
|
23511
24137
|
{ kind: CompilationJobKind.Tmpl, fn: resolveI18nExpressionPlaceholders },
|
|
23512
24138
|
{ kind: CompilationJobKind.Tmpl, fn: extractI18nMessages },
|
|
@@ -23519,6 +24145,7 @@ function wrapI18nIcus(job) {
|
|
|
23519
24145
|
{ kind: CompilationJobKind.Tmpl, fn: generateAdvance },
|
|
23520
24146
|
{ kind: CompilationJobKind.Both, fn: optimizeVariables },
|
|
23521
24147
|
{ kind: CompilationJobKind.Both, fn: nameFunctionsAndVariables },
|
|
24148
|
+
{ kind: CompilationJobKind.Tmpl, fn: createDeferDepsFns },
|
|
23522
24149
|
{ kind: CompilationJobKind.Tmpl, fn: mergeNextContextExpressions },
|
|
23523
24150
|
{ kind: CompilationJobKind.Tmpl, fn: generateNgContainerOps },
|
|
23524
24151
|
{ kind: CompilationJobKind.Tmpl, fn: collapseEmptyInstructions },
|
|
@@ -23527,546 +24154,1111 @@ function wrapI18nIcus(job) {
|
|
|
23527
24154
|
{ kind: CompilationJobKind.Both, fn: reify },
|
|
23528
24155
|
{ kind: CompilationJobKind.Both, fn: chain },
|
|
23529
24156
|
];
|
|
23530
|
-
|
|
23531
|
-
CompatibilityMode.TemplateDefinitionBuilder;
|
|
23532
|
-
// Schema containing DOM elements and their properties.
|
|
23533
|
-
new DomElementSchemaRegistry();
|
|
23534
|
-
// TODO: Can we populate Template binding kinds in ingest?
|
|
23535
|
-
new Map([
|
|
23536
|
-
[0 /* e.BindingType.Property */, BindingKind.Property],
|
|
23537
|
-
[1 /* e.BindingType.Attribute */, BindingKind.Attribute],
|
|
23538
|
-
[2 /* e.BindingType.Class */, BindingKind.ClassName],
|
|
23539
|
-
[3 /* e.BindingType.Style */, BindingKind.StyleProperty],
|
|
23540
|
-
[4 /* e.BindingType.Animation */, BindingKind.Animation],
|
|
23541
|
-
]);
|
|
23542
|
-
|
|
23543
|
-
const IMPORTANT_FLAG = '!important';
|
|
23544
24157
|
/**
|
|
23545
|
-
*
|
|
23546
|
-
*
|
|
23547
|
-
|
|
23548
|
-
|
|
23549
|
-
|
|
23550
|
-
|
|
23551
|
-
|
|
23552
|
-
|
|
23553
|
-
|
|
23554
|
-
|
|
23555
|
-
|
|
23556
|
-
|
|
23557
|
-
* slot 1) prop name
|
|
23558
|
-
* slot 2) binding index that points to the previous style/class binding (and some extra config
|
|
23559
|
-
* values)
|
|
23560
|
-
*
|
|
23561
|
-
* Let's imagine we have a binding that looks like so:
|
|
23562
|
-
*
|
|
23563
|
-
* ```
|
|
23564
|
-
* <div [style.width]="x" [style.height]="y">
|
|
23565
|
-
* ```
|
|
23566
|
-
*
|
|
23567
|
-
* Our `LView` and `TData` data-structures look like so:
|
|
23568
|
-
*
|
|
23569
|
-
* ```typescript
|
|
23570
|
-
* LView = [
|
|
23571
|
-
* // ...
|
|
23572
|
-
* x, // value of x
|
|
23573
|
-
* "width: x",
|
|
23574
|
-
*
|
|
23575
|
-
* y, // value of y
|
|
23576
|
-
* "width: x; height: y",
|
|
23577
|
-
* // ...
|
|
23578
|
-
* ];
|
|
23579
|
-
*
|
|
23580
|
-
* TData = [
|
|
23581
|
-
* // ...
|
|
23582
|
-
* "width", // binding slot 20
|
|
23583
|
-
* 0,
|
|
23584
|
-
*
|
|
23585
|
-
* "height",
|
|
23586
|
-
* 20,
|
|
23587
|
-
* // ...
|
|
23588
|
-
* ];
|
|
23589
|
-
* ```
|
|
23590
|
-
*
|
|
23591
|
-
* */
|
|
23592
|
-
const MIN_STYLING_BINDING_SLOTS_REQUIRED = 2;
|
|
24158
|
+
* Run all transformation phases in the correct order against a compilation job. After this
|
|
24159
|
+
* processing, the compilation should be in a state where it can be emitted.
|
|
24160
|
+
*/
|
|
24161
|
+
function transform(job, kind) {
|
|
24162
|
+
for (const phase of phases) {
|
|
24163
|
+
if (phase.kind === kind || phase.kind === CompilationJobKind.Both) {
|
|
24164
|
+
// The type of `Phase` above ensures it is impossible to call a phase that doesn't support the
|
|
24165
|
+
// job kind.
|
|
24166
|
+
phase.fn(job);
|
|
24167
|
+
}
|
|
24168
|
+
}
|
|
24169
|
+
}
|
|
23593
24170
|
/**
|
|
23594
|
-
*
|
|
23595
|
-
*
|
|
23596
|
-
* It also produces the creation instruction to register all initial styling values
|
|
23597
|
-
* (which are all the static class="..." and style="..." attribute values that exist
|
|
23598
|
-
* on an element within a template).
|
|
23599
|
-
*
|
|
23600
|
-
* The builder class below handles producing instructions for the following cases:
|
|
23601
|
-
*
|
|
23602
|
-
* - Static style/class attributes (style="..." and class="...")
|
|
23603
|
-
* - Dynamic style/class map bindings ([style]="map" and [class]="map|string")
|
|
23604
|
-
* - Dynamic style/class property bindings ([style.prop]="exp" and [class.name]="exp")
|
|
23605
|
-
*
|
|
23606
|
-
* Due to the complex relationship of all of these cases, the instructions generated
|
|
23607
|
-
* for these attributes/properties/bindings must be done so in the correct order. The
|
|
23608
|
-
* order which these must be generated is as follows:
|
|
23609
|
-
*
|
|
23610
|
-
* if (createMode) {
|
|
23611
|
-
* styling(...)
|
|
23612
|
-
* }
|
|
23613
|
-
* if (updateMode) {
|
|
23614
|
-
* styleMap(...)
|
|
23615
|
-
* classMap(...)
|
|
23616
|
-
* styleProp(...)
|
|
23617
|
-
* classProp(...)
|
|
23618
|
-
* }
|
|
23619
|
-
*
|
|
23620
|
-
* The creation/update methods within the builder class produce these instructions.
|
|
24171
|
+
* Compile all views in the given `ComponentCompilation` into the final template function, which may
|
|
24172
|
+
* reference constants defined in a `ConstantPool`.
|
|
23621
24173
|
*/
|
|
23622
|
-
|
|
23623
|
-
|
|
23624
|
-
|
|
23625
|
-
|
|
23626
|
-
|
|
23627
|
-
|
|
23628
|
-
|
|
23629
|
-
|
|
23630
|
-
|
|
23631
|
-
|
|
23632
|
-
|
|
23633
|
-
|
|
23634
|
-
|
|
23635
|
-
|
|
23636
|
-
this._styleMapInput = null;
|
|
23637
|
-
/** an array of each [style.prop] input */
|
|
23638
|
-
this._singleStyleInputs = null;
|
|
23639
|
-
/** an array of each [class.name] input */
|
|
23640
|
-
this._singleClassInputs = null;
|
|
23641
|
-
this._lastStylingInput = null;
|
|
23642
|
-
this._firstStylingInput = null;
|
|
23643
|
-
// maps are used instead of hash maps because a Map will
|
|
23644
|
-
// retain the ordering of the keys
|
|
23645
|
-
/**
|
|
23646
|
-
* Represents the location of each style binding in the template
|
|
23647
|
-
* (e.g. `<div [style.width]="w" [style.height]="h">` implies
|
|
23648
|
-
* that `width=0` and `height=1`)
|
|
23649
|
-
*/
|
|
23650
|
-
this._stylesIndex = new Map();
|
|
23651
|
-
/**
|
|
23652
|
-
* Represents the location of each class binding in the template
|
|
23653
|
-
* (e.g. `<div [class.big]="b" [class.hidden]="h">` implies
|
|
23654
|
-
* that `big=0` and `hidden=1`)
|
|
23655
|
-
*/
|
|
23656
|
-
this._classesIndex = new Map();
|
|
23657
|
-
this._initialStyleValues = [];
|
|
23658
|
-
this._initialClassValues = [];
|
|
24174
|
+
function emitTemplateFn(tpl, pool) {
|
|
24175
|
+
const rootFn = emitView(tpl.root);
|
|
24176
|
+
emitChildViews(tpl.root, pool);
|
|
24177
|
+
return rootFn;
|
|
24178
|
+
}
|
|
24179
|
+
function emitChildViews(parent, pool) {
|
|
24180
|
+
for (const unit of parent.job.units) {
|
|
24181
|
+
if (unit.parent !== parent.xref) {
|
|
24182
|
+
continue;
|
|
24183
|
+
}
|
|
24184
|
+
// Child views are emitted depth-first.
|
|
24185
|
+
emitChildViews(unit, pool);
|
|
24186
|
+
const viewFn = emitView(unit);
|
|
24187
|
+
pool.statements.push(viewFn.toDeclStmt(viewFn.name));
|
|
23659
24188
|
}
|
|
23660
|
-
|
|
23661
|
-
|
|
23662
|
-
|
|
23663
|
-
|
|
23664
|
-
|
|
23665
|
-
|
|
23666
|
-
|
|
23667
|
-
|
|
23668
|
-
|
|
23669
|
-
|
|
23670
|
-
|
|
23671
|
-
|
|
23672
|
-
|
|
23673
|
-
let binding = null;
|
|
23674
|
-
let name = input.name;
|
|
23675
|
-
switch (input.type) {
|
|
23676
|
-
case 0 /* BindingType.Property */:
|
|
23677
|
-
binding = this.registerInputBasedOnName(name, input.value, input.sourceSpan);
|
|
23678
|
-
break;
|
|
23679
|
-
case 3 /* BindingType.Style */:
|
|
23680
|
-
binding = this.registerStyleInput(name, false, input.value, input.sourceSpan, input.unit);
|
|
23681
|
-
break;
|
|
23682
|
-
case 2 /* BindingType.Class */:
|
|
23683
|
-
binding = this.registerClassInput(name, false, input.value, input.sourceSpan);
|
|
23684
|
-
break;
|
|
24189
|
+
}
|
|
24190
|
+
/**
|
|
24191
|
+
* Emit a template function for an individual `ViewCompilation` (which may be either the root view
|
|
24192
|
+
* or an embedded view).
|
|
24193
|
+
*/
|
|
24194
|
+
function emitView(view) {
|
|
24195
|
+
if (view.fnName === null) {
|
|
24196
|
+
throw new Error(`AssertionError: view ${view.xref} is unnamed`);
|
|
24197
|
+
}
|
|
24198
|
+
const createStatements = [];
|
|
24199
|
+
for (const op of view.create) {
|
|
24200
|
+
if (op.kind !== OpKind.Statement) {
|
|
24201
|
+
throw new Error(`AssertionError: expected all create ops to have been compiled, but got ${OpKind[op.kind]}`);
|
|
23685
24202
|
}
|
|
23686
|
-
|
|
24203
|
+
createStatements.push(op.statement);
|
|
23687
24204
|
}
|
|
23688
|
-
|
|
23689
|
-
|
|
23690
|
-
|
|
23691
|
-
|
|
23692
|
-
const isClass = !isStyle && (name === 'class' || prefix === 'class.' || prefix === 'class!');
|
|
23693
|
-
if (isStyle || isClass) {
|
|
23694
|
-
const isMapBased = name.charAt(5) !== '.'; // style.prop or class.prop makes this a no
|
|
23695
|
-
const property = name.slice(isMapBased ? 5 : 6); // the dot explains why there's a +1
|
|
23696
|
-
if (isStyle) {
|
|
23697
|
-
binding = this.registerStyleInput(property, isMapBased, expression, sourceSpan);
|
|
23698
|
-
}
|
|
23699
|
-
else {
|
|
23700
|
-
binding = this.registerClassInput(property, isMapBased, expression, sourceSpan);
|
|
23701
|
-
}
|
|
24205
|
+
const updateStatements = [];
|
|
24206
|
+
for (const op of view.update) {
|
|
24207
|
+
if (op.kind !== OpKind.Statement) {
|
|
24208
|
+
throw new Error(`AssertionError: expected all update ops to have been compiled, but got ${OpKind[op.kind]}`);
|
|
23702
24209
|
}
|
|
23703
|
-
|
|
24210
|
+
updateStatements.push(op.statement);
|
|
23704
24211
|
}
|
|
23705
|
-
|
|
23706
|
-
|
|
23707
|
-
|
|
24212
|
+
const createCond = maybeGenerateRfBlock(1, createStatements);
|
|
24213
|
+
const updateCond = maybeGenerateRfBlock(2, updateStatements);
|
|
24214
|
+
return fn([
|
|
24215
|
+
new FnParam('rf'),
|
|
24216
|
+
new FnParam('ctx'),
|
|
24217
|
+
], [
|
|
24218
|
+
...createCond,
|
|
24219
|
+
...updateCond,
|
|
24220
|
+
],
|
|
24221
|
+
/* type */ undefined, /* sourceSpan */ undefined, view.fnName);
|
|
24222
|
+
}
|
|
24223
|
+
function maybeGenerateRfBlock(flag, statements) {
|
|
24224
|
+
if (statements.length === 0) {
|
|
24225
|
+
return [];
|
|
24226
|
+
}
|
|
24227
|
+
return [
|
|
24228
|
+
ifStmt(new BinaryOperatorExpr(exports.BinaryOperator.BitwiseAnd, variable('rf'), literal(flag)), statements),
|
|
24229
|
+
];
|
|
24230
|
+
}
|
|
24231
|
+
function emitHostBindingFunction(job) {
|
|
24232
|
+
if (job.root.fnName === null) {
|
|
24233
|
+
throw new Error(`AssertionError: host binding function is unnamed`);
|
|
24234
|
+
}
|
|
24235
|
+
const createStatements = [];
|
|
24236
|
+
for (const op of job.root.create) {
|
|
24237
|
+
if (op.kind !== OpKind.Statement) {
|
|
24238
|
+
throw new Error(`AssertionError: expected all create ops to have been compiled, but got ${OpKind[op.kind]}`);
|
|
23708
24239
|
}
|
|
23709
|
-
|
|
23710
|
-
|
|
23711
|
-
|
|
23712
|
-
|
|
24240
|
+
createStatements.push(op.statement);
|
|
24241
|
+
}
|
|
24242
|
+
const updateStatements = [];
|
|
24243
|
+
for (const op of job.root.update) {
|
|
24244
|
+
if (op.kind !== OpKind.Statement) {
|
|
24245
|
+
throw new Error(`AssertionError: expected all update ops to have been compiled, but got ${OpKind[op.kind]}`);
|
|
23713
24246
|
}
|
|
23714
|
-
|
|
23715
|
-
|
|
23716
|
-
|
|
23717
|
-
|
|
23718
|
-
|
|
24247
|
+
updateStatements.push(op.statement);
|
|
24248
|
+
}
|
|
24249
|
+
if (createStatements.length === 0 && updateStatements.length === 0) {
|
|
24250
|
+
return null;
|
|
24251
|
+
}
|
|
24252
|
+
const createCond = maybeGenerateRfBlock(1, createStatements);
|
|
24253
|
+
const updateCond = maybeGenerateRfBlock(2, updateStatements);
|
|
24254
|
+
return fn([
|
|
24255
|
+
new FnParam('rf'),
|
|
24256
|
+
new FnParam('ctx'),
|
|
24257
|
+
], [
|
|
24258
|
+
...createCond,
|
|
24259
|
+
...updateCond,
|
|
24260
|
+
],
|
|
24261
|
+
/* type */ undefined, /* sourceSpan */ undefined, job.root.fnName);
|
|
24262
|
+
}
|
|
24263
|
+
|
|
24264
|
+
const compatibilityMode = CompatibilityMode.TemplateDefinitionBuilder;
|
|
24265
|
+
// Schema containing DOM elements and their properties.
|
|
24266
|
+
const domSchema = new DomElementSchemaRegistry();
|
|
24267
|
+
// Tag name of the `ng-template` element.
|
|
24268
|
+
const NG_TEMPLATE_TAG_NAME$1 = 'ng-template';
|
|
24269
|
+
/**
|
|
24270
|
+
* Process a template AST and convert it into a `ComponentCompilation` in the intermediate
|
|
24271
|
+
* representation.
|
|
24272
|
+
* TODO: Refactor more of the ingestion code into phases.
|
|
24273
|
+
*/
|
|
24274
|
+
function ingestComponent(componentName, template, constantPool, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta, allDeferrableDepsFn) {
|
|
24275
|
+
const job = new ComponentCompilationJob(componentName, constantPool, compatibilityMode, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta, allDeferrableDepsFn);
|
|
24276
|
+
ingestNodes(job.root, template);
|
|
24277
|
+
return job;
|
|
24278
|
+
}
|
|
24279
|
+
/**
|
|
24280
|
+
* Process a host binding AST and convert it into a `HostBindingCompilationJob` in the intermediate
|
|
24281
|
+
* representation.
|
|
24282
|
+
*/
|
|
24283
|
+
function ingestHostBinding(input, bindingParser, constantPool) {
|
|
24284
|
+
const job = new HostBindingCompilationJob(input.componentName, constantPool, compatibilityMode);
|
|
24285
|
+
for (const property of input.properties ?? []) {
|
|
24286
|
+
let bindingKind = BindingKind.Property;
|
|
24287
|
+
// TODO: this should really be handled in the parser.
|
|
24288
|
+
if (property.name.startsWith('attr.')) {
|
|
24289
|
+
property.name = property.name.substring('attr.'.length);
|
|
24290
|
+
bindingKind = BindingKind.Attribute;
|
|
23719
24291
|
}
|
|
23720
|
-
|
|
23721
|
-
|
|
23722
|
-
registerIntoMap(this._stylesIndex, property);
|
|
24292
|
+
if (property.isAnimation) {
|
|
24293
|
+
bindingKind = BindingKind.Animation;
|
|
23723
24294
|
}
|
|
23724
|
-
|
|
23725
|
-
|
|
23726
|
-
|
|
23727
|
-
|
|
23728
|
-
return entry;
|
|
24295
|
+
const securityContexts = bindingParser
|
|
24296
|
+
.calcPossibleSecurityContexts(input.componentSelector, property.name, bindingKind === BindingKind.Attribute)
|
|
24297
|
+
.filter(context => context !== SecurityContext.NONE);
|
|
24298
|
+
ingestHostProperty(job, property, bindingKind, securityContexts);
|
|
23729
24299
|
}
|
|
23730
|
-
|
|
23731
|
-
|
|
23732
|
-
|
|
24300
|
+
for (const [name, expr] of Object.entries(input.attributes) ?? []) {
|
|
24301
|
+
const securityContexts = bindingParser.calcPossibleSecurityContexts(input.componentSelector, name, true)
|
|
24302
|
+
.filter(context => context !== SecurityContext.NONE);
|
|
24303
|
+
ingestHostAttribute(job, name, expr, securityContexts);
|
|
24304
|
+
}
|
|
24305
|
+
for (const event of input.events ?? []) {
|
|
24306
|
+
ingestHostEvent(job, event);
|
|
24307
|
+
}
|
|
24308
|
+
return job;
|
|
24309
|
+
}
|
|
24310
|
+
// TODO: We should refactor the parser to use the same types and structures for host bindings as
|
|
24311
|
+
// with ordinary components. This would allow us to share a lot more ingestion code.
|
|
24312
|
+
function ingestHostProperty(job, property, bindingKind, securityContexts) {
|
|
24313
|
+
let expression;
|
|
24314
|
+
const ast = property.expression.ast;
|
|
24315
|
+
if (ast instanceof Interpolation$1) {
|
|
24316
|
+
expression = new Interpolation(ast.strings, ast.expressions.map(expr => convertAst(expr, job, property.sourceSpan)), []);
|
|
24317
|
+
}
|
|
24318
|
+
else {
|
|
24319
|
+
expression = convertAst(ast, job, property.sourceSpan);
|
|
24320
|
+
}
|
|
24321
|
+
job.root.update.push(createBindingOp(job.root.xref, bindingKind, property.name, expression, null, securityContexts, false, false, null, /* TODO: How do Host bindings handle i18n attrs? */ null, property.sourceSpan));
|
|
24322
|
+
}
|
|
24323
|
+
function ingestHostAttribute(job, name, value, securityContexts) {
|
|
24324
|
+
const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, securityContexts,
|
|
24325
|
+
/* Host attributes should always be extracted to const hostAttrs, even if they are not
|
|
24326
|
+
*strictly* text literals */
|
|
24327
|
+
true, false, null,
|
|
24328
|
+
/* TODO */ null,
|
|
24329
|
+
/** TODO: May be null? */ value.sourceSpan);
|
|
24330
|
+
job.root.update.push(attrBinding);
|
|
24331
|
+
}
|
|
24332
|
+
function ingestHostEvent(job, event) {
|
|
24333
|
+
const [phase, target] = event.type !== 1 /* e.ParsedEventType.Animation */ ? [null, event.targetOrPhase] :
|
|
24334
|
+
[event.targetOrPhase, null];
|
|
24335
|
+
const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, makeListenerHandlerOps(job.root, event.handler, event.handlerSpan), phase, target, true, event.sourceSpan);
|
|
24336
|
+
job.root.create.push(eventBinding);
|
|
24337
|
+
}
|
|
24338
|
+
/**
|
|
24339
|
+
* Ingest the nodes of a template AST into the given `ViewCompilation`.
|
|
24340
|
+
*/
|
|
24341
|
+
function ingestNodes(unit, template) {
|
|
24342
|
+
for (const node of template) {
|
|
24343
|
+
if (node instanceof Element$1) {
|
|
24344
|
+
ingestElement(unit, node);
|
|
23733
24345
|
}
|
|
23734
|
-
|
|
23735
|
-
|
|
23736
|
-
if (isMapBased) {
|
|
23737
|
-
this._classMapInput = entry;
|
|
24346
|
+
else if (node instanceof Template) {
|
|
24347
|
+
ingestTemplate(unit, node);
|
|
23738
24348
|
}
|
|
23739
|
-
else {
|
|
23740
|
-
(
|
|
23741
|
-
registerIntoMap(this._classesIndex, property);
|
|
24349
|
+
else if (node instanceof Content) {
|
|
24350
|
+
ingestContent(unit, node);
|
|
23742
24351
|
}
|
|
23743
|
-
|
|
23744
|
-
|
|
23745
|
-
|
|
23746
|
-
|
|
23747
|
-
|
|
23748
|
-
|
|
23749
|
-
|
|
23750
|
-
|
|
23751
|
-
|
|
24352
|
+
else if (node instanceof Text$3) {
|
|
24353
|
+
ingestText(unit, node, null);
|
|
24354
|
+
}
|
|
24355
|
+
else if (node instanceof BoundText) {
|
|
24356
|
+
ingestBoundText(unit, node, null);
|
|
24357
|
+
}
|
|
24358
|
+
else if (node instanceof IfBlock) {
|
|
24359
|
+
ingestIfBlock(unit, node);
|
|
24360
|
+
}
|
|
24361
|
+
else if (node instanceof SwitchBlock) {
|
|
24362
|
+
ingestSwitchBlock(unit, node);
|
|
24363
|
+
}
|
|
24364
|
+
else if (node instanceof DeferredBlock) {
|
|
24365
|
+
ingestDeferBlock(unit, node);
|
|
24366
|
+
}
|
|
24367
|
+
else if (node instanceof Icu$1) {
|
|
24368
|
+
ingestIcu(unit, node);
|
|
23752
24369
|
}
|
|
24370
|
+
else if (node instanceof ForLoopBlock) {
|
|
24371
|
+
ingestForBlock(unit, node);
|
|
24372
|
+
}
|
|
24373
|
+
else {
|
|
24374
|
+
throw new Error(`Unsupported template node: ${node.constructor.name}`);
|
|
24375
|
+
}
|
|
24376
|
+
}
|
|
24377
|
+
}
|
|
24378
|
+
/**
|
|
24379
|
+
* Ingest an element AST from the template into the given `ViewCompilation`.
|
|
24380
|
+
*/
|
|
24381
|
+
function ingestElement(unit, element) {
|
|
24382
|
+
if (element.i18n !== undefined &&
|
|
24383
|
+
!(element.i18n instanceof Message || element.i18n instanceof TagPlaceholder)) {
|
|
24384
|
+
throw Error(`Unhandled i18n metadata type for element: ${element.i18n.constructor.name}`);
|
|
24385
|
+
}
|
|
24386
|
+
const id = unit.job.allocateXrefId();
|
|
24387
|
+
const [namespaceKey, elementName] = splitNsName(element.name);
|
|
24388
|
+
const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan, element.sourceSpan);
|
|
24389
|
+
unit.create.push(startOp);
|
|
24390
|
+
ingestElementBindings(unit, startOp, element);
|
|
24391
|
+
ingestReferences(startOp, element);
|
|
24392
|
+
// Start i18n, if needed, goes after the element create and bindings, but before the nodes
|
|
24393
|
+
let i18nBlockId = null;
|
|
24394
|
+
if (element.i18n instanceof Message) {
|
|
24395
|
+
i18nBlockId = unit.job.allocateXrefId();
|
|
24396
|
+
unit.create.push(createI18nStartOp(i18nBlockId, element.i18n, undefined, element.startSourceSpan));
|
|
24397
|
+
}
|
|
24398
|
+
ingestNodes(unit, element.children);
|
|
24399
|
+
// The source span for the end op is typically the element closing tag. However, if no closing tag
|
|
24400
|
+
// exists, such as in `<input>`, we use the start source span instead. Usually the start and end
|
|
24401
|
+
// instructions will be collapsed into one `element` instruction, negating the purpose of this
|
|
24402
|
+
// fallback, but in cases when it is not collapsed (such as an input with a binding), we still
|
|
24403
|
+
// want to map the end instruction to the main element.
|
|
24404
|
+
const endOp = createElementEndOp(id, element.endSourceSpan ?? element.startSourceSpan);
|
|
24405
|
+
unit.create.push(endOp);
|
|
24406
|
+
// If there is an i18n message associated with this element, insert i18n start and end ops.
|
|
24407
|
+
if (i18nBlockId !== null) {
|
|
24408
|
+
OpList.insertBefore(createI18nEndOp(i18nBlockId, element.endSourceSpan ?? element.startSourceSpan), endOp);
|
|
24409
|
+
}
|
|
24410
|
+
}
|
|
24411
|
+
/**
|
|
24412
|
+
* Ingest an `ng-template` node from the AST into the given `ViewCompilation`.
|
|
24413
|
+
*/
|
|
24414
|
+
function ingestTemplate(unit, tmpl) {
|
|
24415
|
+
if (tmpl.i18n !== undefined &&
|
|
24416
|
+
!(tmpl.i18n instanceof Message || tmpl.i18n instanceof TagPlaceholder)) {
|
|
24417
|
+
throw Error(`Unhandled i18n metadata type for template: ${tmpl.i18n.constructor.name}`);
|
|
24418
|
+
}
|
|
24419
|
+
const childView = unit.job.allocateView(unit.xref);
|
|
24420
|
+
let tagNameWithoutNamespace = tmpl.tagName;
|
|
24421
|
+
let namespacePrefix = '';
|
|
24422
|
+
if (tmpl.tagName) {
|
|
24423
|
+
[namespacePrefix, tagNameWithoutNamespace] = splitNsName(tmpl.tagName);
|
|
24424
|
+
}
|
|
24425
|
+
const i18nPlaceholder = tmpl.i18n instanceof TagPlaceholder ? tmpl.i18n : undefined;
|
|
24426
|
+
const namespace = namespaceForKey(namespacePrefix);
|
|
24427
|
+
const functionNameSuffix = tagNameWithoutNamespace === null ?
|
|
24428
|
+
'' :
|
|
24429
|
+
prefixWithNamespace(tagNameWithoutNamespace, namespace);
|
|
24430
|
+
const templateKind = isPlainTemplate(tmpl) ? TemplateKind.NgTemplate : TemplateKind.Structural;
|
|
24431
|
+
const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan, tmpl.sourceSpan);
|
|
24432
|
+
unit.create.push(templateOp);
|
|
24433
|
+
ingestTemplateBindings(unit, templateOp, tmpl, templateKind);
|
|
24434
|
+
ingestReferences(templateOp, tmpl);
|
|
24435
|
+
ingestNodes(childView, tmpl.children);
|
|
24436
|
+
for (const { name, value } of tmpl.variables) {
|
|
24437
|
+
childView.contextVariables.set(name, value !== '' ? value : '$implicit');
|
|
24438
|
+
}
|
|
24439
|
+
// If this is a plain template and there is an i18n message associated with it, insert i18n start
|
|
24440
|
+
// and end ops. For structural directive templates, the i18n ops will be added when ingesting the
|
|
24441
|
+
// element/template the directive is placed on.
|
|
24442
|
+
if (templateKind === TemplateKind.NgTemplate && tmpl.i18n instanceof Message) {
|
|
24443
|
+
const id = unit.job.allocateXrefId();
|
|
24444
|
+
OpList.insertAfter(createI18nStartOp(id, tmpl.i18n, undefined, tmpl.startSourceSpan), childView.create.head);
|
|
24445
|
+
OpList.insertBefore(createI18nEndOp(id, tmpl.endSourceSpan ?? tmpl.startSourceSpan), childView.create.tail);
|
|
23753
24446
|
}
|
|
23754
|
-
|
|
23755
|
-
|
|
23756
|
-
|
|
23757
|
-
|
|
23758
|
-
|
|
23759
|
-
|
|
23760
|
-
|
|
23761
|
-
this._hasInitialValues = true;
|
|
24447
|
+
}
|
|
24448
|
+
/**
|
|
24449
|
+
* Ingest a content node from the AST into the given `ViewCompilation`.
|
|
24450
|
+
*/
|
|
24451
|
+
function ingestContent(unit, content) {
|
|
24452
|
+
if (content.i18n !== undefined && !(content.i18n instanceof TagPlaceholder)) {
|
|
24453
|
+
throw Error(`Unhandled i18n metadata type for element: ${content.i18n.constructor.name}`);
|
|
23762
24454
|
}
|
|
23763
|
-
|
|
23764
|
-
|
|
23765
|
-
|
|
23766
|
-
|
|
23767
|
-
*/
|
|
23768
|
-
registerClassAttr(value) {
|
|
23769
|
-
this._initialClassValues = value.trim().split(/\s+/g);
|
|
23770
|
-
this._hasInitialValues = true;
|
|
24455
|
+
const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.i18n, content.sourceSpan);
|
|
24456
|
+
for (const attr of content.attributes) {
|
|
24457
|
+
const securityContext = domSchema.securityContext(content.name, attr.name, true);
|
|
24458
|
+
unit.update.push(createBindingOp(op.xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
|
|
23771
24459
|
}
|
|
23772
|
-
|
|
23773
|
-
|
|
23774
|
-
|
|
23775
|
-
|
|
23776
|
-
|
|
23777
|
-
|
|
23778
|
-
|
|
23779
|
-
|
|
23780
|
-
|
|
23781
|
-
|
|
23782
|
-
|
|
23783
|
-
|
|
23784
|
-
|
|
24460
|
+
unit.create.push(op);
|
|
24461
|
+
}
|
|
24462
|
+
/**
|
|
24463
|
+
* Ingest a literal text node from the AST into the given `ViewCompilation`.
|
|
24464
|
+
*/
|
|
24465
|
+
function ingestText(unit, text, icuPlaceholder) {
|
|
24466
|
+
unit.create.push(createTextOp(unit.job.allocateXrefId(), text.value, icuPlaceholder, text.sourceSpan));
|
|
24467
|
+
}
|
|
24468
|
+
/**
|
|
24469
|
+
* Ingest an interpolated text node from the AST into the given `ViewCompilation`.
|
|
24470
|
+
*/
|
|
24471
|
+
function ingestBoundText(unit, text, icuPlaceholder) {
|
|
24472
|
+
let value = text.value;
|
|
24473
|
+
if (value instanceof ASTWithSource) {
|
|
24474
|
+
value = value.ast;
|
|
24475
|
+
}
|
|
24476
|
+
if (!(value instanceof Interpolation$1)) {
|
|
24477
|
+
throw new Error(`AssertionError: expected Interpolation for BoundText node, got ${value.constructor.name}`);
|
|
24478
|
+
}
|
|
24479
|
+
if (text.i18n !== undefined && !(text.i18n instanceof Container)) {
|
|
24480
|
+
throw Error(`Unhandled i18n metadata type for text interpolation: ${text.i18n?.constructor.name}`);
|
|
24481
|
+
}
|
|
24482
|
+
const i18nPlaceholders = text.i18n instanceof Container ?
|
|
24483
|
+
text.i18n.children
|
|
24484
|
+
.filter((node) => node instanceof Placeholder)
|
|
24485
|
+
.map(placeholder => placeholder.name) :
|
|
24486
|
+
[];
|
|
24487
|
+
if (i18nPlaceholders.length > 0 && i18nPlaceholders.length !== value.expressions.length) {
|
|
24488
|
+
throw Error(`Unexpected number of i18n placeholders (${value.expressions.length}) for BoundText with ${value.expressions.length} expressions`);
|
|
24489
|
+
}
|
|
24490
|
+
const textXref = unit.job.allocateXrefId();
|
|
24491
|
+
unit.create.push(createTextOp(textXref, '', icuPlaceholder, text.sourceSpan));
|
|
24492
|
+
// TemplateDefinitionBuilder does not generate source maps for sub-expressions inside an
|
|
24493
|
+
// interpolation. We copy that behavior in compatibility mode.
|
|
24494
|
+
// TODO: is it actually correct to generate these extra maps in modern mode?
|
|
24495
|
+
const baseSourceSpan = unit.job.compatibility ? null : text.sourceSpan;
|
|
24496
|
+
unit.update.push(createInterpolateTextOp(textXref, new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, unit.job, baseSourceSpan)), i18nPlaceholders), text.sourceSpan));
|
|
24497
|
+
}
|
|
24498
|
+
/**
|
|
24499
|
+
* Ingest an `@if` block into the given `ViewCompilation`.
|
|
24500
|
+
*/
|
|
24501
|
+
function ingestIfBlock(unit, ifBlock) {
|
|
24502
|
+
let firstXref = null;
|
|
24503
|
+
let firstSlotHandle = null;
|
|
24504
|
+
let conditions = [];
|
|
24505
|
+
for (let i = 0; i < ifBlock.branches.length; i++) {
|
|
24506
|
+
const ifCase = ifBlock.branches[i];
|
|
24507
|
+
const cView = unit.job.allocateView(unit.xref);
|
|
24508
|
+
let tagName = null;
|
|
24509
|
+
// Only the first branch can be used for projection, because the conditional
|
|
24510
|
+
// uses the container of the first branch as the insertion point for all branches.
|
|
24511
|
+
if (i === 0) {
|
|
24512
|
+
tagName = ingestControlFlowInsertionPoint(unit, cView.xref, ifCase);
|
|
23785
24513
|
}
|
|
23786
|
-
|
|
23787
|
-
|
|
23788
|
-
|
|
23789
|
-
|
|
23790
|
-
|
|
24514
|
+
if (ifCase.expressionAlias !== null) {
|
|
24515
|
+
cView.contextVariables.set(ifCase.expressionAlias.name, CTX_REF);
|
|
24516
|
+
}
|
|
24517
|
+
let ifCaseI18nMeta = undefined;
|
|
24518
|
+
if (ifCase.i18n !== undefined) {
|
|
24519
|
+
if (!(ifCase.i18n instanceof BlockPlaceholder)) {
|
|
24520
|
+
throw Error(`Unhandled i18n metadata type for if block: ${ifCase.i18n?.constructor.name}`);
|
|
23791
24521
|
}
|
|
24522
|
+
ifCaseI18nMeta = ifCase.i18n;
|
|
23792
24523
|
}
|
|
23793
|
-
|
|
23794
|
-
|
|
23795
|
-
|
|
23796
|
-
|
|
23797
|
-
|
|
23798
|
-
* responsible for registering initial styles (within a directive hostBindings' creation block),
|
|
23799
|
-
* as well as any of the provided attribute values, to the directive host element.
|
|
23800
|
-
*/
|
|
23801
|
-
assignHostAttrs(attrs, definitionMap) {
|
|
23802
|
-
if (this._directiveExpr && (attrs.length || this._hasInitialValues)) {
|
|
23803
|
-
this.populateInitialStylingAttrs(attrs);
|
|
23804
|
-
definitionMap.set('hostAttrs', literalArr(attrs));
|
|
24524
|
+
const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, tagName, 'Conditional', Namespace.HTML, ifCaseI18nMeta, ifCase.startSourceSpan, ifCase.sourceSpan);
|
|
24525
|
+
unit.create.push(templateOp);
|
|
24526
|
+
if (firstXref === null) {
|
|
24527
|
+
firstXref = cView.xref;
|
|
24528
|
+
firstSlotHandle = templateOp.handle;
|
|
23805
24529
|
}
|
|
24530
|
+
const caseExpr = ifCase.expression ? convertAst(ifCase.expression, unit.job, null) : null;
|
|
24531
|
+
const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, templateOp.xref, templateOp.handle, ifCase.expressionAlias);
|
|
24532
|
+
conditions.push(conditionalCaseExpr);
|
|
24533
|
+
ingestNodes(cView, ifCase.children);
|
|
23806
24534
|
}
|
|
23807
|
-
|
|
23808
|
-
|
|
23809
|
-
|
|
23810
|
-
|
|
23811
|
-
|
|
23812
|
-
|
|
23813
|
-
|
|
23814
|
-
|
|
23815
|
-
|
|
23816
|
-
|
|
24535
|
+
const conditional = createConditionalOp(firstXref, firstSlotHandle, null, conditions, ifBlock.sourceSpan);
|
|
24536
|
+
unit.update.push(conditional);
|
|
24537
|
+
}
|
|
24538
|
+
/**
|
|
24539
|
+
* Ingest an `@switch` block into the given `ViewCompilation`.
|
|
24540
|
+
*/
|
|
24541
|
+
function ingestSwitchBlock(unit, switchBlock) {
|
|
24542
|
+
// Don't ingest empty switches since they won't render anything.
|
|
24543
|
+
if (switchBlock.cases.length === 0) {
|
|
24544
|
+
return;
|
|
24545
|
+
}
|
|
24546
|
+
let firstXref = null;
|
|
24547
|
+
let firstSlotHandle = null;
|
|
24548
|
+
let conditions = [];
|
|
24549
|
+
for (const switchCase of switchBlock.cases) {
|
|
24550
|
+
const cView = unit.job.allocateView(unit.xref);
|
|
24551
|
+
let switchCaseI18nMeta = undefined;
|
|
24552
|
+
if (switchCase.i18n !== undefined) {
|
|
24553
|
+
if (!(switchCase.i18n instanceof BlockPlaceholder)) {
|
|
24554
|
+
throw Error(`Unhandled i18n metadata type for switch block: ${switchCase.i18n?.constructor.name}`);
|
|
24555
|
+
}
|
|
24556
|
+
switchCaseI18nMeta = switchCase.i18n;
|
|
24557
|
+
}
|
|
24558
|
+
const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, null, 'Case', Namespace.HTML, switchCaseI18nMeta, switchCase.startSourceSpan, switchCase.sourceSpan);
|
|
24559
|
+
unit.create.push(templateOp);
|
|
24560
|
+
if (firstXref === null) {
|
|
24561
|
+
firstXref = cView.xref;
|
|
24562
|
+
firstSlotHandle = templateOp.handle;
|
|
24563
|
+
}
|
|
24564
|
+
const caseExpr = switchCase.expression ?
|
|
24565
|
+
convertAst(switchCase.expression, unit.job, switchBlock.startSourceSpan) :
|
|
24566
|
+
null;
|
|
24567
|
+
const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, templateOp.xref, templateOp.handle);
|
|
24568
|
+
conditions.push(conditionalCaseExpr);
|
|
24569
|
+
ingestNodes(cView, switchCase.children);
|
|
24570
|
+
}
|
|
24571
|
+
const conditional = createConditionalOp(firstXref, firstSlotHandle, convertAst(switchBlock.expression, unit.job, null), conditions, switchBlock.sourceSpan);
|
|
24572
|
+
unit.update.push(conditional);
|
|
24573
|
+
}
|
|
24574
|
+
function ingestDeferView(unit, suffix, i18nMeta, children, sourceSpan) {
|
|
24575
|
+
if (i18nMeta !== undefined && !(i18nMeta instanceof BlockPlaceholder)) {
|
|
24576
|
+
throw Error('Unhandled i18n metadata type for defer block');
|
|
24577
|
+
}
|
|
24578
|
+
if (children === undefined) {
|
|
23817
24579
|
return null;
|
|
23818
24580
|
}
|
|
23819
|
-
|
|
23820
|
-
|
|
23821
|
-
|
|
23822
|
-
|
|
23823
|
-
|
|
23824
|
-
|
|
23825
|
-
|
|
23826
|
-
|
|
23827
|
-
|
|
24581
|
+
const secondaryView = unit.job.allocateView(unit.xref);
|
|
24582
|
+
ingestNodes(secondaryView, children);
|
|
24583
|
+
const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, i18nMeta, sourceSpan, sourceSpan);
|
|
24584
|
+
unit.create.push(templateOp);
|
|
24585
|
+
return templateOp;
|
|
24586
|
+
}
|
|
24587
|
+
function ingestDeferBlock(unit, deferBlock) {
|
|
24588
|
+
const blockMeta = unit.job.deferBlocksMeta.get(deferBlock);
|
|
24589
|
+
if (blockMeta === undefined) {
|
|
24590
|
+
throw new Error(`AssertionError: unable to find metadata for deferred block`);
|
|
24591
|
+
}
|
|
24592
|
+
// Generate the defer main view and all secondary views.
|
|
24593
|
+
const main = ingestDeferView(unit, '', deferBlock.i18n, deferBlock.children, deferBlock.sourceSpan);
|
|
24594
|
+
const loading = ingestDeferView(unit, 'Loading', deferBlock.loading?.i18n, deferBlock.loading?.children, deferBlock.loading?.sourceSpan);
|
|
24595
|
+
const placeholder = ingestDeferView(unit, 'Placeholder', deferBlock.placeholder?.i18n, deferBlock.placeholder?.children, deferBlock.placeholder?.sourceSpan);
|
|
24596
|
+
const error = ingestDeferView(unit, 'Error', deferBlock.error?.i18n, deferBlock.error?.children, deferBlock.error?.sourceSpan);
|
|
24597
|
+
// Create the main defer op, and ops for all secondary views.
|
|
24598
|
+
const deferXref = unit.job.allocateXrefId();
|
|
24599
|
+
const deferOp = createDeferOp(deferXref, main.xref, main.handle, blockMeta, unit.job.allDeferrableDepsFn, deferBlock.sourceSpan);
|
|
24600
|
+
deferOp.placeholderView = placeholder?.xref ?? null;
|
|
24601
|
+
deferOp.placeholderSlot = placeholder?.handle ?? null;
|
|
24602
|
+
deferOp.loadingSlot = loading?.handle ?? null;
|
|
24603
|
+
deferOp.errorSlot = error?.handle ?? null;
|
|
24604
|
+
deferOp.placeholderMinimumTime = deferBlock.placeholder?.minimumTime ?? null;
|
|
24605
|
+
deferOp.loadingMinimumTime = deferBlock.loading?.minimumTime ?? null;
|
|
24606
|
+
deferOp.loadingAfterTime = deferBlock.loading?.afterTime ?? null;
|
|
24607
|
+
unit.create.push(deferOp);
|
|
24608
|
+
// Configure all defer `on` conditions.
|
|
24609
|
+
// TODO: refactor prefetch triggers to use a separate op type, with a shared superclass. This will
|
|
24610
|
+
// make it easier to refactor prefetch behavior in the future.
|
|
24611
|
+
let prefetch = false;
|
|
24612
|
+
let deferOnOps = [];
|
|
24613
|
+
let deferWhenOps = [];
|
|
24614
|
+
for (const triggers of [deferBlock.triggers, deferBlock.prefetchTriggers]) {
|
|
24615
|
+
if (triggers.idle !== undefined) {
|
|
24616
|
+
const deferOnOp = createDeferOnOp(deferXref, { kind: DeferTriggerKind.Idle }, prefetch, triggers.idle.sourceSpan);
|
|
24617
|
+
deferOnOps.push(deferOnOp);
|
|
24618
|
+
}
|
|
24619
|
+
if (triggers.immediate !== undefined) {
|
|
24620
|
+
const deferOnOp = createDeferOnOp(deferXref, { kind: DeferTriggerKind.Immediate }, prefetch, triggers.immediate.sourceSpan);
|
|
24621
|
+
deferOnOps.push(deferOnOp);
|
|
24622
|
+
}
|
|
24623
|
+
if (triggers.timer !== undefined) {
|
|
24624
|
+
const deferOnOp = createDeferOnOp(deferXref, { kind: DeferTriggerKind.Timer, delay: triggers.timer.delay }, prefetch, triggers.timer.sourceSpan);
|
|
24625
|
+
deferOnOps.push(deferOnOp);
|
|
24626
|
+
}
|
|
24627
|
+
if (triggers.hover !== undefined) {
|
|
24628
|
+
const deferOnOp = createDeferOnOp(deferXref, {
|
|
24629
|
+
kind: DeferTriggerKind.Hover,
|
|
24630
|
+
targetName: triggers.hover.reference,
|
|
24631
|
+
targetXref: null,
|
|
24632
|
+
targetSlot: null,
|
|
24633
|
+
targetView: null,
|
|
24634
|
+
targetSlotViewSteps: null,
|
|
24635
|
+
}, prefetch, triggers.hover.sourceSpan);
|
|
24636
|
+
deferOnOps.push(deferOnOp);
|
|
24637
|
+
}
|
|
24638
|
+
if (triggers.interaction !== undefined) {
|
|
24639
|
+
const deferOnOp = createDeferOnOp(deferXref, {
|
|
24640
|
+
kind: DeferTriggerKind.Interaction,
|
|
24641
|
+
targetName: triggers.interaction.reference,
|
|
24642
|
+
targetXref: null,
|
|
24643
|
+
targetSlot: null,
|
|
24644
|
+
targetView: null,
|
|
24645
|
+
targetSlotViewSteps: null,
|
|
24646
|
+
}, prefetch, triggers.interaction.sourceSpan);
|
|
24647
|
+
deferOnOps.push(deferOnOp);
|
|
24648
|
+
}
|
|
24649
|
+
if (triggers.viewport !== undefined) {
|
|
24650
|
+
const deferOnOp = createDeferOnOp(deferXref, {
|
|
24651
|
+
kind: DeferTriggerKind.Viewport,
|
|
24652
|
+
targetName: triggers.viewport.reference,
|
|
24653
|
+
targetXref: null,
|
|
24654
|
+
targetSlot: null,
|
|
24655
|
+
targetView: null,
|
|
24656
|
+
targetSlotViewSteps: null,
|
|
24657
|
+
}, prefetch, triggers.viewport.sourceSpan);
|
|
24658
|
+
deferOnOps.push(deferOnOp);
|
|
24659
|
+
}
|
|
24660
|
+
if (triggers.when !== undefined) {
|
|
24661
|
+
if (triggers.when.value instanceof Interpolation$1) {
|
|
24662
|
+
// TemplateDefinitionBuilder supports this case, but it's very strange to me. What would it
|
|
24663
|
+
// even mean?
|
|
24664
|
+
throw new Error(`Unexpected interpolation in defer block when trigger`);
|
|
24665
|
+
}
|
|
24666
|
+
const deferOnOp = createDeferWhenOp(deferXref, convertAst(triggers.when.value, unit.job, triggers.when.sourceSpan), prefetch, triggers.when.sourceSpan);
|
|
24667
|
+
deferWhenOps.push(deferOnOp);
|
|
24668
|
+
}
|
|
24669
|
+
// If no (non-prefetching) defer triggers were provided, default to `idle`.
|
|
24670
|
+
if (deferOnOps.length === 0 && deferWhenOps.length === 0) {
|
|
24671
|
+
deferOnOps.push(createDeferOnOp(deferXref, { kind: DeferTriggerKind.Idle }, false, null));
|
|
24672
|
+
}
|
|
24673
|
+
prefetch = true;
|
|
24674
|
+
}
|
|
24675
|
+
unit.create.push(deferOnOps);
|
|
24676
|
+
unit.update.push(deferWhenOps);
|
|
24677
|
+
}
|
|
24678
|
+
function ingestIcu(unit, icu) {
|
|
24679
|
+
if (icu.i18n instanceof Message && isSingleI18nIcu(icu.i18n)) {
|
|
24680
|
+
const xref = unit.job.allocateXrefId();
|
|
24681
|
+
unit.create.push(createIcuStartOp(xref, icu.i18n, icuFromI18nMessage(icu.i18n).name, null));
|
|
24682
|
+
for (const [placeholder, text] of Object.entries({ ...icu.vars, ...icu.placeholders })) {
|
|
24683
|
+
if (text instanceof BoundText) {
|
|
24684
|
+
ingestBoundText(unit, text, placeholder);
|
|
24685
|
+
}
|
|
24686
|
+
else {
|
|
24687
|
+
ingestText(unit, text, placeholder);
|
|
24688
|
+
}
|
|
23828
24689
|
}
|
|
23829
|
-
|
|
24690
|
+
unit.create.push(createIcuEndOp(xref));
|
|
23830
24691
|
}
|
|
23831
|
-
|
|
23832
|
-
|
|
23833
|
-
|
|
23834
|
-
|
|
23835
|
-
|
|
23836
|
-
|
|
23837
|
-
|
|
23838
|
-
|
|
23839
|
-
|
|
23840
|
-
|
|
23841
|
-
|
|
23842
|
-
|
|
23843
|
-
|
|
23844
|
-
|
|
23845
|
-
|
|
24692
|
+
else {
|
|
24693
|
+
throw Error(`Unhandled i18n metadata type for ICU: ${icu.i18n?.constructor.name}`);
|
|
24694
|
+
}
|
|
24695
|
+
}
|
|
24696
|
+
/**
|
|
24697
|
+
* Ingest an `@for` block into the given `ViewCompilation`.
|
|
24698
|
+
*/
|
|
24699
|
+
function ingestForBlock(unit, forBlock) {
|
|
24700
|
+
const repeaterView = unit.job.allocateView(unit.xref);
|
|
24701
|
+
// Set all the context variables and aliases available in the repeater.
|
|
24702
|
+
repeaterView.contextVariables.set(forBlock.item.name, forBlock.item.value);
|
|
24703
|
+
repeaterView.contextVariables.set(forBlock.contextVariables.$index.name, forBlock.contextVariables.$index.value);
|
|
24704
|
+
repeaterView.contextVariables.set(forBlock.contextVariables.$count.name, forBlock.contextVariables.$count.value);
|
|
24705
|
+
// We copy TemplateDefinitionBuilder's scheme of creating names for `$count` and `$index` that are
|
|
24706
|
+
// suffixed with special information, to disambiguate which level of nested loop the below aliases
|
|
24707
|
+
// refer to.
|
|
24708
|
+
// TODO: We should refactor Template Pipeline's variable phases to gracefully handle shadowing,
|
|
24709
|
+
// and arbitrarily many levels of variables depending on each other.
|
|
24710
|
+
const indexName = `ɵ${forBlock.contextVariables.$index.name}_${repeaterView.xref}`;
|
|
24711
|
+
const countName = `ɵ${forBlock.contextVariables.$count.name}_${repeaterView.xref}`;
|
|
24712
|
+
repeaterView.contextVariables.set(indexName, forBlock.contextVariables.$index.value);
|
|
24713
|
+
repeaterView.contextVariables.set(countName, forBlock.contextVariables.$count.value);
|
|
24714
|
+
repeaterView.aliases.add({
|
|
24715
|
+
kind: SemanticVariableKind.Alias,
|
|
24716
|
+
name: null,
|
|
24717
|
+
identifier: forBlock.contextVariables.$first.name,
|
|
24718
|
+
expression: new LexicalReadExpr(indexName).identical(literal(0))
|
|
24719
|
+
});
|
|
24720
|
+
repeaterView.aliases.add({
|
|
24721
|
+
kind: SemanticVariableKind.Alias,
|
|
24722
|
+
name: null,
|
|
24723
|
+
identifier: forBlock.contextVariables.$last.name,
|
|
24724
|
+
expression: new LexicalReadExpr(indexName).identical(new LexicalReadExpr(countName).minus(literal(1)))
|
|
24725
|
+
});
|
|
24726
|
+
repeaterView.aliases.add({
|
|
24727
|
+
kind: SemanticVariableKind.Alias,
|
|
24728
|
+
name: null,
|
|
24729
|
+
identifier: forBlock.contextVariables.$even.name,
|
|
24730
|
+
expression: new LexicalReadExpr(indexName).modulo(literal(2)).identical(literal(0))
|
|
24731
|
+
});
|
|
24732
|
+
repeaterView.aliases.add({
|
|
24733
|
+
kind: SemanticVariableKind.Alias,
|
|
24734
|
+
name: null,
|
|
24735
|
+
identifier: forBlock.contextVariables.$odd.name,
|
|
24736
|
+
expression: new LexicalReadExpr(indexName).modulo(literal(2)).notIdentical(literal(0))
|
|
24737
|
+
});
|
|
24738
|
+
const sourceSpan = convertSourceSpan(forBlock.trackBy.span, forBlock.sourceSpan);
|
|
24739
|
+
const track = convertAst(forBlock.trackBy, unit.job, sourceSpan);
|
|
24740
|
+
ingestNodes(repeaterView, forBlock.children);
|
|
24741
|
+
let emptyView = null;
|
|
24742
|
+
let emptyTagName = null;
|
|
24743
|
+
if (forBlock.empty !== null) {
|
|
24744
|
+
emptyView = unit.job.allocateView(unit.xref);
|
|
24745
|
+
ingestNodes(emptyView, forBlock.empty.children);
|
|
24746
|
+
emptyTagName = ingestControlFlowInsertionPoint(unit, emptyView.xref, forBlock.empty);
|
|
24747
|
+
}
|
|
24748
|
+
const varNames = {
|
|
24749
|
+
$index: forBlock.contextVariables.$index.name,
|
|
24750
|
+
$count: forBlock.contextVariables.$count.name,
|
|
24751
|
+
$first: forBlock.contextVariables.$first.name,
|
|
24752
|
+
$last: forBlock.contextVariables.$last.name,
|
|
24753
|
+
$even: forBlock.contextVariables.$even.name,
|
|
24754
|
+
$odd: forBlock.contextVariables.$odd.name,
|
|
24755
|
+
$implicit: forBlock.item.name,
|
|
24756
|
+
};
|
|
24757
|
+
if (forBlock.i18n !== undefined && !(forBlock.i18n instanceof BlockPlaceholder)) {
|
|
24758
|
+
throw Error('AssertionError: Unhandled i18n metadata type or @for');
|
|
24759
|
+
}
|
|
24760
|
+
if (forBlock.empty?.i18n !== undefined &&
|
|
24761
|
+
!(forBlock.empty.i18n instanceof BlockPlaceholder)) {
|
|
24762
|
+
throw Error('AssertionError: Unhandled i18n metadata type or @empty');
|
|
24763
|
+
}
|
|
24764
|
+
const i18nPlaceholder = forBlock.i18n;
|
|
24765
|
+
const emptyI18nPlaceholder = forBlock.empty?.i18n;
|
|
24766
|
+
const tagName = ingestControlFlowInsertionPoint(unit, repeaterView.xref, forBlock);
|
|
24767
|
+
const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, emptyTagName, i18nPlaceholder, emptyI18nPlaceholder, forBlock.startSourceSpan, forBlock.sourceSpan);
|
|
24768
|
+
unit.create.push(repeaterCreate);
|
|
24769
|
+
const expression = convertAst(forBlock.expression, unit.job, convertSourceSpan(forBlock.expression.span, forBlock.sourceSpan));
|
|
24770
|
+
const repeater = createRepeaterOp(repeaterCreate.xref, repeaterCreate.handle, expression, forBlock.sourceSpan);
|
|
24771
|
+
unit.update.push(repeater);
|
|
24772
|
+
}
|
|
24773
|
+
/**
|
|
24774
|
+
* Convert a template AST expression into an output AST expression.
|
|
24775
|
+
*/
|
|
24776
|
+
function convertAst(ast, job, baseSourceSpan) {
|
|
24777
|
+
if (ast instanceof ASTWithSource) {
|
|
24778
|
+
return convertAst(ast.ast, job, baseSourceSpan);
|
|
24779
|
+
}
|
|
24780
|
+
else if (ast instanceof PropertyRead) {
|
|
24781
|
+
const isThisReceiver = ast.receiver instanceof ThisReceiver;
|
|
24782
|
+
// Whether this is an implicit receiver, *excluding* explicit reads of `this`.
|
|
24783
|
+
const isImplicitReceiver = ast.receiver instanceof ImplicitReceiver && !(ast.receiver instanceof ThisReceiver);
|
|
24784
|
+
// Whether the name of the read is a node that should be never retain its explicit this
|
|
24785
|
+
// receiver.
|
|
24786
|
+
const isSpecialNode = ast.name === '$any' || ast.name === '$event';
|
|
24787
|
+
// TODO: The most sensible condition here would be simply `isImplicitReceiver`, to convert only
|
|
24788
|
+
// actual implicit `this` reads, and not explicit ones. However, TemplateDefinitionBuilder (and
|
|
24789
|
+
// the Typecheck block!) both have the same bug, in which they also consider explicit `this`
|
|
24790
|
+
// reads to be implicit. This causes problems when the explicit `this` read is inside a
|
|
24791
|
+
// template with a context that also provides the variable name being read:
|
|
24792
|
+
// ```
|
|
24793
|
+
// <ng-template let-a>{{this.a}}</ng-template>
|
|
24794
|
+
// ```
|
|
24795
|
+
// The whole point of the explicit `this` was to access the class property, but TDB and the
|
|
24796
|
+
// current TCB treat the read as implicit, and give you the context property instead!
|
|
24797
|
+
//
|
|
24798
|
+
// For now, we emulate this old behvaior by aggressively converting explicit reads to to
|
|
24799
|
+
// implicit reads, except for the special cases that TDB and the current TCB protect. However,
|
|
24800
|
+
// it would be an improvement to fix this.
|
|
24801
|
+
//
|
|
24802
|
+
// See also the corresponding comment for the TCB, in `type_check_block.ts`.
|
|
24803
|
+
if (isImplicitReceiver || (isThisReceiver && !isSpecialNode)) {
|
|
24804
|
+
return new LexicalReadExpr(ast.name);
|
|
23846
24805
|
}
|
|
23847
24806
|
else {
|
|
23848
|
-
|
|
24807
|
+
return new ReadPropExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.name, null, convertSourceSpan(ast.span, baseSourceSpan));
|
|
23849
24808
|
}
|
|
23850
|
-
return {
|
|
23851
|
-
reference,
|
|
23852
|
-
calls: [{
|
|
23853
|
-
supportsInterpolation: true,
|
|
23854
|
-
sourceSpan: stylingInput.sourceSpan,
|
|
23855
|
-
allocateBindingSlots: totalBindingSlotsRequired,
|
|
23856
|
-
params: (convertFn) => {
|
|
23857
|
-
const convertResult = convertFn(mapValue);
|
|
23858
|
-
const params = Array.isArray(convertResult) ? convertResult : [convertResult];
|
|
23859
|
-
return params;
|
|
23860
|
-
}
|
|
23861
|
-
}]
|
|
23862
|
-
};
|
|
23863
24809
|
}
|
|
23864
|
-
|
|
23865
|
-
|
|
23866
|
-
|
|
23867
|
-
|
|
23868
|
-
|
|
23869
|
-
|
|
23870
|
-
|
|
23871
|
-
|
|
23872
|
-
|
|
23873
|
-
|
|
23874
|
-
|
|
23875
|
-
|
|
23876
|
-
|
|
23877
|
-
|
|
23878
|
-
|
|
23879
|
-
|
|
23880
|
-
|
|
23881
|
-
|
|
23882
|
-
|
|
23883
|
-
|
|
23884
|
-
|
|
23885
|
-
|
|
23886
|
-
|
|
23887
|
-
|
|
23888
|
-
|
|
23889
|
-
|
|
23890
|
-
|
|
23891
|
-
|
|
23892
|
-
|
|
23893
|
-
|
|
23894
|
-
|
|
23895
|
-
|
|
23896
|
-
|
|
23897
|
-
|
|
23898
|
-
|
|
23899
|
-
|
|
23900
|
-
|
|
23901
|
-
|
|
23902
|
-
|
|
23903
|
-
|
|
23904
|
-
|
|
23905
|
-
|
|
23906
|
-
|
|
23907
|
-
|
|
23908
|
-
|
|
23909
|
-
|
|
23910
|
-
|
|
23911
|
-
|
|
23912
|
-
|
|
23913
|
-
|
|
23914
|
-
|
|
23915
|
-
|
|
23916
|
-
|
|
24810
|
+
else if (ast instanceof PropertyWrite) {
|
|
24811
|
+
if (ast.receiver instanceof ImplicitReceiver) {
|
|
24812
|
+
return new WritePropExpr(
|
|
24813
|
+
// TODO: Is it correct to always use the root context in place of the implicit receiver?
|
|
24814
|
+
new ContextExpr(job.root.xref), ast.name, convertAst(ast.value, job, baseSourceSpan), null, convertSourceSpan(ast.span, baseSourceSpan));
|
|
24815
|
+
}
|
|
24816
|
+
return new WritePropExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.name, convertAst(ast.value, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
24817
|
+
}
|
|
24818
|
+
else if (ast instanceof KeyedWrite) {
|
|
24819
|
+
return new WriteKeyExpr(convertAst(ast.receiver, job, baseSourceSpan), convertAst(ast.key, job, baseSourceSpan), convertAst(ast.value, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
24820
|
+
}
|
|
24821
|
+
else if (ast instanceof Call) {
|
|
24822
|
+
if (ast.receiver instanceof ImplicitReceiver) {
|
|
24823
|
+
throw new Error(`Unexpected ImplicitReceiver`);
|
|
24824
|
+
}
|
|
24825
|
+
else {
|
|
24826
|
+
return new InvokeFunctionExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.args.map(arg => convertAst(arg, job, baseSourceSpan)), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
24827
|
+
}
|
|
24828
|
+
}
|
|
24829
|
+
else if (ast instanceof LiteralPrimitive) {
|
|
24830
|
+
return literal(ast.value, undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
24831
|
+
}
|
|
24832
|
+
else if (ast instanceof Unary) {
|
|
24833
|
+
switch (ast.operator) {
|
|
24834
|
+
case '+':
|
|
24835
|
+
return new UnaryOperatorExpr(exports.UnaryOperator.Plus, convertAst(ast.expr, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
24836
|
+
case '-':
|
|
24837
|
+
return new UnaryOperatorExpr(exports.UnaryOperator.Minus, convertAst(ast.expr, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
24838
|
+
default:
|
|
24839
|
+
throw new Error(`AssertionError: unknown unary operator ${ast.operator}`);
|
|
24840
|
+
}
|
|
24841
|
+
}
|
|
24842
|
+
else if (ast instanceof Binary) {
|
|
24843
|
+
const operator = BINARY_OPERATORS.get(ast.operation);
|
|
24844
|
+
if (operator === undefined) {
|
|
24845
|
+
throw new Error(`AssertionError: unknown binary operator ${ast.operation}`);
|
|
24846
|
+
}
|
|
24847
|
+
return new BinaryOperatorExpr(operator, convertAst(ast.left, job, baseSourceSpan), convertAst(ast.right, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
24848
|
+
}
|
|
24849
|
+
else if (ast instanceof ThisReceiver) {
|
|
24850
|
+
// TODO: should context expressions have source maps?
|
|
24851
|
+
return new ContextExpr(job.root.xref);
|
|
24852
|
+
}
|
|
24853
|
+
else if (ast instanceof KeyedRead) {
|
|
24854
|
+
return new ReadKeyExpr(convertAst(ast.receiver, job, baseSourceSpan), convertAst(ast.key, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
24855
|
+
}
|
|
24856
|
+
else if (ast instanceof Chain) {
|
|
24857
|
+
throw new Error(`AssertionError: Chain in unknown context`);
|
|
24858
|
+
}
|
|
24859
|
+
else if (ast instanceof LiteralMap) {
|
|
24860
|
+
const entries = ast.keys.map((key, idx) => {
|
|
24861
|
+
const value = ast.values[idx];
|
|
24862
|
+
// TODO: should literals have source maps, or do we just map the whole surrounding
|
|
24863
|
+
// expression?
|
|
24864
|
+
return new LiteralMapEntry(key.key, convertAst(value, job, baseSourceSpan), key.quoted);
|
|
23917
24865
|
});
|
|
23918
|
-
return
|
|
24866
|
+
return new LiteralMapExpr(entries, undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
23919
24867
|
}
|
|
23920
|
-
|
|
23921
|
-
|
|
23922
|
-
|
|
24868
|
+
else if (ast instanceof LiteralArray) {
|
|
24869
|
+
// TODO: should literals have source maps, or do we just map the whole surrounding expression?
|
|
24870
|
+
return new LiteralArrayExpr(ast.expressions.map(expr => convertAst(expr, job, baseSourceSpan)));
|
|
24871
|
+
}
|
|
24872
|
+
else if (ast instanceof Conditional) {
|
|
24873
|
+
return new ConditionalExpr(convertAst(ast.condition, job, baseSourceSpan), convertAst(ast.trueExp, job, baseSourceSpan), convertAst(ast.falseExp, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
24874
|
+
}
|
|
24875
|
+
else if (ast instanceof NonNullAssert) {
|
|
24876
|
+
// A non-null assertion shouldn't impact generated instructions, so we can just drop it.
|
|
24877
|
+
return convertAst(ast.expression, job, baseSourceSpan);
|
|
24878
|
+
}
|
|
24879
|
+
else if (ast instanceof BindingPipe) {
|
|
24880
|
+
// TODO: pipes should probably have source maps; figure out details.
|
|
24881
|
+
return new PipeBindingExpr(job.allocateXrefId(), new SlotHandle(), ast.name, [
|
|
24882
|
+
convertAst(ast.exp, job, baseSourceSpan),
|
|
24883
|
+
...ast.args.map(arg => convertAst(arg, job, baseSourceSpan)),
|
|
24884
|
+
]);
|
|
24885
|
+
}
|
|
24886
|
+
else if (ast instanceof SafeKeyedRead) {
|
|
24887
|
+
return new SafeKeyedReadExpr(convertAst(ast.receiver, job, baseSourceSpan), convertAst(ast.key, job, baseSourceSpan), convertSourceSpan(ast.span, baseSourceSpan));
|
|
24888
|
+
}
|
|
24889
|
+
else if (ast instanceof SafePropertyRead) {
|
|
24890
|
+
// TODO: source span
|
|
24891
|
+
return new SafePropertyReadExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.name);
|
|
24892
|
+
}
|
|
24893
|
+
else if (ast instanceof SafeCall) {
|
|
24894
|
+
// TODO: source span
|
|
24895
|
+
return new SafeInvokeFunctionExpr(convertAst(ast.receiver, job, baseSourceSpan), ast.args.map(a => convertAst(a, job, baseSourceSpan)));
|
|
24896
|
+
}
|
|
24897
|
+
else if (ast instanceof EmptyExpr$1) {
|
|
24898
|
+
return new EmptyExpr(convertSourceSpan(ast.span, baseSourceSpan));
|
|
24899
|
+
}
|
|
24900
|
+
else if (ast instanceof PrefixNot) {
|
|
24901
|
+
return not(convertAst(ast.expression, job, baseSourceSpan), convertSourceSpan(ast.span, baseSourceSpan));
|
|
24902
|
+
}
|
|
24903
|
+
else {
|
|
24904
|
+
throw new Error(`Unhandled expression type "${ast.constructor.name}" in file "${baseSourceSpan?.start.file.url}"`);
|
|
24905
|
+
}
|
|
24906
|
+
}
|
|
24907
|
+
function convertAstWithInterpolation(job, value, i18nMeta, sourceSpan) {
|
|
24908
|
+
let expression;
|
|
24909
|
+
if (value instanceof Interpolation$1) {
|
|
24910
|
+
expression = new Interpolation(value.strings, value.expressions.map(e => convertAst(e, job, sourceSpan ?? null)), Object.keys(asMessage(i18nMeta)?.placeholders ?? {}));
|
|
24911
|
+
}
|
|
24912
|
+
else if (value instanceof AST) {
|
|
24913
|
+
expression = convertAst(value, job, sourceSpan ?? null);
|
|
24914
|
+
}
|
|
24915
|
+
else {
|
|
24916
|
+
expression = literal(value);
|
|
24917
|
+
}
|
|
24918
|
+
return expression;
|
|
24919
|
+
}
|
|
24920
|
+
// TODO: Can we populate Template binding kinds in ingest?
|
|
24921
|
+
const BINDING_KINDS = new Map([
|
|
24922
|
+
[0 /* e.BindingType.Property */, BindingKind.Property],
|
|
24923
|
+
[5 /* e.BindingType.TwoWay */, BindingKind.TwoWayProperty],
|
|
24924
|
+
[1 /* e.BindingType.Attribute */, BindingKind.Attribute],
|
|
24925
|
+
[2 /* e.BindingType.Class */, BindingKind.ClassName],
|
|
24926
|
+
[3 /* e.BindingType.Style */, BindingKind.StyleProperty],
|
|
24927
|
+
[4 /* e.BindingType.Animation */, BindingKind.Animation],
|
|
24928
|
+
]);
|
|
24929
|
+
/**
|
|
24930
|
+
* Checks whether the given template is a plain ng-template (as opposed to another kind of template
|
|
24931
|
+
* such as a structural directive template or control flow template). This is checked based on the
|
|
24932
|
+
* tagName. We can expect that only plain ng-templates will come through with a tagName of
|
|
24933
|
+
* 'ng-template'.
|
|
24934
|
+
*
|
|
24935
|
+
* Here are some of the cases we expect:
|
|
24936
|
+
*
|
|
24937
|
+
* | Angular HTML | Template tagName |
|
|
24938
|
+
* | ---------------------------------- | ------------------ |
|
|
24939
|
+
* | `<ng-template>` | 'ng-template' |
|
|
24940
|
+
* | `<div *ngIf="true">` | 'div' |
|
|
24941
|
+
* | `<svg><ng-template>` | 'svg:ng-template' |
|
|
24942
|
+
* | `@if (true) {` | 'Conditional' |
|
|
24943
|
+
* | `<ng-template *ngIf>` (plain) | 'ng-template' |
|
|
24944
|
+
* | `<ng-template *ngIf>` (structural) | null |
|
|
24945
|
+
*/
|
|
24946
|
+
function isPlainTemplate(tmpl) {
|
|
24947
|
+
return splitNsName(tmpl.tagName ?? '')[1] === NG_TEMPLATE_TAG_NAME$1;
|
|
24948
|
+
}
|
|
24949
|
+
/**
|
|
24950
|
+
* Ensures that the i18nMeta, if provided, is an i18n.Message.
|
|
24951
|
+
*/
|
|
24952
|
+
function asMessage(i18nMeta) {
|
|
24953
|
+
if (i18nMeta == null) {
|
|
24954
|
+
return null;
|
|
24955
|
+
}
|
|
24956
|
+
if (!(i18nMeta instanceof Message)) {
|
|
24957
|
+
throw Error(`Expected i18n meta to be a Message, but got: ${i18nMeta.constructor.name}`);
|
|
24958
|
+
}
|
|
24959
|
+
return i18nMeta;
|
|
24960
|
+
}
|
|
24961
|
+
/**
|
|
24962
|
+
* Process all of the bindings on an element in the template AST and convert them to their IR
|
|
24963
|
+
* representation.
|
|
24964
|
+
*/
|
|
24965
|
+
function ingestElementBindings(unit, op, element) {
|
|
24966
|
+
let bindings = new Array();
|
|
24967
|
+
let i18nAttributeBindingNames = new Set();
|
|
24968
|
+
for (const attr of element.attributes) {
|
|
24969
|
+
// Attribute literal bindings, such as `attr.foo="bar"`.
|
|
24970
|
+
const securityContext = domSchema.securityContext(element.name, attr.name, true);
|
|
24971
|
+
bindings.push(createBindingOp(op.xref, BindingKind.Attribute, attr.name, convertAstWithInterpolation(unit.job, attr.value, attr.i18n), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
|
|
24972
|
+
if (attr.i18n) {
|
|
24973
|
+
i18nAttributeBindingNames.add(attr.name);
|
|
23923
24974
|
}
|
|
23924
|
-
return [];
|
|
23925
24975
|
}
|
|
23926
|
-
|
|
23927
|
-
if (
|
|
23928
|
-
|
|
24976
|
+
for (const input of element.inputs) {
|
|
24977
|
+
if (i18nAttributeBindingNames.has(input.name)) {
|
|
24978
|
+
console.error(`On component ${unit.job.componentName}, the binding ${input
|
|
24979
|
+
.name} is both an i18n attribute and a property. You may want to remove the property binding. This will become a compilation error in future versions of Angular.`);
|
|
23929
24980
|
}
|
|
23930
|
-
|
|
24981
|
+
// All dynamic bindings (both attribute and property bindings).
|
|
24982
|
+
bindings.push(createBindingOp(op.xref, BINDING_KINDS.get(input.type), input.name, convertAstWithInterpolation(unit.job, astOf(input.value), input.i18n), input.unit, input.securityContext, false, false, null, asMessage(input.i18n) ?? null, input.sourceSpan));
|
|
23931
24983
|
}
|
|
23932
|
-
|
|
23933
|
-
|
|
23934
|
-
|
|
23935
|
-
|
|
23936
|
-
|
|
23937
|
-
|
|
23938
|
-
if (
|
|
23939
|
-
|
|
23940
|
-
|
|
23941
|
-
|
|
24984
|
+
unit.create.push(bindings.filter((b) => b?.kind === OpKind.ExtractedAttribute));
|
|
24985
|
+
unit.update.push(bindings.filter((b) => b?.kind === OpKind.Binding));
|
|
24986
|
+
for (const output of element.outputs) {
|
|
24987
|
+
if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) {
|
|
24988
|
+
throw Error('Animation listener should have a phase');
|
|
24989
|
+
}
|
|
24990
|
+
if (output.type === 2 /* e.ParsedEventType.TwoWay */) {
|
|
24991
|
+
unit.create.push(createTwoWayListenerOp(op.xref, op.handle, output.name, op.tag, makeTwoWayListenerHandlerOps(unit, output.handler, output.handlerSpan), output.sourceSpan));
|
|
24992
|
+
}
|
|
24993
|
+
else {
|
|
24994
|
+
unit.create.push(createListenerOp(op.xref, op.handle, output.name, op.tag, makeListenerHandlerOps(unit, output.handler, output.handlerSpan), output.phase, output.target, false, output.sourceSpan));
|
|
24995
|
+
}
|
|
24996
|
+
}
|
|
24997
|
+
// If any of the bindings on this element have an i18n message, then an i18n attrs configuration
|
|
24998
|
+
// op is also required.
|
|
24999
|
+
if (bindings.some(b => b?.i18nMessage) !== null) {
|
|
25000
|
+
unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
|
|
25001
|
+
}
|
|
25002
|
+
}
|
|
25003
|
+
/**
|
|
25004
|
+
* Process all of the bindings on a template in the template AST and convert them to their IR
|
|
25005
|
+
* representation.
|
|
25006
|
+
*/
|
|
25007
|
+
function ingestTemplateBindings(unit, op, template, templateKind) {
|
|
25008
|
+
let bindings = new Array();
|
|
25009
|
+
for (const attr of template.templateAttrs) {
|
|
25010
|
+
if (attr instanceof TextAttribute) {
|
|
25011
|
+
const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
|
|
25012
|
+
bindings.push(createTemplateBinding(unit, op.xref, 1 /* e.BindingType.Attribute */, attr.name, attr.value, null, securityContext, true, templateKind, asMessage(attr.i18n), attr.sourceSpan));
|
|
25013
|
+
}
|
|
25014
|
+
else {
|
|
25015
|
+
bindings.push(createTemplateBinding(unit, op.xref, attr.type, attr.name, astOf(attr.value), attr.unit, attr.securityContext, true, templateKind, asMessage(attr.i18n), attr.sourceSpan));
|
|
25016
|
+
}
|
|
25017
|
+
}
|
|
25018
|
+
for (const attr of template.attributes) {
|
|
25019
|
+
// Attribute literal bindings, such as `attr.foo="bar"`.
|
|
25020
|
+
const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
|
|
25021
|
+
bindings.push(createTemplateBinding(unit, op.xref, 1 /* e.BindingType.Attribute */, attr.name, attr.value, null, securityContext, false, templateKind, asMessage(attr.i18n), attr.sourceSpan));
|
|
25022
|
+
}
|
|
25023
|
+
for (const input of template.inputs) {
|
|
25024
|
+
// Dynamic bindings (both attribute and property bindings).
|
|
25025
|
+
bindings.push(createTemplateBinding(unit, op.xref, input.type, input.name, astOf(input.value), input.unit, input.securityContext, false, templateKind, asMessage(input.i18n), input.sourceSpan));
|
|
25026
|
+
}
|
|
25027
|
+
unit.create.push(bindings.filter((b) => b?.kind === OpKind.ExtractedAttribute));
|
|
25028
|
+
unit.update.push(bindings.filter((b) => b?.kind === OpKind.Binding));
|
|
25029
|
+
for (const output of template.outputs) {
|
|
25030
|
+
if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) {
|
|
25031
|
+
throw Error('Animation listener should have a phase');
|
|
25032
|
+
}
|
|
25033
|
+
if (templateKind === TemplateKind.NgTemplate) {
|
|
25034
|
+
if (output.type === 2 /* e.ParsedEventType.TwoWay */) {
|
|
25035
|
+
unit.create.push(createTwoWayListenerOp(op.xref, op.handle, output.name, op.tag, makeTwoWayListenerHandlerOps(unit, output.handler, output.handlerSpan), output.sourceSpan));
|
|
23942
25036
|
}
|
|
23943
|
-
|
|
23944
|
-
|
|
23945
|
-
instructions.push(classMapInstruction);
|
|
25037
|
+
else {
|
|
25038
|
+
unit.create.push(createListenerOp(op.xref, op.handle, output.name, op.tag, makeListenerHandlerOps(unit, output.handler, output.handlerSpan), output.phase, output.target, false, output.sourceSpan));
|
|
23946
25039
|
}
|
|
23947
|
-
instructions.push(...this._buildStyleInputs(valueConverter));
|
|
23948
|
-
instructions.push(...this._buildClassInputs(valueConverter));
|
|
23949
25040
|
}
|
|
23950
|
-
|
|
25041
|
+
if (templateKind === TemplateKind.Structural &&
|
|
25042
|
+
output.type !== 1 /* e.ParsedEventType.Animation */) {
|
|
25043
|
+
// Animation bindings are excluded from the structural template's const array.
|
|
25044
|
+
const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, output.name, false);
|
|
25045
|
+
unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, null, output.name, null, null, null, securityContext));
|
|
25046
|
+
}
|
|
23951
25047
|
}
|
|
23952
|
-
|
|
23953
|
-
|
|
23954
|
-
|
|
23955
|
-
map.set(key, map.size);
|
|
25048
|
+
// TODO: Perhaps we could do this in a phase? (It likely wouldn't change the slot indices.)
|
|
25049
|
+
if (bindings.some(b => b?.i18nMessage) !== null) {
|
|
25050
|
+
unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
|
|
23956
25051
|
}
|
|
23957
25052
|
}
|
|
23958
|
-
|
|
23959
|
-
|
|
23960
|
-
|
|
23961
|
-
|
|
23962
|
-
|
|
23963
|
-
|
|
25053
|
+
/**
|
|
25054
|
+
* Helper to ingest an individual binding on a template, either an explicit `ng-template`, or an
|
|
25055
|
+
* implicit template created via structural directive.
|
|
25056
|
+
*
|
|
25057
|
+
* Bindings on templates are *extremely* tricky. I have tried to isolate all of the confusing edge
|
|
25058
|
+
* cases into this function, and to comment it well to document the behavior.
|
|
25059
|
+
*
|
|
25060
|
+
* Some of this behavior is intuitively incorrect, and we should consider changing it in the future.
|
|
25061
|
+
*
|
|
25062
|
+
* @param view The compilation unit for the view containing the template.
|
|
25063
|
+
* @param xref The xref of the template op.
|
|
25064
|
+
* @param type The binding type, according to the parser. This is fairly reasonable, e.g. both
|
|
25065
|
+
* dynamic and static attributes have e.BindingType.Attribute.
|
|
25066
|
+
* @param name The binding's name.
|
|
25067
|
+
* @param value The bindings's value, which will either be an input AST expression, or a string
|
|
25068
|
+
* literal. Note that the input AST expression may or may not be const -- it will only be a
|
|
25069
|
+
* string literal if the parser considered it a text binding.
|
|
25070
|
+
* @param unit If the binding has a unit (e.g. `px` for style bindings), then this is the unit.
|
|
25071
|
+
* @param securityContext The security context of the binding.
|
|
25072
|
+
* @param isStructuralTemplateAttribute Whether this binding actually applies to the structural
|
|
25073
|
+
* ng-template. For example, an `ngFor` would actually apply to the structural template. (Most
|
|
25074
|
+
* bindings on structural elements target the inner element, not the template.)
|
|
25075
|
+
* @param templateKind Whether this is an explicit `ng-template` or an implicit template created by
|
|
25076
|
+
* a structural directive. This should never be a block template.
|
|
25077
|
+
* @param i18nMessage The i18n metadata for the binding, if any.
|
|
25078
|
+
* @param sourceSpan The source span of the binding.
|
|
25079
|
+
* @returns An IR binding op, or null if the binding should be skipped.
|
|
25080
|
+
*/
|
|
25081
|
+
function createTemplateBinding(view, xref, type, name, value, unit, securityContext, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
|
|
25082
|
+
const isTextBinding = typeof value === 'string';
|
|
25083
|
+
// If this is a structural template, then several kinds of bindings should not result in an
|
|
25084
|
+
// update instruction.
|
|
25085
|
+
if (templateKind === TemplateKind.Structural) {
|
|
25086
|
+
if (!isStructuralTemplateAttribute) {
|
|
25087
|
+
switch (type) {
|
|
25088
|
+
case 0 /* e.BindingType.Property */:
|
|
25089
|
+
case 2 /* e.BindingType.Class */:
|
|
25090
|
+
case 3 /* e.BindingType.Style */:
|
|
25091
|
+
// Because this binding doesn't really target the ng-template, it must be a binding on an
|
|
25092
|
+
// inner node of a structural template. We can't skip it entirely, because we still need
|
|
25093
|
+
// it on the ng-template's consts (e.g. for the purposes of directive matching). However,
|
|
25094
|
+
// we should not generate an update instruction for it.
|
|
25095
|
+
return createExtractedAttributeOp(xref, BindingKind.Property, null, name, null, null, i18nMessage, securityContext);
|
|
25096
|
+
case 5 /* e.BindingType.TwoWay */:
|
|
25097
|
+
return createExtractedAttributeOp(xref, BindingKind.TwoWayProperty, null, name, null, null, i18nMessage, securityContext);
|
|
25098
|
+
}
|
|
25099
|
+
}
|
|
25100
|
+
if (!isTextBinding && (type === 1 /* e.BindingType.Attribute */ || type === 4 /* e.BindingType.Animation */)) {
|
|
25101
|
+
// Again, this binding doesn't really target the ng-template; it actually targets the element
|
|
25102
|
+
// inside the structural template. In the case of non-text attribute or animation bindings,
|
|
25103
|
+
// the binding doesn't even show up on the ng-template const array, so we just skip it
|
|
25104
|
+
// entirely.
|
|
25105
|
+
return null;
|
|
25106
|
+
}
|
|
23964
25107
|
}
|
|
23965
|
-
let
|
|
23966
|
-
|
|
23967
|
-
|
|
23968
|
-
|
|
23969
|
-
|
|
23970
|
-
|
|
25108
|
+
let bindingType = BINDING_KINDS.get(type);
|
|
25109
|
+
if (templateKind === TemplateKind.NgTemplate) {
|
|
25110
|
+
// We know we are dealing with bindings directly on an explicit ng-template.
|
|
25111
|
+
// Static attribute bindings should be collected into the const array as k/v pairs. Property
|
|
25112
|
+
// bindings should result in a `property` instruction, and `AttributeMarker.Bindings` const
|
|
25113
|
+
// entries.
|
|
25114
|
+
//
|
|
25115
|
+
// The difficulty is with dynamic attribute, style, and class bindings. These don't really make
|
|
25116
|
+
// sense on an `ng-template` and should probably be parser errors. However,
|
|
25117
|
+
// TemplateDefinitionBuilder generates `property` instructions for them, and so we do that as
|
|
25118
|
+
// well.
|
|
25119
|
+
//
|
|
25120
|
+
// Note that we do have a slight behavior difference with TemplateDefinitionBuilder: although
|
|
25121
|
+
// TDB emits `property` instructions for dynamic attributes, styles, and classes, only styles
|
|
25122
|
+
// and classes also get const collected into the `AttributeMarker.Bindings` field. Dynamic
|
|
25123
|
+
// attribute bindings are missing from the consts entirely. We choose to emit them into the
|
|
25124
|
+
// consts field anyway, to avoid creating special cases for something so arcane and nonsensical.
|
|
25125
|
+
if (type === 2 /* e.BindingType.Class */ || type === 3 /* e.BindingType.Style */ ||
|
|
25126
|
+
(type === 1 /* e.BindingType.Attribute */ && !isTextBinding)) {
|
|
25127
|
+
// TODO: These cases should be parse errors.
|
|
25128
|
+
bindingType = BindingKind.Property;
|
|
25129
|
+
}
|
|
25130
|
+
}
|
|
25131
|
+
return createBindingOp(xref, bindingType, name, convertAstWithInterpolation(view.job, value, i18nMessage), unit, securityContext, isTextBinding, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan);
|
|
25132
|
+
}
|
|
25133
|
+
function makeListenerHandlerOps(unit, handler, handlerSpan) {
|
|
25134
|
+
handler = astOf(handler);
|
|
25135
|
+
const handlerOps = new Array();
|
|
25136
|
+
let handlerExprs = handler instanceof Chain ? handler.expressions : [handler];
|
|
25137
|
+
if (handlerExprs.length === 0) {
|
|
25138
|
+
throw new Error('Expected listener to have non-empty expression list.');
|
|
25139
|
+
}
|
|
25140
|
+
const expressions = handlerExprs.map(expr => convertAst(expr, unit.job, handlerSpan));
|
|
25141
|
+
const returnExpr = expressions.pop();
|
|
25142
|
+
handlerOps.push(...expressions.map(e => createStatementOp(new ExpressionStatement(e, e.sourceSpan))));
|
|
25143
|
+
handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
|
|
25144
|
+
return handlerOps;
|
|
25145
|
+
}
|
|
25146
|
+
function makeTwoWayListenerHandlerOps(unit, handler, handlerSpan) {
|
|
25147
|
+
handler = astOf(handler);
|
|
25148
|
+
const handlerOps = new Array();
|
|
25149
|
+
if (handler instanceof Chain) {
|
|
25150
|
+
if (handler.expressions.length === 1) {
|
|
25151
|
+
handler = handler.expressions[0];
|
|
25152
|
+
}
|
|
25153
|
+
else {
|
|
25154
|
+
// This is validated during parsing already, but we do it here just in case.
|
|
25155
|
+
throw new Error('Expected two-way listener to have a single expression.');
|
|
25156
|
+
}
|
|
23971
25157
|
}
|
|
23972
|
-
|
|
25158
|
+
const handlerExpr = convertAst(handler, unit.job, handlerSpan);
|
|
25159
|
+
const eventReference = new LexicalReadExpr('$event');
|
|
25160
|
+
const twoWaySetExpr = new TwoWayBindingSetExpr(handlerExpr, eventReference);
|
|
25161
|
+
handlerOps.push(createStatementOp(new ExpressionStatement(twoWaySetExpr)));
|
|
25162
|
+
handlerOps.push(createStatementOp(new ReturnStatement(eventReference)));
|
|
25163
|
+
return handlerOps;
|
|
25164
|
+
}
|
|
25165
|
+
function astOf(ast) {
|
|
25166
|
+
return ast instanceof ASTWithSource ? ast.ast : ast;
|
|
23973
25167
|
}
|
|
23974
25168
|
/**
|
|
23975
|
-
*
|
|
23976
|
-
*
|
|
25169
|
+
* Process all of the local references on an element-like structure in the template AST and
|
|
25170
|
+
* convert them to their IR representation.
|
|
23977
25171
|
*/
|
|
23978
|
-
function
|
|
23979
|
-
|
|
23980
|
-
|
|
23981
|
-
|
|
23982
|
-
|
|
23983
|
-
|
|
23984
|
-
|
|
23985
|
-
return Identifiers.classMapInterpolate2;
|
|
23986
|
-
case 7:
|
|
23987
|
-
return Identifiers.classMapInterpolate3;
|
|
23988
|
-
case 9:
|
|
23989
|
-
return Identifiers.classMapInterpolate4;
|
|
23990
|
-
case 11:
|
|
23991
|
-
return Identifiers.classMapInterpolate5;
|
|
23992
|
-
case 13:
|
|
23993
|
-
return Identifiers.classMapInterpolate6;
|
|
23994
|
-
case 15:
|
|
23995
|
-
return Identifiers.classMapInterpolate7;
|
|
23996
|
-
case 17:
|
|
23997
|
-
return Identifiers.classMapInterpolate8;
|
|
23998
|
-
default:
|
|
23999
|
-
return Identifiers.classMapInterpolateV;
|
|
25172
|
+
function ingestReferences(op, element) {
|
|
25173
|
+
assertIsArray(op.localRefs);
|
|
25174
|
+
for (const { name, value } of element.references) {
|
|
25175
|
+
op.localRefs.push({
|
|
25176
|
+
name,
|
|
25177
|
+
target: value,
|
|
25178
|
+
});
|
|
24000
25179
|
}
|
|
24001
25180
|
}
|
|
24002
25181
|
/**
|
|
24003
|
-
*
|
|
24004
|
-
* @param interpolation An Interpolation AST
|
|
25182
|
+
* Assert that the given value is an array.
|
|
24005
25183
|
*/
|
|
24006
|
-
function
|
|
24007
|
-
|
|
24008
|
-
|
|
24009
|
-
return Identifiers.styleMap;
|
|
24010
|
-
case 3:
|
|
24011
|
-
return Identifiers.styleMapInterpolate1;
|
|
24012
|
-
case 5:
|
|
24013
|
-
return Identifiers.styleMapInterpolate2;
|
|
24014
|
-
case 7:
|
|
24015
|
-
return Identifiers.styleMapInterpolate3;
|
|
24016
|
-
case 9:
|
|
24017
|
-
return Identifiers.styleMapInterpolate4;
|
|
24018
|
-
case 11:
|
|
24019
|
-
return Identifiers.styleMapInterpolate5;
|
|
24020
|
-
case 13:
|
|
24021
|
-
return Identifiers.styleMapInterpolate6;
|
|
24022
|
-
case 15:
|
|
24023
|
-
return Identifiers.styleMapInterpolate7;
|
|
24024
|
-
case 17:
|
|
24025
|
-
return Identifiers.styleMapInterpolate8;
|
|
24026
|
-
default:
|
|
24027
|
-
return Identifiers.styleMapInterpolateV;
|
|
25184
|
+
function assertIsArray(value) {
|
|
25185
|
+
if (!Array.isArray(value)) {
|
|
25186
|
+
throw new Error(`AssertionError: expected an array`);
|
|
24028
25187
|
}
|
|
24029
25188
|
}
|
|
24030
25189
|
/**
|
|
24031
|
-
*
|
|
24032
|
-
*
|
|
25190
|
+
* Creates an absolute `ParseSourceSpan` from the relative `ParseSpan`.
|
|
25191
|
+
*
|
|
25192
|
+
* `ParseSpan` objects are relative to the start of the expression.
|
|
25193
|
+
* This method converts these to full `ParseSourceSpan` objects that
|
|
25194
|
+
* show where the span is within the overall source file.
|
|
25195
|
+
*
|
|
25196
|
+
* @param span the relative span to convert.
|
|
25197
|
+
* @param baseSourceSpan a span corresponding to the base of the expression tree.
|
|
25198
|
+
* @returns a `ParseSourceSpan` for the given span or null if no `baseSourceSpan` was provided.
|
|
24033
25199
|
*/
|
|
24034
|
-
function
|
|
24035
|
-
|
|
24036
|
-
|
|
24037
|
-
return Identifiers.styleProp;
|
|
24038
|
-
case 3:
|
|
24039
|
-
return Identifiers.stylePropInterpolate1;
|
|
24040
|
-
case 5:
|
|
24041
|
-
return Identifiers.stylePropInterpolate2;
|
|
24042
|
-
case 7:
|
|
24043
|
-
return Identifiers.stylePropInterpolate3;
|
|
24044
|
-
case 9:
|
|
24045
|
-
return Identifiers.stylePropInterpolate4;
|
|
24046
|
-
case 11:
|
|
24047
|
-
return Identifiers.stylePropInterpolate5;
|
|
24048
|
-
case 13:
|
|
24049
|
-
return Identifiers.stylePropInterpolate6;
|
|
24050
|
-
case 15:
|
|
24051
|
-
return Identifiers.stylePropInterpolate7;
|
|
24052
|
-
case 17:
|
|
24053
|
-
return Identifiers.stylePropInterpolate8;
|
|
24054
|
-
default:
|
|
24055
|
-
return Identifiers.stylePropInterpolateV;
|
|
25200
|
+
function convertSourceSpan(span, baseSourceSpan) {
|
|
25201
|
+
if (baseSourceSpan === null) {
|
|
25202
|
+
return null;
|
|
24056
25203
|
}
|
|
25204
|
+
const start = baseSourceSpan.start.moveBy(span.start);
|
|
25205
|
+
const end = baseSourceSpan.start.moveBy(span.end);
|
|
25206
|
+
const fullStart = baseSourceSpan.fullStart.moveBy(span.start);
|
|
25207
|
+
return new ParseSourceSpan(start, end, fullStart);
|
|
24057
25208
|
}
|
|
24058
25209
|
/**
|
|
24059
|
-
*
|
|
24060
|
-
*
|
|
25210
|
+
* With the directive-based control flow users were able to conditionally project content using
|
|
25211
|
+
* the `*` syntax. E.g. `<div *ngIf="expr" projectMe></div>` will be projected into
|
|
25212
|
+
* `<ng-content select="[projectMe]"/>`, because the attributes and tag name from the `div` are
|
|
25213
|
+
* copied to the template via the template creation instruction. With `@if` and `@for` that is
|
|
25214
|
+
* not the case, because the conditional is placed *around* elements, rather than *on* them.
|
|
25215
|
+
* The result is that content projection won't work in the same way if a user converts from
|
|
25216
|
+
* `*ngIf` to `@if`.
|
|
25217
|
+
*
|
|
25218
|
+
* This function aims to cover the most common case by doing the same copying when a control flow
|
|
25219
|
+
* node has *one and only one* root element or template node.
|
|
25220
|
+
*
|
|
25221
|
+
* This approach comes with some caveats:
|
|
25222
|
+
* 1. As soon as any other node is added to the root, the copying behavior won't work anymore.
|
|
25223
|
+
* A diagnostic will be added to flag cases like this and to explain how to work around it.
|
|
25224
|
+
* 2. If `preserveWhitespaces` is enabled, it's very likely that indentation will break this
|
|
25225
|
+
* workaround, because it'll include an additional text node as the first child. We can work
|
|
25226
|
+
* around it here, but in a discussion it was decided not to, because the user explicitly opted
|
|
25227
|
+
* into preserving the whitespace and we would have to drop it from the generated code.
|
|
25228
|
+
* The diagnostic mentioned point #1 will flag such cases to users.
|
|
25229
|
+
*
|
|
25230
|
+
* @returns Tag name to be used for the control flow template.
|
|
24061
25231
|
*/
|
|
24062
|
-
function
|
|
24063
|
-
|
|
24064
|
-
|
|
24065
|
-
|
|
24066
|
-
|
|
24067
|
-
|
|
25232
|
+
function ingestControlFlowInsertionPoint(unit, xref, node) {
|
|
25233
|
+
let root = null;
|
|
25234
|
+
for (const child of node.children) {
|
|
25235
|
+
// Skip over comment nodes.
|
|
25236
|
+
if (child instanceof Comment$1) {
|
|
25237
|
+
continue;
|
|
25238
|
+
}
|
|
25239
|
+
// We can only infer the tag name/attributes if there's a single root node.
|
|
25240
|
+
if (root !== null) {
|
|
25241
|
+
return null;
|
|
25242
|
+
}
|
|
25243
|
+
// Root nodes can only elements or templates with a tag name (e.g. `<div *foo></div>`).
|
|
25244
|
+
if (child instanceof Element$1 || (child instanceof Template && child.tagName !== null)) {
|
|
25245
|
+
root = child;
|
|
25246
|
+
}
|
|
24068
25247
|
}
|
|
24069
|
-
|
|
25248
|
+
// If we've found a single root node, its tag name and *static* attributes can be copied
|
|
25249
|
+
// to the surrounding template to be used for content projection. Note that it's important
|
|
25250
|
+
// that we don't copy any bound attributes since they don't participate in content projection
|
|
25251
|
+
// and they can be used in directive matching (in the case of `Template.templateAttrs`).
|
|
25252
|
+
if (root !== null) {
|
|
25253
|
+
for (const attr of root.attributes) {
|
|
25254
|
+
const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
|
|
25255
|
+
unit.update.push(createBindingOp(xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
|
|
25256
|
+
}
|
|
25257
|
+
const tagName = root instanceof Element$1 ? root.name : root.tagName;
|
|
25258
|
+
// Don't pass along `ng-template` tag name since it enables directive matching.
|
|
25259
|
+
return tagName === NG_TEMPLATE_TAG_NAME$1 ? null : tagName;
|
|
25260
|
+
}
|
|
25261
|
+
return null;
|
|
24070
25262
|
}
|
|
24071
25263
|
|
|
24072
25264
|
class HtmlParser extends Parser {
|
|
@@ -24194,7 +25386,7 @@ class BindingParser {
|
|
|
24194
25386
|
for (const propName of Object.keys(properties)) {
|
|
24195
25387
|
const expression = properties[propName];
|
|
24196
25388
|
if (typeof expression === 'string') {
|
|
24197
|
-
this.parsePropertyBinding(propName, expression, true, sourceSpan, sourceSpan.start.offset, undefined, [],
|
|
25389
|
+
this.parsePropertyBinding(propName, expression, true, false, sourceSpan, sourceSpan.start.offset, undefined, [],
|
|
24198
25390
|
// Use the `sourceSpan` for `keySpan`. This isn't really accurate, but neither is the
|
|
24199
25391
|
// sourceSpan, as it represents the sourceSpan of the host itself rather than the
|
|
24200
25392
|
// source of the host binding (which doesn't exist in the template). Regardless,
|
|
@@ -24290,7 +25482,7 @@ class BindingParser {
|
|
|
24290
25482
|
else if (binding.value) {
|
|
24291
25483
|
const srcSpan = isIvyAst ? bindingSpan : sourceSpan;
|
|
24292
25484
|
const valueSpan = moveParseSourceSpan(sourceSpan, binding.value.ast.sourceSpan);
|
|
24293
|
-
this._parsePropertyAst(key, binding.value, srcSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
25485
|
+
this._parsePropertyAst(key, binding.value, false, srcSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
24294
25486
|
}
|
|
24295
25487
|
else {
|
|
24296
25488
|
targetMatchableAttrs.push([key, '' /* value */]);
|
|
@@ -24343,7 +25535,7 @@ class BindingParser {
|
|
|
24343
25535
|
targetProps.push(new ParsedProperty(name, this._exprParser.wrapLiteralPrimitive(value, '', absoluteOffset), exports.ParsedPropertyType.LITERAL_ATTR, sourceSpan, keySpan, valueSpan));
|
|
24344
25536
|
}
|
|
24345
25537
|
}
|
|
24346
|
-
parsePropertyBinding(name, expression, isHost, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs, targetProps, keySpan) {
|
|
25538
|
+
parsePropertyBinding(name, expression, isHost, isPartOfAssignmentBinding, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs, targetProps, keySpan) {
|
|
24347
25539
|
if (name.length === 0) {
|
|
24348
25540
|
this._reportError(`Property name is missing in binding`, sourceSpan);
|
|
24349
25541
|
}
|
|
@@ -24366,20 +25558,20 @@ class BindingParser {
|
|
|
24366
25558
|
this._parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
24367
25559
|
}
|
|
24368
25560
|
else {
|
|
24369
|
-
this._parsePropertyAst(name, this.parseBinding(expression, isHost, valueSpan || sourceSpan, absoluteOffset), sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
25561
|
+
this._parsePropertyAst(name, this.parseBinding(expression, isHost, valueSpan || sourceSpan, absoluteOffset), isPartOfAssignmentBinding, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
24370
25562
|
}
|
|
24371
25563
|
}
|
|
24372
25564
|
parsePropertyInterpolation(name, value, sourceSpan, valueSpan, targetMatchableAttrs, targetProps, keySpan, interpolatedTokens) {
|
|
24373
25565
|
const expr = this.parseInterpolation(value, valueSpan || sourceSpan, interpolatedTokens);
|
|
24374
25566
|
if (expr) {
|
|
24375
|
-
this._parsePropertyAst(name, expr, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
25567
|
+
this._parsePropertyAst(name, expr, false, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
24376
25568
|
return true;
|
|
24377
25569
|
}
|
|
24378
25570
|
return false;
|
|
24379
25571
|
}
|
|
24380
|
-
_parsePropertyAst(name, ast, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
|
|
25572
|
+
_parsePropertyAst(name, ast, isPartOfAssignmentBinding, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
|
|
24381
25573
|
targetMatchableAttrs.push([name, ast.source]);
|
|
24382
|
-
targetProps.push(new ParsedProperty(name, ast, exports.ParsedPropertyType.DEFAULT, sourceSpan, keySpan, valueSpan));
|
|
25574
|
+
targetProps.push(new ParsedProperty(name, ast, isPartOfAssignmentBinding ? exports.ParsedPropertyType.TWO_WAY : exports.ParsedPropertyType.DEFAULT, sourceSpan, keySpan, valueSpan));
|
|
24383
25575
|
}
|
|
24384
25576
|
_parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
|
|
24385
25577
|
if (name.length === 0) {
|
|
@@ -24449,7 +25641,8 @@ class BindingParser {
|
|
|
24449
25641
|
const mappedPropName = this._schemaRegistry.getMappedPropName(boundProp.name);
|
|
24450
25642
|
boundPropertyName = mapPropertyName ? mappedPropName : boundProp.name;
|
|
24451
25643
|
securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, mappedPropName, false);
|
|
24452
|
-
bindingType =
|
|
25644
|
+
bindingType =
|
|
25645
|
+
boundProp.type === exports.ParsedPropertyType.TWO_WAY ? 5 /* BindingType.TwoWay */ : 0 /* BindingType.Property */;
|
|
24453
25646
|
if (!skipValidation) {
|
|
24454
25647
|
this._validatePropertyOrAttributeName(mappedPropName, boundProp.sourceSpan, false);
|
|
24455
25648
|
}
|
|
@@ -24466,7 +25659,7 @@ class BindingParser {
|
|
|
24466
25659
|
if (keySpan !== undefined) {
|
|
24467
25660
|
keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
|
|
24468
25661
|
}
|
|
24469
|
-
this._parseAnimationEvent(name, expression,
|
|
25662
|
+
this._parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan);
|
|
24470
25663
|
}
|
|
24471
25664
|
else {
|
|
24472
25665
|
this._parseRegularEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan);
|
|
@@ -24476,11 +25669,11 @@ class BindingParser {
|
|
|
24476
25669
|
const prop = this._schemaRegistry.getMappedPropName(propName);
|
|
24477
25670
|
return calcPossibleSecurityContexts(this._schemaRegistry, selector, prop, isAttribute);
|
|
24478
25671
|
}
|
|
24479
|
-
_parseAnimationEvent(name, expression,
|
|
25672
|
+
_parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan) {
|
|
24480
25673
|
const matches = splitAtPeriod(name, [name, '']);
|
|
24481
25674
|
const eventName = matches[0];
|
|
24482
25675
|
const phase = matches[1].toLowerCase();
|
|
24483
|
-
const ast = this._parseAction(expression,
|
|
25676
|
+
const ast = this._parseAction(expression, handlerSpan);
|
|
24484
25677
|
targetEvents.push(new ParsedEvent(eventName, phase, 1 /* ParsedEventType.Animation */, ast, sourceSpan, handlerSpan, keySpan));
|
|
24485
25678
|
if (eventName.length === 0) {
|
|
24486
25679
|
this._reportError(`Animation event name is missing in binding`, sourceSpan);
|
|
@@ -24497,17 +25690,24 @@ class BindingParser {
|
|
|
24497
25690
|
_parseRegularEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) {
|
|
24498
25691
|
// long format: 'target: eventName'
|
|
24499
25692
|
const [target, eventName] = splitAtColon(name, [null, name]);
|
|
24500
|
-
const
|
|
25693
|
+
const prevErrorCount = this.errors.length;
|
|
25694
|
+
const ast = this._parseAction(expression, handlerSpan);
|
|
25695
|
+
const isValid = this.errors.length === prevErrorCount;
|
|
24501
25696
|
targetMatchableAttrs.push([name, ast.source]);
|
|
24502
|
-
|
|
25697
|
+
// Don't try to validate assignment events if there were other
|
|
25698
|
+
// parsing errors to avoid adding more noise to the error logs.
|
|
25699
|
+
if (isAssignmentEvent && isValid && !this._isAllowedAssignmentEvent(ast)) {
|
|
25700
|
+
this._reportError('Unsupported expression in a two-way binding', sourceSpan);
|
|
25701
|
+
}
|
|
25702
|
+
targetEvents.push(new ParsedEvent(eventName, target, isAssignmentEvent ? 2 /* ParsedEventType.TwoWay */ : 0 /* ParsedEventType.Regular */, ast, sourceSpan, handlerSpan, keySpan));
|
|
24503
25703
|
// Don't detect directives for event names for now,
|
|
24504
25704
|
// so don't add the event name to the matchableAttrs
|
|
24505
25705
|
}
|
|
24506
|
-
_parseAction(value,
|
|
25706
|
+
_parseAction(value, sourceSpan) {
|
|
24507
25707
|
const sourceInfo = (sourceSpan && sourceSpan.start || '(unknown').toString();
|
|
24508
25708
|
const absoluteOffset = (sourceSpan && sourceSpan.start) ? sourceSpan.start.offset : 0;
|
|
24509
25709
|
try {
|
|
24510
|
-
const ast = this._exprParser.parseAction(value,
|
|
25710
|
+
const ast = this._exprParser.parseAction(value, sourceInfo, absoluteOffset, this._interpolationConfig);
|
|
24511
25711
|
if (ast) {
|
|
24512
25712
|
this._reportExpressionParserErrors(ast.errors, sourceSpan);
|
|
24513
25713
|
}
|
|
@@ -24542,6 +25742,26 @@ class BindingParser {
|
|
|
24542
25742
|
this._reportError(report.msg, sourceSpan, exports.ParseErrorLevel.ERROR);
|
|
24543
25743
|
}
|
|
24544
25744
|
}
|
|
25745
|
+
/**
|
|
25746
|
+
* Returns whether a parsed AST is allowed to be used within the event side of a two-way binding.
|
|
25747
|
+
* @param ast Parsed AST to be checked.
|
|
25748
|
+
*/
|
|
25749
|
+
_isAllowedAssignmentEvent(ast) {
|
|
25750
|
+
if (ast instanceof ASTWithSource) {
|
|
25751
|
+
return this._isAllowedAssignmentEvent(ast.ast);
|
|
25752
|
+
}
|
|
25753
|
+
if (ast instanceof NonNullAssert) {
|
|
25754
|
+
return this._isAllowedAssignmentEvent(ast.expression);
|
|
25755
|
+
}
|
|
25756
|
+
if (ast instanceof PropertyRead || ast instanceof KeyedRead) {
|
|
25757
|
+
return true;
|
|
25758
|
+
}
|
|
25759
|
+
if (ast instanceof Binary) {
|
|
25760
|
+
return (ast.operation === '&&' || ast.operation === '||' || ast.operation === '??') &&
|
|
25761
|
+
(ast.right instanceof PropertyRead || ast.right instanceof KeyedRead);
|
|
25762
|
+
}
|
|
25763
|
+
return ast instanceof Conditional || ast instanceof PrefixNot;
|
|
25764
|
+
}
|
|
24545
25765
|
}
|
|
24546
25766
|
function isAnimationLabel(name) {
|
|
24547
25767
|
return name[0] == '@';
|
|
@@ -24662,11 +25882,16 @@ const FOR_LOOP_EXPRESSION_PATTERN = /^\s*([0-9A-Za-z_$]*)\s+of\s+([\S\s]*)/;
|
|
|
24662
25882
|
/** Pattern for the tracking expression in a for loop block. */
|
|
24663
25883
|
const FOR_LOOP_TRACK_PATTERN = /^track\s+([\S\s]*)/;
|
|
24664
25884
|
/** Pattern for the `as` expression in a conditional block. */
|
|
24665
|
-
const CONDITIONAL_ALIAS_PATTERN = /^as\s+(.*)/;
|
|
25885
|
+
const CONDITIONAL_ALIAS_PATTERN = /^(as\s)+(.*)/;
|
|
24666
25886
|
/** Pattern used to identify an `else if` block. */
|
|
24667
25887
|
const ELSE_IF_PATTERN = /^else[^\S\r\n]+if/;
|
|
24668
25888
|
/** Pattern used to identify a `let` parameter. */
|
|
24669
25889
|
const FOR_LOOP_LET_PATTERN = /^let\s+([\S\s]*)/;
|
|
25890
|
+
/**
|
|
25891
|
+
* Pattern to group a string into leading whitespace, non whitespace, and trailing whitespace.
|
|
25892
|
+
* Useful for getting the variable name span when a span can contain leading and trailing space.
|
|
25893
|
+
*/
|
|
25894
|
+
const CHARACTERS_IN_SURROUNDING_WHITESPACE_PATTERN = /(\s*)(\S+)(\s*)/;
|
|
24670
25895
|
/** Names of variables that are allowed to be used in the `let` expression of a `for` loop. */
|
|
24671
25896
|
const ALLOWED_FOR_LOOP_LET_VARIABLES = new Set(['$index', '$first', '$last', '$even', '$odd', '$count']);
|
|
24672
25897
|
/**
|
|
@@ -24806,8 +26031,13 @@ function parseForLoopParameters(block, errors, bindingParser) {
|
|
|
24806
26031
|
return null;
|
|
24807
26032
|
}
|
|
24808
26033
|
const [, itemName, rawExpression] = match;
|
|
26034
|
+
// `expressionParam.expression` contains the variable declaration and the expression of the
|
|
26035
|
+
// for...of statement, i.e. 'user of users' The variable of a ForOfStatement is _only_ the "const
|
|
26036
|
+
// user" part and does not include "of x".
|
|
26037
|
+
const variableName = expressionParam.expression.split(' ')[0];
|
|
26038
|
+
const variableSpan = new ParseSourceSpan(expressionParam.sourceSpan.start, expressionParam.sourceSpan.start.moveBy(variableName.length));
|
|
24809
26039
|
const result = {
|
|
24810
|
-
itemName: new Variable(itemName, '$implicit',
|
|
26040
|
+
itemName: new Variable(itemName, '$implicit', variableSpan, variableSpan),
|
|
24811
26041
|
trackBy: null,
|
|
24812
26042
|
expression: parseBlockParameterToBinding(expressionParam, bindingParser, rawExpression),
|
|
24813
26043
|
context: {},
|
|
@@ -24815,7 +26045,8 @@ function parseForLoopParameters(block, errors, bindingParser) {
|
|
|
24815
26045
|
for (const param of secondaryParams) {
|
|
24816
26046
|
const letMatch = param.expression.match(FOR_LOOP_LET_PATTERN);
|
|
24817
26047
|
if (letMatch !== null) {
|
|
24818
|
-
|
|
26048
|
+
const variablesSpan = new ParseSourceSpan(param.sourceSpan.start.moveBy(letMatch[0].length - letMatch[1].length), param.sourceSpan.end);
|
|
26049
|
+
parseLetParameter(param.sourceSpan, letMatch[1], variablesSpan, result.context, errors);
|
|
24819
26050
|
continue;
|
|
24820
26051
|
}
|
|
24821
26052
|
const trackMatch = param.expression.match(FOR_LOOP_TRACK_PATTERN);
|
|
@@ -24825,6 +26056,9 @@ function parseForLoopParameters(block, errors, bindingParser) {
|
|
|
24825
26056
|
}
|
|
24826
26057
|
else {
|
|
24827
26058
|
const expression = parseBlockParameterToBinding(param, bindingParser, trackMatch[1]);
|
|
26059
|
+
if (expression.ast instanceof EmptyExpr$1) {
|
|
26060
|
+
errors.push(new ParseError(param.sourceSpan, '@for loop must have a "track" expression'));
|
|
26061
|
+
}
|
|
24828
26062
|
const keywordSpan = new ParseSourceSpan(param.sourceSpan.start, param.sourceSpan.start.moveBy('track'.length));
|
|
24829
26063
|
result.trackBy = { expression, keywordSpan };
|
|
24830
26064
|
}
|
|
@@ -24846,6 +26080,7 @@ function parseForLoopParameters(block, errors, bindingParser) {
|
|
|
24846
26080
|
/** Parses the `let` parameter of a `for` loop block. */
|
|
24847
26081
|
function parseLetParameter(sourceSpan, expression, span, context, errors) {
|
|
24848
26082
|
const parts = expression.split(',');
|
|
26083
|
+
let startSpan = span.start;
|
|
24849
26084
|
for (const part of parts) {
|
|
24850
26085
|
const expressionParts = part.split('=');
|
|
24851
26086
|
const name = expressionParts.length === 2 ? expressionParts[0].trim() : '';
|
|
@@ -24860,8 +26095,26 @@ function parseLetParameter(sourceSpan, expression, span, context, errors) {
|
|
|
24860
26095
|
errors.push(new ParseError(sourceSpan, `Duplicate "let" parameter variable "${variableName}"`));
|
|
24861
26096
|
}
|
|
24862
26097
|
else {
|
|
24863
|
-
|
|
26098
|
+
const [, keyLeadingWhitespace, keyName] = expressionParts[0].match(CHARACTERS_IN_SURROUNDING_WHITESPACE_PATTERN) ?? [];
|
|
26099
|
+
const keySpan = keyLeadingWhitespace !== undefined && expressionParts.length === 2 ?
|
|
26100
|
+
new ParseSourceSpan(
|
|
26101
|
+
/* strip leading spaces */
|
|
26102
|
+
startSpan.moveBy(keyLeadingWhitespace.length),
|
|
26103
|
+
/* advance to end of the variable name */
|
|
26104
|
+
startSpan.moveBy(keyLeadingWhitespace.length + keyName.length)) :
|
|
26105
|
+
span;
|
|
26106
|
+
let valueSpan = undefined;
|
|
26107
|
+
if (expressionParts.length === 2) {
|
|
26108
|
+
const [, valueLeadingWhitespace, implicit] = expressionParts[1].match(CHARACTERS_IN_SURROUNDING_WHITESPACE_PATTERN) ?? [];
|
|
26109
|
+
valueSpan = valueLeadingWhitespace !== undefined ?
|
|
26110
|
+
new ParseSourceSpan(startSpan.moveBy(expressionParts[0].length + 1 + valueLeadingWhitespace.length), startSpan.moveBy(expressionParts[0].length + 1 + valueLeadingWhitespace.length +
|
|
26111
|
+
implicit.length)) :
|
|
26112
|
+
undefined;
|
|
26113
|
+
}
|
|
26114
|
+
const sourceSpan = new ParseSourceSpan(keySpan.start, valueSpan?.end ?? keySpan.end);
|
|
26115
|
+
context[variableName] = new Variable(name, variableName, sourceSpan, keySpan, valueSpan);
|
|
24864
26116
|
}
|
|
26117
|
+
startSpan = startSpan.moveBy(part.length + 1 /* add 1 to move past the comma */);
|
|
24865
26118
|
}
|
|
24866
26119
|
}
|
|
24867
26120
|
/**
|
|
@@ -24973,8 +26226,10 @@ function parseConditionalBlockParameters(block, errors, bindingParser) {
|
|
|
24973
26226
|
errors.push(new ParseError(param.sourceSpan, 'Conditional can only have one "as" expression'));
|
|
24974
26227
|
}
|
|
24975
26228
|
else {
|
|
24976
|
-
const name = aliasMatch[
|
|
24977
|
-
|
|
26229
|
+
const name = aliasMatch[2].trim();
|
|
26230
|
+
const variableStart = param.sourceSpan.start.moveBy(aliasMatch[1].length);
|
|
26231
|
+
const variableSpan = new ParseSourceSpan(variableStart, variableStart.moveBy(name.length));
|
|
26232
|
+
expressionAlias = new Variable(name, name, variableSpan, variableSpan);
|
|
24978
26233
|
}
|
|
24979
26234
|
}
|
|
24980
26235
|
return { expression, expressionAlias };
|
|
@@ -25835,7 +27090,7 @@ class HtmlAstToIvyAst {
|
|
|
25835
27090
|
if (bindParts[KW_BIND_IDX] != null) {
|
|
25836
27091
|
const identifier = bindParts[IDENT_KW_IDX];
|
|
25837
27092
|
const keySpan = createKeySpan(srcSpan, bindParts[KW_BIND_IDX], identifier);
|
|
25838
|
-
this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
27093
|
+
this.bindingParser.parsePropertyBinding(identifier, value, false, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
25839
27094
|
}
|
|
25840
27095
|
else if (bindParts[KW_LET_IDX]) {
|
|
25841
27096
|
if (isTemplateElement) {
|
|
@@ -25862,7 +27117,7 @@ class HtmlAstToIvyAst {
|
|
|
25862
27117
|
else if (bindParts[KW_BINDON_IDX]) {
|
|
25863
27118
|
const identifier = bindParts[IDENT_KW_IDX];
|
|
25864
27119
|
const keySpan = createKeySpan(srcSpan, bindParts[KW_BINDON_IDX], identifier);
|
|
25865
|
-
this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
27120
|
+
this.bindingParser.parsePropertyBinding(identifier, value, false, true, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
25866
27121
|
this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);
|
|
25867
27122
|
}
|
|
25868
27123
|
else if (bindParts[KW_AT_IDX]) {
|
|
@@ -25892,11 +27147,11 @@ class HtmlAstToIvyAst {
|
|
|
25892
27147
|
const identifier = name.substring(delims.start.length, name.length - delims.end.length);
|
|
25893
27148
|
const keySpan = createKeySpan(srcSpan, delims.start, identifier);
|
|
25894
27149
|
if (delims.start === BINDING_DELIMS.BANANA_BOX.start) {
|
|
25895
|
-
this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
27150
|
+
this.bindingParser.parsePropertyBinding(identifier, value, false, true, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
25896
27151
|
this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);
|
|
25897
27152
|
}
|
|
25898
27153
|
else if (delims.start === BINDING_DELIMS.PROPERTY.start) {
|
|
25899
|
-
this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
27154
|
+
this.bindingParser.parsePropertyBinding(identifier, value, false, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
25900
27155
|
}
|
|
25901
27156
|
else {
|
|
25902
27157
|
const events = [];
|
|
@@ -25938,7 +27193,7 @@ class HtmlAstToIvyAst {
|
|
|
25938
27193
|
}
|
|
25939
27194
|
parseAssignmentEvent(name, expression, sourceSpan, valueSpan, targetMatchableAttrs, boundEvents, keySpan) {
|
|
25940
27195
|
const events = [];
|
|
25941
|
-
this.bindingParser.parseEvent(`${name}Change`,
|
|
27196
|
+
this.bindingParser.parseEvent(`${name}Change`, expression, /* isAssignmentEvent */ true, sourceSpan, valueSpan || sourceSpan, targetMatchableAttrs, events, keySpan);
|
|
25942
27197
|
addEvents(events, boundEvents);
|
|
25943
27198
|
}
|
|
25944
27199
|
reportError(message, sourceSpan, level = exports.ParseErrorLevel.ERROR) {
|
|
@@ -26217,6 +27472,535 @@ function serializePlaceholderValue(value) {
|
|
|
26217
27472
|
}
|
|
26218
27473
|
}
|
|
26219
27474
|
|
|
27475
|
+
const IMPORTANT_FLAG = '!important';
|
|
27476
|
+
/**
|
|
27477
|
+
* Minimum amount of binding slots required in the runtime for style/class bindings.
|
|
27478
|
+
*
|
|
27479
|
+
* Styling in Angular uses up two slots in the runtime LView/TData data structures to
|
|
27480
|
+
* record binding data, property information and metadata.
|
|
27481
|
+
*
|
|
27482
|
+
* When a binding is registered it will place the following information in the `LView`:
|
|
27483
|
+
*
|
|
27484
|
+
* slot 1) binding value
|
|
27485
|
+
* slot 2) cached value (all other values collected before it in string form)
|
|
27486
|
+
*
|
|
27487
|
+
* When a binding is registered it will place the following information in the `TData`:
|
|
27488
|
+
*
|
|
27489
|
+
* slot 1) prop name
|
|
27490
|
+
* slot 2) binding index that points to the previous style/class binding (and some extra config
|
|
27491
|
+
* values)
|
|
27492
|
+
*
|
|
27493
|
+
* Let's imagine we have a binding that looks like so:
|
|
27494
|
+
*
|
|
27495
|
+
* ```
|
|
27496
|
+
* <div [style.width]="x" [style.height]="y">
|
|
27497
|
+
* ```
|
|
27498
|
+
*
|
|
27499
|
+
* Our `LView` and `TData` data-structures look like so:
|
|
27500
|
+
*
|
|
27501
|
+
* ```typescript
|
|
27502
|
+
* LView = [
|
|
27503
|
+
* // ...
|
|
27504
|
+
* x, // value of x
|
|
27505
|
+
* "width: x",
|
|
27506
|
+
*
|
|
27507
|
+
* y, // value of y
|
|
27508
|
+
* "width: x; height: y",
|
|
27509
|
+
* // ...
|
|
27510
|
+
* ];
|
|
27511
|
+
*
|
|
27512
|
+
* TData = [
|
|
27513
|
+
* // ...
|
|
27514
|
+
* "width", // binding slot 20
|
|
27515
|
+
* 0,
|
|
27516
|
+
*
|
|
27517
|
+
* "height",
|
|
27518
|
+
* 20,
|
|
27519
|
+
* // ...
|
|
27520
|
+
* ];
|
|
27521
|
+
* ```
|
|
27522
|
+
*
|
|
27523
|
+
* */
|
|
27524
|
+
const MIN_STYLING_BINDING_SLOTS_REQUIRED = 2;
|
|
27525
|
+
/**
|
|
27526
|
+
* Produces creation/update instructions for all styling bindings (class and style)
|
|
27527
|
+
*
|
|
27528
|
+
* It also produces the creation instruction to register all initial styling values
|
|
27529
|
+
* (which are all the static class="..." and style="..." attribute values that exist
|
|
27530
|
+
* on an element within a template).
|
|
27531
|
+
*
|
|
27532
|
+
* The builder class below handles producing instructions for the following cases:
|
|
27533
|
+
*
|
|
27534
|
+
* - Static style/class attributes (style="..." and class="...")
|
|
27535
|
+
* - Dynamic style/class map bindings ([style]="map" and [class]="map|string")
|
|
27536
|
+
* - Dynamic style/class property bindings ([style.prop]="exp" and [class.name]="exp")
|
|
27537
|
+
*
|
|
27538
|
+
* Due to the complex relationship of all of these cases, the instructions generated
|
|
27539
|
+
* for these attributes/properties/bindings must be done so in the correct order. The
|
|
27540
|
+
* order which these must be generated is as follows:
|
|
27541
|
+
*
|
|
27542
|
+
* if (createMode) {
|
|
27543
|
+
* styling(...)
|
|
27544
|
+
* }
|
|
27545
|
+
* if (updateMode) {
|
|
27546
|
+
* styleMap(...)
|
|
27547
|
+
* classMap(...)
|
|
27548
|
+
* styleProp(...)
|
|
27549
|
+
* classProp(...)
|
|
27550
|
+
* }
|
|
27551
|
+
*
|
|
27552
|
+
* The creation/update methods within the builder class produce these instructions.
|
|
27553
|
+
*/
|
|
27554
|
+
class StylingBuilder {
|
|
27555
|
+
constructor(_directiveExpr) {
|
|
27556
|
+
this._directiveExpr = _directiveExpr;
|
|
27557
|
+
/** Whether or not there are any static styling values present */
|
|
27558
|
+
this._hasInitialValues = false;
|
|
27559
|
+
/**
|
|
27560
|
+
* Whether or not there are any styling bindings present
|
|
27561
|
+
* (i.e. `[style]`, `[class]`, `[style.prop]` or `[class.name]`)
|
|
27562
|
+
*/
|
|
27563
|
+
this.hasBindings = false;
|
|
27564
|
+
this.hasBindingsWithPipes = false;
|
|
27565
|
+
/** the input for [class] (if it exists) */
|
|
27566
|
+
this._classMapInput = null;
|
|
27567
|
+
/** the input for [style] (if it exists) */
|
|
27568
|
+
this._styleMapInput = null;
|
|
27569
|
+
/** an array of each [style.prop] input */
|
|
27570
|
+
this._singleStyleInputs = null;
|
|
27571
|
+
/** an array of each [class.name] input */
|
|
27572
|
+
this._singleClassInputs = null;
|
|
27573
|
+
this._lastStylingInput = null;
|
|
27574
|
+
this._firstStylingInput = null;
|
|
27575
|
+
// maps are used instead of hash maps because a Map will
|
|
27576
|
+
// retain the ordering of the keys
|
|
27577
|
+
/**
|
|
27578
|
+
* Represents the location of each style binding in the template
|
|
27579
|
+
* (e.g. `<div [style.width]="w" [style.height]="h">` implies
|
|
27580
|
+
* that `width=0` and `height=1`)
|
|
27581
|
+
*/
|
|
27582
|
+
this._stylesIndex = new Map();
|
|
27583
|
+
/**
|
|
27584
|
+
* Represents the location of each class binding in the template
|
|
27585
|
+
* (e.g. `<div [class.big]="b" [class.hidden]="h">` implies
|
|
27586
|
+
* that `big=0` and `hidden=1`)
|
|
27587
|
+
*/
|
|
27588
|
+
this._classesIndex = new Map();
|
|
27589
|
+
this._initialStyleValues = [];
|
|
27590
|
+
this._initialClassValues = [];
|
|
27591
|
+
}
|
|
27592
|
+
/**
|
|
27593
|
+
* Registers a given input to the styling builder to be later used when producing AOT code.
|
|
27594
|
+
*
|
|
27595
|
+
* The code below will only accept the input if it is somehow tied to styling (whether it be
|
|
27596
|
+
* style/class bindings or static style/class attributes).
|
|
27597
|
+
*/
|
|
27598
|
+
registerBoundInput(input) {
|
|
27599
|
+
// [attr.style] or [attr.class] are skipped in the code below,
|
|
27600
|
+
// they should not be treated as styling-based bindings since
|
|
27601
|
+
// they are intended to be written directly to the attr and
|
|
27602
|
+
// will therefore skip all style/class resolution that is present
|
|
27603
|
+
// with style="", [style]="" and [style.prop]="", class="",
|
|
27604
|
+
// [class.prop]="". [class]="" assignments
|
|
27605
|
+
let binding = null;
|
|
27606
|
+
let name = input.name;
|
|
27607
|
+
switch (input.type) {
|
|
27608
|
+
case 0 /* BindingType.Property */:
|
|
27609
|
+
binding = this.registerInputBasedOnName(name, input.value, input.sourceSpan);
|
|
27610
|
+
break;
|
|
27611
|
+
case 3 /* BindingType.Style */:
|
|
27612
|
+
binding = this.registerStyleInput(name, false, input.value, input.sourceSpan, input.unit);
|
|
27613
|
+
break;
|
|
27614
|
+
case 2 /* BindingType.Class */:
|
|
27615
|
+
binding = this.registerClassInput(name, false, input.value, input.sourceSpan);
|
|
27616
|
+
break;
|
|
27617
|
+
}
|
|
27618
|
+
return binding ? true : false;
|
|
27619
|
+
}
|
|
27620
|
+
registerInputBasedOnName(name, expression, sourceSpan) {
|
|
27621
|
+
let binding = null;
|
|
27622
|
+
const prefix = name.substring(0, 6);
|
|
27623
|
+
const isStyle = name === 'style' || prefix === 'style.' || prefix === 'style!';
|
|
27624
|
+
const isClass = !isStyle && (name === 'class' || prefix === 'class.' || prefix === 'class!');
|
|
27625
|
+
if (isStyle || isClass) {
|
|
27626
|
+
const isMapBased = name.charAt(5) !== '.'; // style.prop or class.prop makes this a no
|
|
27627
|
+
const property = name.slice(isMapBased ? 5 : 6); // the dot explains why there's a +1
|
|
27628
|
+
if (isStyle) {
|
|
27629
|
+
binding = this.registerStyleInput(property, isMapBased, expression, sourceSpan);
|
|
27630
|
+
}
|
|
27631
|
+
else {
|
|
27632
|
+
binding = this.registerClassInput(property, isMapBased, expression, sourceSpan);
|
|
27633
|
+
}
|
|
27634
|
+
}
|
|
27635
|
+
return binding;
|
|
27636
|
+
}
|
|
27637
|
+
registerStyleInput(name, isMapBased, value, sourceSpan, suffix) {
|
|
27638
|
+
if (isEmptyExpression(value)) {
|
|
27639
|
+
return null;
|
|
27640
|
+
}
|
|
27641
|
+
// CSS custom properties are case-sensitive so we shouldn't normalize them.
|
|
27642
|
+
// See: https://www.w3.org/TR/css-variables-1/#defining-variables
|
|
27643
|
+
if (!isCssCustomProperty(name)) {
|
|
27644
|
+
name = hyphenate(name);
|
|
27645
|
+
}
|
|
27646
|
+
const { property, hasOverrideFlag, suffix: bindingSuffix } = parseProperty(name);
|
|
27647
|
+
suffix = typeof suffix === 'string' && suffix.length !== 0 ? suffix : bindingSuffix;
|
|
27648
|
+
const entry = { name: property, suffix: suffix, value, sourceSpan, hasOverrideFlag };
|
|
27649
|
+
if (isMapBased) {
|
|
27650
|
+
this._styleMapInput = entry;
|
|
27651
|
+
}
|
|
27652
|
+
else {
|
|
27653
|
+
(this._singleStyleInputs = this._singleStyleInputs || []).push(entry);
|
|
27654
|
+
registerIntoMap(this._stylesIndex, property);
|
|
27655
|
+
}
|
|
27656
|
+
this._lastStylingInput = entry;
|
|
27657
|
+
this._firstStylingInput = this._firstStylingInput || entry;
|
|
27658
|
+
this._checkForPipes(value);
|
|
27659
|
+
this.hasBindings = true;
|
|
27660
|
+
return entry;
|
|
27661
|
+
}
|
|
27662
|
+
registerClassInput(name, isMapBased, value, sourceSpan) {
|
|
27663
|
+
if (isEmptyExpression(value)) {
|
|
27664
|
+
return null;
|
|
27665
|
+
}
|
|
27666
|
+
const { property, hasOverrideFlag } = parseProperty(name);
|
|
27667
|
+
const entry = { name: property, value, sourceSpan, hasOverrideFlag, suffix: null };
|
|
27668
|
+
if (isMapBased) {
|
|
27669
|
+
this._classMapInput = entry;
|
|
27670
|
+
}
|
|
27671
|
+
else {
|
|
27672
|
+
(this._singleClassInputs = this._singleClassInputs || []).push(entry);
|
|
27673
|
+
registerIntoMap(this._classesIndex, property);
|
|
27674
|
+
}
|
|
27675
|
+
this._lastStylingInput = entry;
|
|
27676
|
+
this._firstStylingInput = this._firstStylingInput || entry;
|
|
27677
|
+
this._checkForPipes(value);
|
|
27678
|
+
this.hasBindings = true;
|
|
27679
|
+
return entry;
|
|
27680
|
+
}
|
|
27681
|
+
_checkForPipes(value) {
|
|
27682
|
+
if ((value instanceof ASTWithSource) && (value.ast instanceof BindingPipe)) {
|
|
27683
|
+
this.hasBindingsWithPipes = true;
|
|
27684
|
+
}
|
|
27685
|
+
}
|
|
27686
|
+
/**
|
|
27687
|
+
* Registers the element's static style string value to the builder.
|
|
27688
|
+
*
|
|
27689
|
+
* @param value the style string (e.g. `width:100px; height:200px;`)
|
|
27690
|
+
*/
|
|
27691
|
+
registerStyleAttr(value) {
|
|
27692
|
+
this._initialStyleValues = parse(value);
|
|
27693
|
+
this._hasInitialValues = true;
|
|
27694
|
+
}
|
|
27695
|
+
/**
|
|
27696
|
+
* Registers the element's static class string value to the builder.
|
|
27697
|
+
*
|
|
27698
|
+
* @param value the className string (e.g. `disabled gold zoom`)
|
|
27699
|
+
*/
|
|
27700
|
+
registerClassAttr(value) {
|
|
27701
|
+
this._initialClassValues = value.trim().split(/\s+/g);
|
|
27702
|
+
this._hasInitialValues = true;
|
|
27703
|
+
}
|
|
27704
|
+
/**
|
|
27705
|
+
* Appends all styling-related expressions to the provided attrs array.
|
|
27706
|
+
*
|
|
27707
|
+
* @param attrs an existing array where each of the styling expressions
|
|
27708
|
+
* will be inserted into.
|
|
27709
|
+
*/
|
|
27710
|
+
populateInitialStylingAttrs(attrs) {
|
|
27711
|
+
// [CLASS_MARKER, 'foo', 'bar', 'baz' ...]
|
|
27712
|
+
if (this._initialClassValues.length) {
|
|
27713
|
+
attrs.push(literal(1 /* AttributeMarker.Classes */));
|
|
27714
|
+
for (let i = 0; i < this._initialClassValues.length; i++) {
|
|
27715
|
+
attrs.push(literal(this._initialClassValues[i]));
|
|
27716
|
+
}
|
|
27717
|
+
}
|
|
27718
|
+
// [STYLE_MARKER, 'width', '200px', 'height', '100px', ...]
|
|
27719
|
+
if (this._initialStyleValues.length) {
|
|
27720
|
+
attrs.push(literal(2 /* AttributeMarker.Styles */));
|
|
27721
|
+
for (let i = 0; i < this._initialStyleValues.length; i += 2) {
|
|
27722
|
+
attrs.push(literal(this._initialStyleValues[i]), literal(this._initialStyleValues[i + 1]));
|
|
27723
|
+
}
|
|
27724
|
+
}
|
|
27725
|
+
}
|
|
27726
|
+
/**
|
|
27727
|
+
* Builds an instruction with all the expressions and parameters for `elementHostAttrs`.
|
|
27728
|
+
*
|
|
27729
|
+
* The instruction generation code below is used for producing the AOT statement code which is
|
|
27730
|
+
* responsible for registering initial styles (within a directive hostBindings' creation block),
|
|
27731
|
+
* as well as any of the provided attribute values, to the directive host element.
|
|
27732
|
+
*/
|
|
27733
|
+
assignHostAttrs(attrs, definitionMap) {
|
|
27734
|
+
if (this._directiveExpr && (attrs.length || this._hasInitialValues)) {
|
|
27735
|
+
this.populateInitialStylingAttrs(attrs);
|
|
27736
|
+
definitionMap.set('hostAttrs', literalArr(attrs));
|
|
27737
|
+
}
|
|
27738
|
+
}
|
|
27739
|
+
/**
|
|
27740
|
+
* Builds an instruction with all the expressions and parameters for `classMap`.
|
|
27741
|
+
*
|
|
27742
|
+
* The instruction data will contain all expressions for `classMap` to function
|
|
27743
|
+
* which includes the `[class]` expression params.
|
|
27744
|
+
*/
|
|
27745
|
+
buildClassMapInstruction(valueConverter) {
|
|
27746
|
+
if (this._classMapInput) {
|
|
27747
|
+
return this._buildMapBasedInstruction(valueConverter, true, this._classMapInput);
|
|
27748
|
+
}
|
|
27749
|
+
return null;
|
|
27750
|
+
}
|
|
27751
|
+
/**
|
|
27752
|
+
* Builds an instruction with all the expressions and parameters for `styleMap`.
|
|
27753
|
+
*
|
|
27754
|
+
* The instruction data will contain all expressions for `styleMap` to function
|
|
27755
|
+
* which includes the `[style]` expression params.
|
|
27756
|
+
*/
|
|
27757
|
+
buildStyleMapInstruction(valueConverter) {
|
|
27758
|
+
if (this._styleMapInput) {
|
|
27759
|
+
return this._buildMapBasedInstruction(valueConverter, false, this._styleMapInput);
|
|
27760
|
+
}
|
|
27761
|
+
return null;
|
|
27762
|
+
}
|
|
27763
|
+
_buildMapBasedInstruction(valueConverter, isClassBased, stylingInput) {
|
|
27764
|
+
// each styling binding value is stored in the LView
|
|
27765
|
+
// map-based bindings allocate two slots: one for the
|
|
27766
|
+
// previous binding value and another for the previous
|
|
27767
|
+
// className or style attribute value.
|
|
27768
|
+
let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;
|
|
27769
|
+
// these values must be outside of the update block so that they can
|
|
27770
|
+
// be evaluated (the AST visit call) during creation time so that any
|
|
27771
|
+
// pipes can be picked up in time before the template is built
|
|
27772
|
+
const mapValue = stylingInput.value.visit(valueConverter);
|
|
27773
|
+
let reference;
|
|
27774
|
+
if (mapValue instanceof Interpolation$1) {
|
|
27775
|
+
totalBindingSlotsRequired += mapValue.expressions.length;
|
|
27776
|
+
reference = isClassBased ? getClassMapInterpolationExpression(mapValue) :
|
|
27777
|
+
getStyleMapInterpolationExpression(mapValue);
|
|
27778
|
+
}
|
|
27779
|
+
else {
|
|
27780
|
+
reference = isClassBased ? Identifiers.classMap : Identifiers.styleMap;
|
|
27781
|
+
}
|
|
27782
|
+
return {
|
|
27783
|
+
reference,
|
|
27784
|
+
calls: [{
|
|
27785
|
+
supportsInterpolation: true,
|
|
27786
|
+
sourceSpan: stylingInput.sourceSpan,
|
|
27787
|
+
allocateBindingSlots: totalBindingSlotsRequired,
|
|
27788
|
+
params: (convertFn) => {
|
|
27789
|
+
const convertResult = convertFn(mapValue);
|
|
27790
|
+
const params = Array.isArray(convertResult) ? convertResult : [convertResult];
|
|
27791
|
+
return params;
|
|
27792
|
+
}
|
|
27793
|
+
}]
|
|
27794
|
+
};
|
|
27795
|
+
}
|
|
27796
|
+
_buildSingleInputs(reference, inputs, valueConverter, getInterpolationExpressionFn, isClassBased) {
|
|
27797
|
+
const instructions = [];
|
|
27798
|
+
inputs.forEach(input => {
|
|
27799
|
+
const previousInstruction = instructions[instructions.length - 1];
|
|
27800
|
+
const value = input.value.visit(valueConverter);
|
|
27801
|
+
let referenceForCall = reference;
|
|
27802
|
+
// each styling binding value is stored in the LView
|
|
27803
|
+
// but there are two values stored for each binding:
|
|
27804
|
+
// 1) the value itself
|
|
27805
|
+
// 2) an intermediate value (concatenation of style up to this point).
|
|
27806
|
+
// We need to store the intermediate value so that we don't allocate
|
|
27807
|
+
// the strings on each CD.
|
|
27808
|
+
let totalBindingSlotsRequired = MIN_STYLING_BINDING_SLOTS_REQUIRED;
|
|
27809
|
+
if (value instanceof Interpolation$1) {
|
|
27810
|
+
totalBindingSlotsRequired += value.expressions.length;
|
|
27811
|
+
if (getInterpolationExpressionFn) {
|
|
27812
|
+
referenceForCall = getInterpolationExpressionFn(value);
|
|
27813
|
+
}
|
|
27814
|
+
}
|
|
27815
|
+
const call = {
|
|
27816
|
+
sourceSpan: input.sourceSpan,
|
|
27817
|
+
allocateBindingSlots: totalBindingSlotsRequired,
|
|
27818
|
+
supportsInterpolation: !!getInterpolationExpressionFn,
|
|
27819
|
+
params: (convertFn) => {
|
|
27820
|
+
// params => stylingProp(propName, value, suffix)
|
|
27821
|
+
const params = [];
|
|
27822
|
+
params.push(literal(input.name));
|
|
27823
|
+
const convertResult = convertFn(value);
|
|
27824
|
+
if (Array.isArray(convertResult)) {
|
|
27825
|
+
params.push(...convertResult);
|
|
27826
|
+
}
|
|
27827
|
+
else {
|
|
27828
|
+
params.push(convertResult);
|
|
27829
|
+
}
|
|
27830
|
+
// [style.prop] bindings may use suffix values (e.g. px, em, etc...), therefore,
|
|
27831
|
+
// if that is detected then we need to pass that in as an optional param.
|
|
27832
|
+
if (!isClassBased && input.suffix !== null) {
|
|
27833
|
+
params.push(literal(input.suffix));
|
|
27834
|
+
}
|
|
27835
|
+
return params;
|
|
27836
|
+
}
|
|
27837
|
+
};
|
|
27838
|
+
// If we ended up generating a call to the same instruction as the previous styling property
|
|
27839
|
+
// we can chain the calls together safely to save some bytes, otherwise we have to generate
|
|
27840
|
+
// a separate instruction call. This is primarily a concern with interpolation instructions
|
|
27841
|
+
// where we may start off with one `reference`, but end up using another based on the
|
|
27842
|
+
// number of interpolations.
|
|
27843
|
+
if (previousInstruction && previousInstruction.reference === referenceForCall) {
|
|
27844
|
+
previousInstruction.calls.push(call);
|
|
27845
|
+
}
|
|
27846
|
+
else {
|
|
27847
|
+
instructions.push({ reference: referenceForCall, calls: [call] });
|
|
27848
|
+
}
|
|
27849
|
+
});
|
|
27850
|
+
return instructions;
|
|
27851
|
+
}
|
|
27852
|
+
_buildClassInputs(valueConverter) {
|
|
27853
|
+
if (this._singleClassInputs) {
|
|
27854
|
+
return this._buildSingleInputs(Identifiers.classProp, this._singleClassInputs, valueConverter, null, true);
|
|
27855
|
+
}
|
|
27856
|
+
return [];
|
|
27857
|
+
}
|
|
27858
|
+
_buildStyleInputs(valueConverter) {
|
|
27859
|
+
if (this._singleStyleInputs) {
|
|
27860
|
+
return this._buildSingleInputs(Identifiers.styleProp, this._singleStyleInputs, valueConverter, getStylePropInterpolationExpression, false);
|
|
27861
|
+
}
|
|
27862
|
+
return [];
|
|
27863
|
+
}
|
|
27864
|
+
/**
|
|
27865
|
+
* Constructs all instructions which contain the expressions that will be placed
|
|
27866
|
+
* into the update block of a template function or a directive hostBindings function.
|
|
27867
|
+
*/
|
|
27868
|
+
buildUpdateLevelInstructions(valueConverter) {
|
|
27869
|
+
const instructions = [];
|
|
27870
|
+
if (this.hasBindings) {
|
|
27871
|
+
const styleMapInstruction = this.buildStyleMapInstruction(valueConverter);
|
|
27872
|
+
if (styleMapInstruction) {
|
|
27873
|
+
instructions.push(styleMapInstruction);
|
|
27874
|
+
}
|
|
27875
|
+
const classMapInstruction = this.buildClassMapInstruction(valueConverter);
|
|
27876
|
+
if (classMapInstruction) {
|
|
27877
|
+
instructions.push(classMapInstruction);
|
|
27878
|
+
}
|
|
27879
|
+
instructions.push(...this._buildStyleInputs(valueConverter));
|
|
27880
|
+
instructions.push(...this._buildClassInputs(valueConverter));
|
|
27881
|
+
}
|
|
27882
|
+
return instructions;
|
|
27883
|
+
}
|
|
27884
|
+
}
|
|
27885
|
+
function registerIntoMap(map, key) {
|
|
27886
|
+
if (!map.has(key)) {
|
|
27887
|
+
map.set(key, map.size);
|
|
27888
|
+
}
|
|
27889
|
+
}
|
|
27890
|
+
function parseProperty(name) {
|
|
27891
|
+
let hasOverrideFlag = false;
|
|
27892
|
+
const overrideIndex = name.indexOf(IMPORTANT_FLAG);
|
|
27893
|
+
if (overrideIndex !== -1) {
|
|
27894
|
+
name = overrideIndex > 0 ? name.substring(0, overrideIndex) : '';
|
|
27895
|
+
hasOverrideFlag = true;
|
|
27896
|
+
}
|
|
27897
|
+
let suffix = null;
|
|
27898
|
+
let property = name;
|
|
27899
|
+
const unitIndex = name.lastIndexOf('.');
|
|
27900
|
+
if (unitIndex > 0) {
|
|
27901
|
+
suffix = name.slice(unitIndex + 1);
|
|
27902
|
+
property = name.substring(0, unitIndex);
|
|
27903
|
+
}
|
|
27904
|
+
return { property, suffix, hasOverrideFlag };
|
|
27905
|
+
}
|
|
27906
|
+
/**
|
|
27907
|
+
* Gets the instruction to generate for an interpolated class map.
|
|
27908
|
+
* @param interpolation An Interpolation AST
|
|
27909
|
+
*/
|
|
27910
|
+
function getClassMapInterpolationExpression(interpolation) {
|
|
27911
|
+
switch (getInterpolationArgsLength(interpolation)) {
|
|
27912
|
+
case 1:
|
|
27913
|
+
return Identifiers.classMap;
|
|
27914
|
+
case 3:
|
|
27915
|
+
return Identifiers.classMapInterpolate1;
|
|
27916
|
+
case 5:
|
|
27917
|
+
return Identifiers.classMapInterpolate2;
|
|
27918
|
+
case 7:
|
|
27919
|
+
return Identifiers.classMapInterpolate3;
|
|
27920
|
+
case 9:
|
|
27921
|
+
return Identifiers.classMapInterpolate4;
|
|
27922
|
+
case 11:
|
|
27923
|
+
return Identifiers.classMapInterpolate5;
|
|
27924
|
+
case 13:
|
|
27925
|
+
return Identifiers.classMapInterpolate6;
|
|
27926
|
+
case 15:
|
|
27927
|
+
return Identifiers.classMapInterpolate7;
|
|
27928
|
+
case 17:
|
|
27929
|
+
return Identifiers.classMapInterpolate8;
|
|
27930
|
+
default:
|
|
27931
|
+
return Identifiers.classMapInterpolateV;
|
|
27932
|
+
}
|
|
27933
|
+
}
|
|
27934
|
+
/**
|
|
27935
|
+
* Gets the instruction to generate for an interpolated style map.
|
|
27936
|
+
* @param interpolation An Interpolation AST
|
|
27937
|
+
*/
|
|
27938
|
+
function getStyleMapInterpolationExpression(interpolation) {
|
|
27939
|
+
switch (getInterpolationArgsLength(interpolation)) {
|
|
27940
|
+
case 1:
|
|
27941
|
+
return Identifiers.styleMap;
|
|
27942
|
+
case 3:
|
|
27943
|
+
return Identifiers.styleMapInterpolate1;
|
|
27944
|
+
case 5:
|
|
27945
|
+
return Identifiers.styleMapInterpolate2;
|
|
27946
|
+
case 7:
|
|
27947
|
+
return Identifiers.styleMapInterpolate3;
|
|
27948
|
+
case 9:
|
|
27949
|
+
return Identifiers.styleMapInterpolate4;
|
|
27950
|
+
case 11:
|
|
27951
|
+
return Identifiers.styleMapInterpolate5;
|
|
27952
|
+
case 13:
|
|
27953
|
+
return Identifiers.styleMapInterpolate6;
|
|
27954
|
+
case 15:
|
|
27955
|
+
return Identifiers.styleMapInterpolate7;
|
|
27956
|
+
case 17:
|
|
27957
|
+
return Identifiers.styleMapInterpolate8;
|
|
27958
|
+
default:
|
|
27959
|
+
return Identifiers.styleMapInterpolateV;
|
|
27960
|
+
}
|
|
27961
|
+
}
|
|
27962
|
+
/**
|
|
27963
|
+
* Gets the instruction to generate for an interpolated style prop.
|
|
27964
|
+
* @param interpolation An Interpolation AST
|
|
27965
|
+
*/
|
|
27966
|
+
function getStylePropInterpolationExpression(interpolation) {
|
|
27967
|
+
switch (getInterpolationArgsLength(interpolation)) {
|
|
27968
|
+
case 1:
|
|
27969
|
+
return Identifiers.styleProp;
|
|
27970
|
+
case 3:
|
|
27971
|
+
return Identifiers.stylePropInterpolate1;
|
|
27972
|
+
case 5:
|
|
27973
|
+
return Identifiers.stylePropInterpolate2;
|
|
27974
|
+
case 7:
|
|
27975
|
+
return Identifiers.stylePropInterpolate3;
|
|
27976
|
+
case 9:
|
|
27977
|
+
return Identifiers.stylePropInterpolate4;
|
|
27978
|
+
case 11:
|
|
27979
|
+
return Identifiers.stylePropInterpolate5;
|
|
27980
|
+
case 13:
|
|
27981
|
+
return Identifiers.stylePropInterpolate6;
|
|
27982
|
+
case 15:
|
|
27983
|
+
return Identifiers.stylePropInterpolate7;
|
|
27984
|
+
case 17:
|
|
27985
|
+
return Identifiers.stylePropInterpolate8;
|
|
27986
|
+
default:
|
|
27987
|
+
return Identifiers.stylePropInterpolateV;
|
|
27988
|
+
}
|
|
27989
|
+
}
|
|
27990
|
+
/**
|
|
27991
|
+
* Checks whether property name is a custom CSS property.
|
|
27992
|
+
* See: https://www.w3.org/TR/css-variables-1
|
|
27993
|
+
*/
|
|
27994
|
+
function isCssCustomProperty(name) {
|
|
27995
|
+
return name.startsWith('--');
|
|
27996
|
+
}
|
|
27997
|
+
function isEmptyExpression(ast) {
|
|
27998
|
+
if (ast instanceof ASTWithSource) {
|
|
27999
|
+
ast = ast.ast;
|
|
28000
|
+
}
|
|
28001
|
+
return ast instanceof EmptyExpr$1;
|
|
28002
|
+
}
|
|
28003
|
+
|
|
26220
28004
|
// Selector attribute name of `<ng-content>`
|
|
26221
28005
|
const NG_CONTENT_SELECT_ATTR = 'select';
|
|
26222
28006
|
// Attribute name of `ngProjectAs`.
|
|
@@ -26243,7 +28027,9 @@ function prepareEventListenerParameters(eventAst, handlerName = null, scope = nu
|
|
|
26243
28027
|
const implicitReceiverExpr = (scope === null || scope.bindingLevel === 0) ?
|
|
26244
28028
|
variable(CONTEXT_NAME) :
|
|
26245
28029
|
scope.getOrCreateSharedContextVar(0);
|
|
26246
|
-
const bindingStatements =
|
|
28030
|
+
const bindingStatements = eventAst.type === 2 /* ParsedEventType.TwoWay */ ?
|
|
28031
|
+
convertAssignmentActionBinding(scope, implicitReceiverExpr, handler, 'b', eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS) :
|
|
28032
|
+
convertActionBinding(scope, implicitReceiverExpr, handler, 'b', eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS);
|
|
26247
28033
|
const statements = [];
|
|
26248
28034
|
const variableDeclarations = scope?.variableDeclarations();
|
|
26249
28035
|
const restoreViewStatement = scope?.restoreViewStatement();
|
|
@@ -26765,7 +28551,8 @@ class TemplateDefinitionBuilder {
|
|
|
26765
28551
|
element.inputs.forEach(input => {
|
|
26766
28552
|
const stylingInputWasSet = stylingBuilder.registerBoundInput(input);
|
|
26767
28553
|
if (!stylingInputWasSet) {
|
|
26768
|
-
if (input.type === 0 /* BindingType.Property */
|
|
28554
|
+
if ((input.type === 0 /* BindingType.Property */ || input.type === 5 /* BindingType.TwoWay */) &&
|
|
28555
|
+
input.i18n) {
|
|
26769
28556
|
boundI18nAttrs.push(input);
|
|
26770
28557
|
}
|
|
26771
28558
|
else {
|
|
@@ -26810,7 +28597,7 @@ class TemplateDefinitionBuilder {
|
|
|
26810
28597
|
// Generate Listeners (outputs)
|
|
26811
28598
|
if (element.outputs.length > 0) {
|
|
26812
28599
|
for (const outputAst of element.outputs) {
|
|
26813
|
-
this.creationInstruction(outputAst.sourceSpan, Identifiers.listener, this.prepareListenerParameter(element.name, outputAst, elementIndex));
|
|
28600
|
+
this.creationInstruction(outputAst.sourceSpan, outputAst.type === 2 /* ParsedEventType.TwoWay */ ? Identifiers.twoWayListener : Identifiers.listener, this.prepareListenerParameter(element.name, outputAst, elementIndex));
|
|
26814
28601
|
}
|
|
26815
28602
|
}
|
|
26816
28603
|
// Note: it's important to keep i18n/i18nStart instructions after i18nAttributes and
|
|
@@ -26853,6 +28640,7 @@ class TemplateDefinitionBuilder {
|
|
|
26853
28640
|
this.allocateBindingSlots(value);
|
|
26854
28641
|
propertyBindings.push({
|
|
26855
28642
|
span: input.sourceSpan,
|
|
28643
|
+
reference: Identifiers.property,
|
|
26856
28644
|
paramsOrFn: getBindingFunctionParams(() => hasValue ? this.convertPropertyBinding(value) : emptyValueBindInstruction, prepareSyntheticPropertyName(input.name))
|
|
26857
28645
|
});
|
|
26858
28646
|
}
|
|
@@ -26891,7 +28679,9 @@ class TemplateDefinitionBuilder {
|
|
|
26891
28679
|
}
|
|
26892
28680
|
}
|
|
26893
28681
|
this.allocateBindingSlots(value);
|
|
26894
|
-
|
|
28682
|
+
// Note: we don't separate two-way property bindings and regular ones,
|
|
28683
|
+
// because their assignment order needs to be maintained.
|
|
28684
|
+
if (inputType === 0 /* BindingType.Property */ || inputType === 5 /* BindingType.TwoWay */) {
|
|
26895
28685
|
if (value instanceof Interpolation$1) {
|
|
26896
28686
|
// prop="{{value}}" and friends
|
|
26897
28687
|
this.interpolatedUpdateInstruction(getPropertyInterpolationExpression(value), elementIndex, attrName, input, value, params);
|
|
@@ -26901,6 +28691,7 @@ class TemplateDefinitionBuilder {
|
|
|
26901
28691
|
// Collect all the properties so that we can chain into a single function at the end.
|
|
26902
28692
|
propertyBindings.push({
|
|
26903
28693
|
span: input.sourceSpan,
|
|
28694
|
+
reference: inputType === 5 /* BindingType.TwoWay */ ? Identifiers.twoWayProperty : Identifiers.property,
|
|
26904
28695
|
paramsOrFn: getBindingFunctionParams(() => this.convertPropertyBinding(value), attrName, params)
|
|
26905
28696
|
});
|
|
26906
28697
|
}
|
|
@@ -26933,7 +28724,7 @@ class TemplateDefinitionBuilder {
|
|
|
26933
28724
|
}
|
|
26934
28725
|
});
|
|
26935
28726
|
for (const propertyBinding of propertyBindings) {
|
|
26936
|
-
this.updateInstructionWithAdvance(elementIndex, propertyBinding.span,
|
|
28727
|
+
this.updateInstructionWithAdvance(elementIndex, propertyBinding.span, propertyBinding.reference, propertyBinding.paramsOrFn);
|
|
26937
28728
|
}
|
|
26938
28729
|
for (const attributeBinding of attributeBindings) {
|
|
26939
28730
|
this.updateInstructionWithAdvance(elementIndex, attributeBinding.span, Identifiers.attribute, attributeBinding.paramsOrFn);
|
|
@@ -26966,7 +28757,9 @@ class TemplateDefinitionBuilder {
|
|
|
26966
28757
|
}
|
|
26967
28758
|
}
|
|
26968
28759
|
const contextName = `${this.contextName}${contextNameSuffix}_${index}`;
|
|
26969
|
-
|
|
28760
|
+
// Note: For the unique name, we don't include an unique suffix, unless really needed.
|
|
28761
|
+
// This keeps the generated output more clean as most of the time, we don't expect conflicts.
|
|
28762
|
+
const name = this.constantPool.uniqueName(`${contextName}_Template`, /** alwaysIncludeSuffix */ false);
|
|
26970
28763
|
// Create the template function
|
|
26971
28764
|
const visitor = new TemplateDefinitionBuilder(this.constantPool, this._bindingScope, this.level + 1, contextName, this.i18n, index, name, this._namespace, this.fileBasedI18nSuffix, this.i18nUseExternalIds, this.deferBlocks, this.elementLocations, this.allDeferrableDepsFn, this._constants);
|
|
26972
28765
|
// Nested templates must not be visited until after their parent templates have completed
|
|
@@ -27029,7 +28822,7 @@ class TemplateDefinitionBuilder {
|
|
|
27029
28822
|
}
|
|
27030
28823
|
// Generate listeners for directive output
|
|
27031
28824
|
for (const outputAst of template.outputs) {
|
|
27032
|
-
this.creationInstruction(outputAst.sourceSpan, Identifiers.listener, this.prepareListenerParameter('ng_template', outputAst, templateIndex));
|
|
28825
|
+
this.creationInstruction(outputAst.sourceSpan, outputAst.type === 2 /* ParsedEventType.TwoWay */ ? Identifiers.twoWayListener : Identifiers.listener, this.prepareListenerParameter('ng_template', outputAst, templateIndex));
|
|
27033
28826
|
}
|
|
27034
28827
|
}
|
|
27035
28828
|
}
|
|
@@ -27283,7 +29076,9 @@ class TemplateDefinitionBuilder {
|
|
|
27283
29076
|
for (const deferredDep of metadata.deps) {
|
|
27284
29077
|
if (deferredDep.isDeferrable) {
|
|
27285
29078
|
// Callback function, e.g. `m () => m.MyCmp;`.
|
|
27286
|
-
const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)],
|
|
29079
|
+
const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)],
|
|
29080
|
+
// Default imports are always accessed through the `default` property.
|
|
29081
|
+
variable('m').prop(deferredDep.isDefaultImport ? 'default' : deferredDep.symbolName));
|
|
27287
29082
|
// Dynamic import, e.g. `import('./a').then(...)`.
|
|
27288
29083
|
const importExpr = (new DynamicImportExpr(deferredDep.importPath)).prop('then').callFn([innerFn]);
|
|
27289
29084
|
dependencyExp.push(importExpr);
|
|
@@ -28525,6 +30320,171 @@ function createClosureModeGuard() {
|
|
|
28525
30320
|
.and(variable(NG_I18N_CLOSURE_MODE));
|
|
28526
30321
|
}
|
|
28527
30322
|
|
|
30323
|
+
/**
|
|
30324
|
+
* Translates query flags into `TQueryFlags` type in
|
|
30325
|
+
* packages/core/src/render3/interfaces/query.ts
|
|
30326
|
+
* @param query
|
|
30327
|
+
*/
|
|
30328
|
+
function toQueryFlags(query) {
|
|
30329
|
+
return ((query.descendants ? 1 /* QueryFlags.descendants */ : 0 /* QueryFlags.none */) |
|
|
30330
|
+
(query.static ? 2 /* QueryFlags.isStatic */ : 0 /* QueryFlags.none */) |
|
|
30331
|
+
(query.emitDistinctChangesOnly ? 4 /* QueryFlags.emitDistinctChangesOnly */ : 0 /* QueryFlags.none */));
|
|
30332
|
+
}
|
|
30333
|
+
function getQueryPredicate(query, constantPool) {
|
|
30334
|
+
if (Array.isArray(query.predicate)) {
|
|
30335
|
+
let predicate = [];
|
|
30336
|
+
query.predicate.forEach((selector) => {
|
|
30337
|
+
// Each item in predicates array may contain strings with comma-separated refs
|
|
30338
|
+
// (for ex. 'ref, ref1, ..., refN'), thus we extract individual refs and store them
|
|
30339
|
+
// as separate array entities
|
|
30340
|
+
const selectors = selector.split(',').map((token) => literal(token.trim()));
|
|
30341
|
+
predicate.push(...selectors);
|
|
30342
|
+
});
|
|
30343
|
+
return constantPool.getConstLiteral(literalArr(predicate), true);
|
|
30344
|
+
}
|
|
30345
|
+
else {
|
|
30346
|
+
// The original predicate may have been wrapped in a `forwardRef()` call.
|
|
30347
|
+
switch (query.predicate.forwardRef) {
|
|
30348
|
+
case 0 /* ForwardRefHandling.None */:
|
|
30349
|
+
case 2 /* ForwardRefHandling.Unwrapped */:
|
|
30350
|
+
return query.predicate.expression;
|
|
30351
|
+
case 1 /* ForwardRefHandling.Wrapped */:
|
|
30352
|
+
return importExpr(Identifiers.resolveForwardRef).callFn([query.predicate.expression]);
|
|
30353
|
+
}
|
|
30354
|
+
}
|
|
30355
|
+
}
|
|
30356
|
+
function createQueryCreateCall(query, constantPool, queryTypeFns, prependParams) {
|
|
30357
|
+
const parameters = [];
|
|
30358
|
+
if (prependParams !== undefined) {
|
|
30359
|
+
parameters.push(...prependParams);
|
|
30360
|
+
}
|
|
30361
|
+
if (query.isSignal) {
|
|
30362
|
+
parameters.push(new ReadPropExpr(variable(CONTEXT_NAME), query.propertyName));
|
|
30363
|
+
}
|
|
30364
|
+
parameters.push(getQueryPredicate(query, constantPool), literal(toQueryFlags(query)));
|
|
30365
|
+
if (query.read) {
|
|
30366
|
+
parameters.push(query.read);
|
|
30367
|
+
}
|
|
30368
|
+
const queryCreateFn = query.isSignal ? queryTypeFns.signalBased : queryTypeFns.nonSignal;
|
|
30369
|
+
return importExpr(queryCreateFn).callFn(parameters);
|
|
30370
|
+
}
|
|
30371
|
+
const queryAdvancePlaceholder = Symbol('queryAdvancePlaceholder');
|
|
30372
|
+
/**
|
|
30373
|
+
* Collapses query advance placeholders in a list of statements.
|
|
30374
|
+
*
|
|
30375
|
+
* This allows for less generated code because multiple sibling query advance
|
|
30376
|
+
* statements can be collapsed into a single call with the count as argument.
|
|
30377
|
+
*
|
|
30378
|
+
* e.g.
|
|
30379
|
+
*
|
|
30380
|
+
* ```ts
|
|
30381
|
+
* bla();
|
|
30382
|
+
* queryAdvance();
|
|
30383
|
+
* queryAdvance();
|
|
30384
|
+
* bla();
|
|
30385
|
+
* ```
|
|
30386
|
+
*
|
|
30387
|
+
* --> will turn into
|
|
30388
|
+
*
|
|
30389
|
+
* ```
|
|
30390
|
+
* bla();
|
|
30391
|
+
* queryAdvance(2);
|
|
30392
|
+
* bla();
|
|
30393
|
+
* ```
|
|
30394
|
+
*/
|
|
30395
|
+
function collapseAdvanceStatements(statements) {
|
|
30396
|
+
const result = [];
|
|
30397
|
+
let advanceCollapseCount = 0;
|
|
30398
|
+
const flushAdvanceCount = () => {
|
|
30399
|
+
if (advanceCollapseCount > 0) {
|
|
30400
|
+
result.unshift(importExpr(Identifiers.queryAdvance)
|
|
30401
|
+
.callFn(advanceCollapseCount === 1 ? [] : [literal(advanceCollapseCount)])
|
|
30402
|
+
.toStmt());
|
|
30403
|
+
advanceCollapseCount = 0;
|
|
30404
|
+
}
|
|
30405
|
+
};
|
|
30406
|
+
// Iterate through statements in reverse and collapse advance placeholders.
|
|
30407
|
+
for (let i = statements.length - 1; i >= 0; i--) {
|
|
30408
|
+
const st = statements[i];
|
|
30409
|
+
if (st === queryAdvancePlaceholder) {
|
|
30410
|
+
advanceCollapseCount++;
|
|
30411
|
+
}
|
|
30412
|
+
else {
|
|
30413
|
+
flushAdvanceCount();
|
|
30414
|
+
result.unshift(st);
|
|
30415
|
+
}
|
|
30416
|
+
}
|
|
30417
|
+
flushAdvanceCount();
|
|
30418
|
+
return result;
|
|
30419
|
+
}
|
|
30420
|
+
// Define and update any view queries
|
|
30421
|
+
function createViewQueriesFunction(viewQueries, constantPool, name) {
|
|
30422
|
+
const createStatements = [];
|
|
30423
|
+
const updateStatements = [];
|
|
30424
|
+
const tempAllocator = temporaryAllocator(st => updateStatements.push(st), TEMPORARY_NAME);
|
|
30425
|
+
viewQueries.forEach((query) => {
|
|
30426
|
+
// creation call, e.g. r3.viewQuery(somePredicate, true) or
|
|
30427
|
+
// r3.viewQuerySignal(ctx.prop, somePredicate, true);
|
|
30428
|
+
const queryDefinitionCall = createQueryCreateCall(query, constantPool, {
|
|
30429
|
+
signalBased: Identifiers.viewQuerySignal,
|
|
30430
|
+
nonSignal: Identifiers.viewQuery,
|
|
30431
|
+
});
|
|
30432
|
+
createStatements.push(queryDefinitionCall.toStmt());
|
|
30433
|
+
// Signal queries update lazily and we just advance the index.
|
|
30434
|
+
if (query.isSignal) {
|
|
30435
|
+
updateStatements.push(queryAdvancePlaceholder);
|
|
30436
|
+
return;
|
|
30437
|
+
}
|
|
30438
|
+
// update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
|
|
30439
|
+
const temporary = tempAllocator();
|
|
30440
|
+
const getQueryList = importExpr(Identifiers.loadQuery).callFn([]);
|
|
30441
|
+
const refresh = importExpr(Identifiers.queryRefresh).callFn([temporary.set(getQueryList)]);
|
|
30442
|
+
const updateDirective = variable(CONTEXT_NAME)
|
|
30443
|
+
.prop(query.propertyName)
|
|
30444
|
+
.set(query.first ? temporary.prop('first') : temporary);
|
|
30445
|
+
updateStatements.push(refresh.and(updateDirective).toStmt());
|
|
30446
|
+
});
|
|
30447
|
+
const viewQueryFnName = name ? `${name}_Query` : null;
|
|
30448
|
+
return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], [
|
|
30449
|
+
renderFlagCheckIfStmt(1 /* core.RenderFlags.Create */, createStatements),
|
|
30450
|
+
renderFlagCheckIfStmt(2 /* core.RenderFlags.Update */, collapseAdvanceStatements(updateStatements))
|
|
30451
|
+
], INFERRED_TYPE, null, viewQueryFnName);
|
|
30452
|
+
}
|
|
30453
|
+
// Define and update any content queries
|
|
30454
|
+
function createContentQueriesFunction(queries, constantPool, name) {
|
|
30455
|
+
const createStatements = [];
|
|
30456
|
+
const updateStatements = [];
|
|
30457
|
+
const tempAllocator = temporaryAllocator(st => updateStatements.push(st), TEMPORARY_NAME);
|
|
30458
|
+
for (const query of queries) {
|
|
30459
|
+
// creation, e.g. r3.contentQuery(dirIndex, somePredicate, true, null) or
|
|
30460
|
+
// r3.contentQuerySignal(dirIndex, propName, somePredicate, <flags>, <read>).
|
|
30461
|
+
createStatements.push(createQueryCreateCall(query, constantPool, { nonSignal: Identifiers.contentQuery, signalBased: Identifiers.contentQuerySignal },
|
|
30462
|
+
/* prependParams */ [variable('dirIndex')])
|
|
30463
|
+
.toStmt());
|
|
30464
|
+
// Signal queries update lazily and we just advance the index.
|
|
30465
|
+
if (query.isSignal) {
|
|
30466
|
+
updateStatements.push(queryAdvancePlaceholder);
|
|
30467
|
+
continue;
|
|
30468
|
+
}
|
|
30469
|
+
// update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
|
|
30470
|
+
const temporary = tempAllocator();
|
|
30471
|
+
const getQueryList = importExpr(Identifiers.loadQuery).callFn([]);
|
|
30472
|
+
const refresh = importExpr(Identifiers.queryRefresh).callFn([temporary.set(getQueryList)]);
|
|
30473
|
+
const updateDirective = variable(CONTEXT_NAME)
|
|
30474
|
+
.prop(query.propertyName)
|
|
30475
|
+
.set(query.first ? temporary.prop('first') : temporary);
|
|
30476
|
+
updateStatements.push(refresh.and(updateDirective).toStmt());
|
|
30477
|
+
}
|
|
30478
|
+
const contentQueriesFnName = name ? `${name}_ContentQueries` : null;
|
|
30479
|
+
return fn([
|
|
30480
|
+
new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null),
|
|
30481
|
+
new FnParam('dirIndex', null)
|
|
30482
|
+
], [
|
|
30483
|
+
renderFlagCheckIfStmt(1 /* core.RenderFlags.Create */, createStatements),
|
|
30484
|
+
renderFlagCheckIfStmt(2 /* core.RenderFlags.Update */, collapseAdvanceStatements(updateStatements)),
|
|
30485
|
+
], INFERRED_TYPE, null, contentQueriesFnName);
|
|
30486
|
+
}
|
|
30487
|
+
|
|
28528
30488
|
// This regex matches any binding names that contain the "attr." prefix, e.g. "attr.required"
|
|
28529
30489
|
// If there is a match, the first matching group will contain the attribute name to bind.
|
|
28530
30490
|
const ATTR_REGEX = /attr\.([^\]]+)/;
|
|
@@ -28625,9 +30585,9 @@ function compileDirectiveFromMetadata(meta, constantPool, bindingParser) {
|
|
|
28625
30585
|
function createDeferredDepsFunction(constantPool, name, deps) {
|
|
28626
30586
|
// This defer block has deps for which we need to generate dynamic imports.
|
|
28627
30587
|
const dependencyExp = [];
|
|
28628
|
-
for (const [symbolName, importPath] of deps) {
|
|
30588
|
+
for (const [symbolName, { importPath, isDefaultImport }] of deps) {
|
|
28629
30589
|
// Callback function, e.g. `m () => m.MyCmp;`.
|
|
28630
|
-
const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(symbolName));
|
|
30590
|
+
const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(isDefaultImport ? 'default' : symbolName));
|
|
28631
30591
|
// Dynamic import, e.g. `import('./a').then(...)`.
|
|
28632
30592
|
const importExpr = (new DynamicImportExpr(importPath)).prop('then').callFn([innerFn]);
|
|
28633
30593
|
dependencyExp.push(importExpr);
|
|
@@ -28656,16 +30616,16 @@ function compileComponentFromMetadata(meta, constantPool, bindingParser) {
|
|
|
28656
30616
|
// e.g. `template: function MyComponent_Template(_ctx, _cm) {...}`
|
|
28657
30617
|
const templateTypeName = meta.name;
|
|
28658
30618
|
const templateName = templateTypeName ? `${templateTypeName}_Template` : null;
|
|
30619
|
+
let allDeferrableDepsFn = null;
|
|
30620
|
+
if (meta.deferBlocks.size > 0 && meta.deferrableTypes.size > 0 &&
|
|
30621
|
+
meta.deferBlockDepsEmitMode === 1 /* DeferBlockDepsEmitMode.PerComponent */) {
|
|
30622
|
+
const fnName = `${templateTypeName}_DeferFn`;
|
|
30623
|
+
allDeferrableDepsFn = createDeferredDepsFunction(constantPool, fnName, meta.deferrableTypes);
|
|
30624
|
+
}
|
|
28659
30625
|
// Template compilation is currently conditional as we're in the process of rewriting it.
|
|
28660
|
-
{
|
|
30626
|
+
if (!meta.useTemplatePipeline) {
|
|
28661
30627
|
// This is the main path currently used in compilation, which compiles the template with the
|
|
28662
30628
|
// legacy `TemplateDefinitionBuilder`.
|
|
28663
|
-
let allDeferrableDepsFn = null;
|
|
28664
|
-
if (meta.deferBlocks.size > 0 && meta.deferrableTypes.size > 0 &&
|
|
28665
|
-
meta.deferBlockDepsEmitMode === 1 /* DeferBlockDepsEmitMode.PerComponent */) {
|
|
28666
|
-
const fnName = `${templateTypeName}_DeferFn`;
|
|
28667
|
-
allDeferrableDepsFn = createDeferredDepsFunction(constantPool, fnName, meta.deferrableTypes);
|
|
28668
|
-
}
|
|
28669
30629
|
const template = meta.template;
|
|
28670
30630
|
const templateBuilder = new TemplateDefinitionBuilder(constantPool, BindingScope.createRootScope(), 0, templateTypeName, null, null, templateName, Identifiers.namespaceHTML, meta.relativeContextFilePath, meta.i18nUseExternalIds, meta.deferBlocks, new Map(), allDeferrableDepsFn);
|
|
28671
30631
|
const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []);
|
|
@@ -28699,6 +30659,31 @@ function compileComponentFromMetadata(meta, constantPool, bindingParser) {
|
|
|
28699
30659
|
}
|
|
28700
30660
|
definitionMap.set('template', templateFunctionExpression);
|
|
28701
30661
|
}
|
|
30662
|
+
else {
|
|
30663
|
+
// This path compiles the template using the prototype template pipeline. First the template is
|
|
30664
|
+
// ingested into IR:
|
|
30665
|
+
const tpl = ingestComponent(meta.name, meta.template.nodes, constantPool, meta.relativeContextFilePath, meta.i18nUseExternalIds, meta.deferBlocks, allDeferrableDepsFn);
|
|
30666
|
+
// Then the IR is transformed to prepare it for cod egeneration.
|
|
30667
|
+
transform(tpl, CompilationJobKind.Tmpl);
|
|
30668
|
+
// Finally we emit the template function:
|
|
30669
|
+
const templateFn = emitTemplateFn(tpl, constantPool);
|
|
30670
|
+
if (tpl.contentSelectors !== null) {
|
|
30671
|
+
definitionMap.set('ngContentSelectors', tpl.contentSelectors);
|
|
30672
|
+
}
|
|
30673
|
+
definitionMap.set('decls', literal(tpl.root.decls));
|
|
30674
|
+
definitionMap.set('vars', literal(tpl.root.vars));
|
|
30675
|
+
if (tpl.consts.length > 0) {
|
|
30676
|
+
if (tpl.constsInitializers.length > 0) {
|
|
30677
|
+
definitionMap.set('consts', arrowFn([], [
|
|
30678
|
+
...tpl.constsInitializers, new ReturnStatement(literalArr(tpl.consts))
|
|
30679
|
+
]));
|
|
30680
|
+
}
|
|
30681
|
+
else {
|
|
30682
|
+
definitionMap.set('consts', literalArr(tpl.consts));
|
|
30683
|
+
}
|
|
30684
|
+
}
|
|
30685
|
+
definitionMap.set('template', templateFn);
|
|
30686
|
+
}
|
|
28702
30687
|
if (meta.declarationListEmitMode !== 3 /* DeclarationListEmitMode.RuntimeResolved */ &&
|
|
28703
30688
|
meta.declarations.length > 0) {
|
|
28704
30689
|
definitionMap.set('dependencies', compileDeclarationList(literalArr(meta.declarations.map(decl => decl.type)), meta.declarationListEmitMode));
|
|
@@ -28794,22 +30779,6 @@ function compileDeclarationList(list, mode) {
|
|
|
28794
30779
|
throw new Error(`Unsupported with an array of pre-resolved dependencies`);
|
|
28795
30780
|
}
|
|
28796
30781
|
}
|
|
28797
|
-
function prepareQueryParams(query, constantPool) {
|
|
28798
|
-
const parameters = [getQueryPredicate(query, constantPool), literal(toQueryFlags(query))];
|
|
28799
|
-
if (query.read) {
|
|
28800
|
-
parameters.push(query.read);
|
|
28801
|
-
}
|
|
28802
|
-
return parameters;
|
|
28803
|
-
}
|
|
28804
|
-
/**
|
|
28805
|
-
* Translates query flags into `TQueryFlags` type in packages/core/src/render3/interfaces/query.ts
|
|
28806
|
-
* @param query
|
|
28807
|
-
*/
|
|
28808
|
-
function toQueryFlags(query) {
|
|
28809
|
-
return (query.descendants ? 1 /* QueryFlags.descendants */ : 0 /* QueryFlags.none */) |
|
|
28810
|
-
(query.static ? 2 /* QueryFlags.isStatic */ : 0 /* QueryFlags.none */) |
|
|
28811
|
-
(query.emitDistinctChangesOnly ? 4 /* QueryFlags.emitDistinctChangesOnly */ : 0 /* QueryFlags.none */);
|
|
28812
|
-
}
|
|
28813
30782
|
function convertAttributesToExpressions(attributes) {
|
|
28814
30783
|
const values = [];
|
|
28815
30784
|
for (let key of Object.getOwnPropertyNames(attributes)) {
|
|
@@ -28818,34 +30787,6 @@ function convertAttributesToExpressions(attributes) {
|
|
|
28818
30787
|
}
|
|
28819
30788
|
return values;
|
|
28820
30789
|
}
|
|
28821
|
-
// Define and update any content queries
|
|
28822
|
-
function createContentQueriesFunction(queries, constantPool, name) {
|
|
28823
|
-
const createStatements = [];
|
|
28824
|
-
const updateStatements = [];
|
|
28825
|
-
const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
|
|
28826
|
-
for (const query of queries) {
|
|
28827
|
-
// creation, e.g. r3.contentQuery(dirIndex, somePredicate, true, null);
|
|
28828
|
-
createStatements.push(importExpr(Identifiers.contentQuery)
|
|
28829
|
-
.callFn([variable('dirIndex'), ...prepareQueryParams(query, constantPool)])
|
|
28830
|
-
.toStmt());
|
|
28831
|
-
// update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
|
|
28832
|
-
const temporary = tempAllocator();
|
|
28833
|
-
const getQueryList = importExpr(Identifiers.loadQuery).callFn([]);
|
|
28834
|
-
const refresh = importExpr(Identifiers.queryRefresh).callFn([temporary.set(getQueryList)]);
|
|
28835
|
-
const updateDirective = variable(CONTEXT_NAME)
|
|
28836
|
-
.prop(query.propertyName)
|
|
28837
|
-
.set(query.first ? temporary.prop('first') : temporary);
|
|
28838
|
-
updateStatements.push(refresh.and(updateDirective).toStmt());
|
|
28839
|
-
}
|
|
28840
|
-
const contentQueriesFnName = name ? `${name}_ContentQueries` : null;
|
|
28841
|
-
return fn([
|
|
28842
|
-
new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null),
|
|
28843
|
-
new FnParam('dirIndex', null)
|
|
28844
|
-
], [
|
|
28845
|
-
renderFlagCheckIfStmt(1 /* core.RenderFlags.Create */, createStatements),
|
|
28846
|
-
renderFlagCheckIfStmt(2 /* core.RenderFlags.Update */, updateStatements)
|
|
28847
|
-
], INFERRED_TYPE, null, contentQueriesFnName);
|
|
28848
|
-
}
|
|
28849
30790
|
function stringAsType(str) {
|
|
28850
30791
|
return expressionType(literal(str));
|
|
28851
30792
|
}
|
|
@@ -28911,35 +30852,39 @@ function createDirectiveType(meta) {
|
|
|
28911
30852
|
}
|
|
28912
30853
|
return expressionType(importExpr(Identifiers.DirectiveDeclaration, typeParams));
|
|
28913
30854
|
}
|
|
28914
|
-
// Define and update any view queries
|
|
28915
|
-
function createViewQueriesFunction(viewQueries, constantPool, name) {
|
|
28916
|
-
const createStatements = [];
|
|
28917
|
-
const updateStatements = [];
|
|
28918
|
-
const tempAllocator = temporaryAllocator(updateStatements, TEMPORARY_NAME);
|
|
28919
|
-
viewQueries.forEach((query) => {
|
|
28920
|
-
// creation, e.g. r3.viewQuery(somePredicate, true);
|
|
28921
|
-
const queryDefinition = importExpr(Identifiers.viewQuery).callFn(prepareQueryParams(query, constantPool));
|
|
28922
|
-
createStatements.push(queryDefinition.toStmt());
|
|
28923
|
-
// update, e.g. (r3.queryRefresh(tmp = r3.loadQuery()) && (ctx.someDir = tmp));
|
|
28924
|
-
const temporary = tempAllocator();
|
|
28925
|
-
const getQueryList = importExpr(Identifiers.loadQuery).callFn([]);
|
|
28926
|
-
const refresh = importExpr(Identifiers.queryRefresh).callFn([temporary.set(getQueryList)]);
|
|
28927
|
-
const updateDirective = variable(CONTEXT_NAME)
|
|
28928
|
-
.prop(query.propertyName)
|
|
28929
|
-
.set(query.first ? temporary.prop('first') : temporary);
|
|
28930
|
-
updateStatements.push(refresh.and(updateDirective).toStmt());
|
|
28931
|
-
});
|
|
28932
|
-
const viewQueryFnName = name ? `${name}_Query` : null;
|
|
28933
|
-
return fn([new FnParam(RENDER_FLAGS, NUMBER_TYPE), new FnParam(CONTEXT_NAME, null)], [
|
|
28934
|
-
renderFlagCheckIfStmt(1 /* core.RenderFlags.Create */, createStatements),
|
|
28935
|
-
renderFlagCheckIfStmt(2 /* core.RenderFlags.Update */, updateStatements)
|
|
28936
|
-
], INFERRED_TYPE, null, viewQueryFnName);
|
|
28937
|
-
}
|
|
28938
30855
|
// Return a host binding function or null if one is not necessary.
|
|
28939
30856
|
function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindingParser, constantPool, selector, name, definitionMap) {
|
|
28940
30857
|
const bindings = bindingParser.createBoundHostProperties(hostBindingsMetadata.properties, typeSourceSpan);
|
|
28941
30858
|
// Calculate host event bindings
|
|
28942
30859
|
const eventBindings = bindingParser.createDirectiveHostEventAsts(hostBindingsMetadata.listeners, typeSourceSpan);
|
|
30860
|
+
if (hostBindingsMetadata.useTemplatePipeline) {
|
|
30861
|
+
// The parser for host bindings treats class and style attributes specially -- they are
|
|
30862
|
+
// extracted into these separate fields. This is not the case for templates, so the compiler can
|
|
30863
|
+
// actually already handle these special attributes internally. Therefore, we just drop them
|
|
30864
|
+
// into the attributes map.
|
|
30865
|
+
if (hostBindingsMetadata.specialAttributes.styleAttr) {
|
|
30866
|
+
hostBindingsMetadata.attributes['style'] =
|
|
30867
|
+
literal(hostBindingsMetadata.specialAttributes.styleAttr);
|
|
30868
|
+
}
|
|
30869
|
+
if (hostBindingsMetadata.specialAttributes.classAttr) {
|
|
30870
|
+
hostBindingsMetadata.attributes['class'] =
|
|
30871
|
+
literal(hostBindingsMetadata.specialAttributes.classAttr);
|
|
30872
|
+
}
|
|
30873
|
+
const hostJob = ingestHostBinding({
|
|
30874
|
+
componentName: name,
|
|
30875
|
+
componentSelector: selector,
|
|
30876
|
+
properties: bindings,
|
|
30877
|
+
events: eventBindings,
|
|
30878
|
+
attributes: hostBindingsMetadata.attributes,
|
|
30879
|
+
}, bindingParser, constantPool);
|
|
30880
|
+
transform(hostJob, CompilationJobKind.Host);
|
|
30881
|
+
definitionMap.set('hostAttrs', hostJob.root.attributes);
|
|
30882
|
+
const varCount = hostJob.root.vars;
|
|
30883
|
+
if (varCount !== null && varCount > 0) {
|
|
30884
|
+
definitionMap.set('hostVars', literal(varCount));
|
|
30885
|
+
}
|
|
30886
|
+
return emitHostBindingFunction(hostJob);
|
|
30887
|
+
}
|
|
28943
30888
|
let bindingId = 0;
|
|
28944
30889
|
const getNextBindingId = () => `${bindingId++}`;
|
|
28945
30890
|
const bindingContext = variable(CONTEXT_NAME);
|
|
@@ -29345,7 +31290,7 @@ class R3TargetBinder {
|
|
|
29345
31290
|
// Finally, run the TemplateBinder to bind references, variables, and other entities within the
|
|
29346
31291
|
// template. This extracts all the metadata that doesn't depend on directive matching.
|
|
29347
31292
|
const { expressions, symbols, nestingLevel, usedPipes, eagerPipes, deferBlocks } = TemplateBinder.applyWithScope(target.template, scope);
|
|
29348
|
-
return new R3BoundTarget(target, directives, eagerDirectives, bindings, references, expressions, symbols, nestingLevel, scopedNodeEntities, usedPipes, eagerPipes, deferBlocks
|
|
31293
|
+
return new R3BoundTarget(target, directives, eagerDirectives, bindings, references, expressions, symbols, nestingLevel, scopedNodeEntities, usedPipes, eagerPipes, deferBlocks);
|
|
29349
31294
|
}
|
|
29350
31295
|
}
|
|
29351
31296
|
/**
|
|
@@ -29741,7 +31686,7 @@ class TemplateBinder extends RecursiveAstVisitor {
|
|
|
29741
31686
|
const usedPipes = new Set();
|
|
29742
31687
|
const eagerPipes = new Set();
|
|
29743
31688
|
const template = nodes instanceof Template ? nodes : null;
|
|
29744
|
-
const deferBlocks = new
|
|
31689
|
+
const deferBlocks = new Map();
|
|
29745
31690
|
// The top-level template has nesting level 0.
|
|
29746
31691
|
const binder = new TemplateBinder(expressions, symbols, usedPipes, eagerPipes, deferBlocks, nestingLevel, scope, template, 0);
|
|
29747
31692
|
binder.ingest(nodes);
|
|
@@ -29770,8 +31715,16 @@ class TemplateBinder extends RecursiveAstVisitor {
|
|
|
29770
31715
|
nodeOrNodes.children.forEach(this.visitNode);
|
|
29771
31716
|
this.nestingLevel.set(nodeOrNodes, this.level);
|
|
29772
31717
|
}
|
|
31718
|
+
else if (nodeOrNodes instanceof DeferredBlock) {
|
|
31719
|
+
if (this.scope.rootNode !== nodeOrNodes) {
|
|
31720
|
+
throw new Error(`Assertion error: resolved incorrect scope for deferred block ${nodeOrNodes}`);
|
|
31721
|
+
}
|
|
31722
|
+
this.deferBlocks.set(nodeOrNodes, this.scope);
|
|
31723
|
+
nodeOrNodes.children.forEach(node => node.visit(this));
|
|
31724
|
+
this.nestingLevel.set(nodeOrNodes, this.level);
|
|
31725
|
+
}
|
|
29773
31726
|
else if (nodeOrNodes instanceof SwitchBlockCase || nodeOrNodes instanceof ForLoopBlockEmpty ||
|
|
29774
|
-
nodeOrNodes instanceof
|
|
31727
|
+
nodeOrNodes instanceof DeferredBlockError ||
|
|
29775
31728
|
nodeOrNodes instanceof DeferredBlockPlaceholder ||
|
|
29776
31729
|
nodeOrNodes instanceof DeferredBlockLoading) {
|
|
29777
31730
|
nodeOrNodes.children.forEach(node => node.visit(this));
|
|
@@ -29828,7 +31781,6 @@ class TemplateBinder extends RecursiveAstVisitor {
|
|
|
29828
31781
|
event.handler.visit(this);
|
|
29829
31782
|
}
|
|
29830
31783
|
visitDeferredBlock(deferred) {
|
|
29831
|
-
this.deferBlocks.add(deferred);
|
|
29832
31784
|
this.ingestScopedNode(deferred);
|
|
29833
31785
|
deferred.triggers.when?.value.visit(this);
|
|
29834
31786
|
deferred.prefetchTriggers.when?.value.visit(this);
|
|
@@ -29917,7 +31869,7 @@ class TemplateBinder extends RecursiveAstVisitor {
|
|
|
29917
31869
|
* See `BoundTarget` for documentation on the individual methods.
|
|
29918
31870
|
*/
|
|
29919
31871
|
class R3BoundTarget {
|
|
29920
|
-
constructor(target, directives, eagerDirectives, bindings, references, exprTargets, symbols, nestingLevel, scopedNodeEntities, usedPipes, eagerPipes,
|
|
31872
|
+
constructor(target, directives, eagerDirectives, bindings, references, exprTargets, symbols, nestingLevel, scopedNodeEntities, usedPipes, eagerPipes, deferBlocks) {
|
|
29921
31873
|
this.target = target;
|
|
29922
31874
|
this.directives = directives;
|
|
29923
31875
|
this.eagerDirectives = eagerDirectives;
|
|
@@ -29929,8 +31881,7 @@ class R3BoundTarget {
|
|
|
29929
31881
|
this.scopedNodeEntities = scopedNodeEntities;
|
|
29930
31882
|
this.usedPipes = usedPipes;
|
|
29931
31883
|
this.eagerPipes = eagerPipes;
|
|
29932
|
-
this.
|
|
29933
|
-
this.rootScope = rootScope;
|
|
31884
|
+
this.deferBlocks = deferBlocks;
|
|
29934
31885
|
}
|
|
29935
31886
|
getEntitiesInScope(node) {
|
|
29936
31887
|
return this.scopedNodeEntities.get(node) ?? new Set();
|
|
@@ -29969,7 +31920,7 @@ class R3BoundTarget {
|
|
|
29969
31920
|
return Array.from(this.eagerPipes);
|
|
29970
31921
|
}
|
|
29971
31922
|
getDeferBlocks() {
|
|
29972
|
-
return Array.from(this.
|
|
31923
|
+
return Array.from(this.deferBlocks.keys());
|
|
29973
31924
|
}
|
|
29974
31925
|
getDeferredTriggerTarget(block, trigger) {
|
|
29975
31926
|
// Only triggers that refer to DOM nodes can be resolved.
|
|
@@ -30021,10 +31972,14 @@ class R3BoundTarget {
|
|
|
30021
31972
|
return null;
|
|
30022
31973
|
}
|
|
30023
31974
|
isDeferred(element) {
|
|
30024
|
-
for (const
|
|
30025
|
-
const
|
|
30026
|
-
|
|
30027
|
-
|
|
31975
|
+
for (const deferredScope of this.deferBlocks.values()) {
|
|
31976
|
+
const stack = [deferredScope];
|
|
31977
|
+
while (stack.length > 0) {
|
|
31978
|
+
const current = stack.pop();
|
|
31979
|
+
if (current.elementsInScope.has(element)) {
|
|
31980
|
+
return true;
|
|
31981
|
+
}
|
|
31982
|
+
stack.push(...current.childScopes.values());
|
|
30028
31983
|
}
|
|
30029
31984
|
}
|
|
30030
31985
|
return false;
|
|
@@ -30096,6 +32051,7 @@ function extractScopedNodeEntities(rootScope) {
|
|
|
30096
32051
|
class ResourceLoader {
|
|
30097
32052
|
}
|
|
30098
32053
|
|
|
32054
|
+
const SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT = true;
|
|
30099
32055
|
class CompilerFacadeImpl {
|
|
30100
32056
|
constructor(jitEvaluator = new JitEvaluator()) {
|
|
30101
32057
|
this.jitEvaluator = jitEvaluator;
|
|
@@ -30229,6 +32185,7 @@ class CompilerFacadeImpl {
|
|
|
30229
32185
|
null,
|
|
30230
32186
|
relativeContextFilePath: '',
|
|
30231
32187
|
i18nUseExternalIds: true,
|
|
32188
|
+
useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
|
|
30232
32189
|
};
|
|
30233
32190
|
const jitExpressionSourceMap = `ng:///${facade.name}.js`;
|
|
30234
32191
|
return this.compileComponentFromMeta(angularCoreEnv, jitExpressionSourceMap, meta);
|
|
@@ -30292,6 +32249,7 @@ class CompilerFacadeImpl {
|
|
|
30292
32249
|
function convertToR3QueryMetadata(facade) {
|
|
30293
32250
|
return {
|
|
30294
32251
|
...facade,
|
|
32252
|
+
isSignal: facade.isSignal,
|
|
30295
32253
|
predicate: convertQueryPredicate(facade.predicate),
|
|
30296
32254
|
read: facade.read ? new WrappedNodeExpr(facade.read) : null,
|
|
30297
32255
|
static: facade.static,
|
|
@@ -30307,6 +32265,7 @@ function convertQueryDeclarationToMetadata(declaration) {
|
|
|
30307
32265
|
read: declaration.read ? new WrappedNodeExpr(declaration.read) : null,
|
|
30308
32266
|
static: declaration.static ?? false,
|
|
30309
32267
|
emitDistinctChangesOnly: declaration.emitDistinctChangesOnly ?? true,
|
|
32268
|
+
isSignal: !!declaration.isSignal,
|
|
30310
32269
|
};
|
|
30311
32270
|
}
|
|
30312
32271
|
function convertQueryPredicate(predicate) {
|
|
@@ -30349,7 +32308,10 @@ function convertDirectiveFacadeToMetadata(facade) {
|
|
|
30349
32308
|
typeSourceSpan: facade.typeSourceSpan,
|
|
30350
32309
|
type: wrapReference(facade.type),
|
|
30351
32310
|
deps: null,
|
|
30352
|
-
host:
|
|
32311
|
+
host: {
|
|
32312
|
+
...extractHostBindings(facade.propMetadata, facade.typeSourceSpan, facade.host),
|
|
32313
|
+
useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
|
|
32314
|
+
},
|
|
30353
32315
|
inputs: { ...inputsFromMetadata, ...inputsFromType },
|
|
30354
32316
|
outputs: { ...outputsFromMetadata, ...outputsFromType },
|
|
30355
32317
|
queries: facade.queries.map(convertToR3QueryMetadata),
|
|
@@ -30392,6 +32354,7 @@ function convertHostDeclarationToMetadata(host = {}) {
|
|
|
30392
32354
|
classAttr: host.classAttribute,
|
|
30393
32355
|
styleAttr: host.styleAttribute,
|
|
30394
32356
|
},
|
|
32357
|
+
useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
|
|
30395
32358
|
};
|
|
30396
32359
|
}
|
|
30397
32360
|
function convertHostDirectivesToMetadata(metadata) {
|
|
@@ -30464,6 +32427,7 @@ function convertDeclareComponentFacadeToMetadata(decl, typeSourceSpan, sourceMap
|
|
|
30464
32427
|
declarationListEmitMode: 2 /* DeclarationListEmitMode.ClosureResolved */,
|
|
30465
32428
|
relativeContextFilePath: '',
|
|
30466
32429
|
i18nUseExternalIds: true,
|
|
32430
|
+
useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
|
|
30467
32431
|
};
|
|
30468
32432
|
}
|
|
30469
32433
|
function convertDeclarationFacadeToMetadata(declaration) {
|
|
@@ -30745,7 +32709,7 @@ function publishFacade(global) {
|
|
|
30745
32709
|
* @description
|
|
30746
32710
|
* Entry point for all public APIs of the compiler package.
|
|
30747
32711
|
*/
|
|
30748
|
-
const VERSION = new Version('17.
|
|
32712
|
+
const VERSION = new Version('17.3.0');
|
|
30749
32713
|
|
|
30750
32714
|
class CompilerConfig {
|
|
30751
32715
|
constructor({ defaultEncapsulation = exports.ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
|
|
@@ -32246,9 +34210,11 @@ function compileComponentClassMetadata(metadata, deferrableTypes) {
|
|
|
32246
34210
|
}
|
|
32247
34211
|
const dynamicImports = [];
|
|
32248
34212
|
const importedSymbols = [];
|
|
32249
|
-
for (const [symbolName, importPath] of deferrableTypes) {
|
|
34213
|
+
for (const [symbolName, { importPath, isDefaultImport }] of deferrableTypes) {
|
|
32250
34214
|
// e.g. `(m) => m.CmpA`
|
|
32251
|
-
const innerFn =
|
|
34215
|
+
const innerFn =
|
|
34216
|
+
// Default imports are always accessed through the `default` property.
|
|
34217
|
+
arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(isDefaultImport ? 'default' : symbolName));
|
|
32252
34218
|
// e.g. `import('./cmp-a').then(...)`
|
|
32253
34219
|
const importExpr = (new DynamicImportExpr(importPath)).prop('then').callFn([innerFn]);
|
|
32254
34220
|
dynamicImports.push(importExpr);
|
|
@@ -32311,7 +34277,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$5 = '12.0.0';
|
|
|
32311
34277
|
function compileDeclareClassMetadata(metadata) {
|
|
32312
34278
|
const definitionMap = new DefinitionMap();
|
|
32313
34279
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$5));
|
|
32314
|
-
definitionMap.set('version', literal('17.
|
|
34280
|
+
definitionMap.set('version', literal('17.3.0'));
|
|
32315
34281
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
32316
34282
|
definitionMap.set('type', metadata.type);
|
|
32317
34283
|
definitionMap.set('decorators', metadata.decorators);
|
|
@@ -32407,7 +34373,7 @@ function createDirectiveDefinitionMap(meta) {
|
|
|
32407
34373
|
const definitionMap = new DefinitionMap();
|
|
32408
34374
|
const minVersion = getMinimumVersionForPartialOutput(meta);
|
|
32409
34375
|
definitionMap.set('minVersion', literal(minVersion));
|
|
32410
|
-
definitionMap.set('version', literal('17.
|
|
34376
|
+
definitionMap.set('version', literal('17.3.0'));
|
|
32411
34377
|
// e.g. `type: MyDirective`
|
|
32412
34378
|
definitionMap.set('type', meta.type.value);
|
|
32413
34379
|
if (meta.isStandalone) {
|
|
@@ -32475,6 +34441,11 @@ function getMinimumVersionForPartialOutput(meta) {
|
|
|
32475
34441
|
if (needsNewInputPartialOutput(meta)) {
|
|
32476
34442
|
minVersion = '17.1.0';
|
|
32477
34443
|
}
|
|
34444
|
+
// If there are signal-based queries, partial output generates an extra field
|
|
34445
|
+
// that should be parsed by linkers. Ensure a proper minimum linker version.
|
|
34446
|
+
if (meta.queries.some(q => q.isSignal) || meta.viewQueries.some(q => q.isSignal)) {
|
|
34447
|
+
minVersion = '17.2.0';
|
|
34448
|
+
}
|
|
32478
34449
|
return minVersion;
|
|
32479
34450
|
}
|
|
32480
34451
|
/**
|
|
@@ -32508,6 +34479,9 @@ function compileQuery(query) {
|
|
|
32508
34479
|
if (query.static) {
|
|
32509
34480
|
meta.set('static', literal(true));
|
|
32510
34481
|
}
|
|
34482
|
+
if (query.isSignal) {
|
|
34483
|
+
meta.set('isSignal', literal(true));
|
|
34484
|
+
}
|
|
32511
34485
|
return meta.toLiteralMap();
|
|
32512
34486
|
}
|
|
32513
34487
|
/**
|
|
@@ -32788,7 +34762,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
|
|
|
32788
34762
|
function compileDeclareFactoryFunction(meta) {
|
|
32789
34763
|
const definitionMap = new DefinitionMap();
|
|
32790
34764
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
|
|
32791
|
-
definitionMap.set('version', literal('17.
|
|
34765
|
+
definitionMap.set('version', literal('17.3.0'));
|
|
32792
34766
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
32793
34767
|
definitionMap.set('type', meta.type.value);
|
|
32794
34768
|
definitionMap.set('deps', compileDependencies(meta.deps));
|
|
@@ -32823,7 +34797,7 @@ function compileDeclareInjectableFromMetadata(meta) {
|
|
|
32823
34797
|
function createInjectableDefinitionMap(meta) {
|
|
32824
34798
|
const definitionMap = new DefinitionMap();
|
|
32825
34799
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
|
|
32826
|
-
definitionMap.set('version', literal('17.
|
|
34800
|
+
definitionMap.set('version', literal('17.3.0'));
|
|
32827
34801
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
32828
34802
|
definitionMap.set('type', meta.type.value);
|
|
32829
34803
|
// Only generate providedIn property if it has a non-null value
|
|
@@ -32874,7 +34848,7 @@ function compileDeclareInjectorFromMetadata(meta) {
|
|
|
32874
34848
|
function createInjectorDefinitionMap(meta) {
|
|
32875
34849
|
const definitionMap = new DefinitionMap();
|
|
32876
34850
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
|
|
32877
|
-
definitionMap.set('version', literal('17.
|
|
34851
|
+
definitionMap.set('version', literal('17.3.0'));
|
|
32878
34852
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
32879
34853
|
definitionMap.set('type', meta.type.value);
|
|
32880
34854
|
definitionMap.set('providers', meta.providers);
|
|
@@ -32907,7 +34881,7 @@ function createNgModuleDefinitionMap(meta) {
|
|
|
32907
34881
|
throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
|
|
32908
34882
|
}
|
|
32909
34883
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
|
|
32910
|
-
definitionMap.set('version', literal('17.
|
|
34884
|
+
definitionMap.set('version', literal('17.3.0'));
|
|
32911
34885
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
32912
34886
|
definitionMap.set('type', meta.type.value);
|
|
32913
34887
|
// We only generate the keys in the metadata if the arrays contain values.
|
|
@@ -32958,7 +34932,7 @@ function compileDeclarePipeFromMetadata(meta) {
|
|
|
32958
34932
|
function createPipeDefinitionMap(meta) {
|
|
32959
34933
|
const definitionMap = new DefinitionMap();
|
|
32960
34934
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
|
|
32961
|
-
definitionMap.set('version', literal('17.
|
|
34935
|
+
definitionMap.set('version', literal('17.3.0'));
|
|
32962
34936
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
32963
34937
|
// e.g. `type: MyPipe`
|
|
32964
34938
|
definitionMap.set('type', meta.type.value);
|
|
@@ -33095,6 +35069,7 @@ exports.TemplateLiteral = TemplateLiteral;
|
|
|
33095
35069
|
exports.TemplateLiteralElement = TemplateLiteralElement;
|
|
33096
35070
|
exports.Text = Text;
|
|
33097
35071
|
exports.ThisReceiver = ThisReceiver;
|
|
35072
|
+
exports.TmplAstBlockNode = BlockNode;
|
|
33098
35073
|
exports.TmplAstBoundAttribute = BoundAttribute;
|
|
33099
35074
|
exports.TmplAstBoundDeferredTrigger = BoundDeferredTrigger;
|
|
33100
35075
|
exports.TmplAstBoundEvent = BoundEvent;
|
|
@@ -33145,7 +35120,6 @@ exports.Xliff2 = Xliff2;
|
|
|
33145
35120
|
exports.Xmb = Xmb;
|
|
33146
35121
|
exports.XmlParser = XmlParser;
|
|
33147
35122
|
exports.Xtb = Xtb;
|
|
33148
|
-
exports._ParseAST = _ParseAST;
|
|
33149
35123
|
exports.compileClassDebugInfo = compileClassDebugInfo;
|
|
33150
35124
|
exports.compileClassMetadata = compileClassMetadata;
|
|
33151
35125
|
exports.compileComponentClassMetadata = compileComponentClassMetadata;
|
|
@@ -33194,5 +35168,6 @@ exports.publishFacade = publishFacade;
|
|
|
33194
35168
|
exports.r3JitTypeSourceSpan = r3JitTypeSourceSpan;
|
|
33195
35169
|
exports.sanitizeIdentifier = sanitizeIdentifier;
|
|
33196
35170
|
exports.splitNsName = splitNsName;
|
|
35171
|
+
exports.tmplAstVisitAll = visitAll$1;
|
|
33197
35172
|
exports.verifyHostBindings = verifyHostBindings;
|
|
33198
35173
|
exports.visitAll = visitAll;
|