@angular/compiler 17.0.6 → 17.0.8
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/esm2022/src/constant_pool.mjs +5 -3
- package/esm2022/src/ml_parser/tags.mjs +8 -3
- package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
- package/esm2022/src/render3/partial/directive.mjs +1 -1
- package/esm2022/src/render3/partial/factory.mjs +1 -1
- package/esm2022/src/render3/partial/injectable.mjs +1 -1
- package/esm2022/src/render3/partial/injector.mjs +1 -1
- package/esm2022/src/render3/partial/ng_module.mjs +1 -1
- package/esm2022/src/render3/partial/pipe.mjs +1 -1
- package/esm2022/src/render3/view/compiler.mjs +2 -1
- package/esm2022/src/render3/view/template.mjs +7 -7
- package/esm2022/src/template/pipeline/ir/src/enums.mjs +15 -19
- package/esm2022/src/template/pipeline/ir/src/expression.mjs +16 -20
- package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +39 -13
- package/esm2022/src/template/pipeline/ir/src/ops/host.mjs +4 -2
- package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +18 -10
- package/esm2022/src/template/pipeline/src/emit.mjs +6 -6
- package/esm2022/src/template/pipeline/src/ingest.mjs +266 -178
- package/esm2022/src/template/pipeline/src/instruction.mjs +14 -14
- package/esm2022/src/template/pipeline/src/phases/assign_i18n_slot_dependencies.mjs +40 -56
- package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +19 -22
- package/esm2022/src/template/pipeline/src/phases/binding_specialization.mjs +4 -4
- package/esm2022/src/template/pipeline/src/phases/const_collection.mjs +37 -13
- package/esm2022/src/template/pipeline/src/phases/convert_i18n_bindings.mjs +2 -2
- package/esm2022/src/template/pipeline/src/phases/create_defer_deps_fns.mjs +3 -2
- package/esm2022/src/template/pipeline/src/phases/create_i18n_contexts.mjs +63 -29
- package/esm2022/src/template/pipeline/src/phases/deduplicate_text_bindings.mjs +40 -0
- package/esm2022/src/template/pipeline/src/phases/extract_i18n_messages.mjs +52 -49
- package/esm2022/src/template/pipeline/src/phases/generate_variables.mjs +7 -1
- package/esm2022/src/template/pipeline/src/phases/host_style_property_parsing.mjs +2 -2
- package/esm2022/src/template/pipeline/src/phases/i18n_const_collection.mjs +11 -8
- package/esm2022/src/template/pipeline/src/phases/i18n_text_extraction.mjs +22 -3
- package/esm2022/src/template/pipeline/src/phases/parse_extracted_styles.mjs +22 -3
- package/esm2022/src/template/pipeline/src/phases/propagate_i18n_blocks.mjs +27 -12
- package/esm2022/src/template/pipeline/src/phases/reify.mjs +18 -21
- package/esm2022/src/template/pipeline/src/phases/resolve_i18n_element_placeholders.mjs +60 -14
- package/esm2022/src/template/pipeline/src/phases/resolve_i18n_expression_placeholders.mjs +23 -10
- package/esm2022/src/template/pipeline/src/phases/resolve_sanitizers.mjs +78 -14
- package/esm2022/src/template/pipeline/src/phases/slot_allocation.mjs +3 -1
- package/esm2022/src/template/pipeline/src/phases/var_counting.mjs +10 -1
- package/esm2022/src/version.mjs +1 -1
- package/fesm2022/compiler.mjs +894 -561
- package/fesm2022/compiler.mjs.map +1 -1
- package/index.d.ts +3 -3
- package/package.json +2 -2
- package/esm2022/src/template/pipeline/src/phases/resolve_i18n_icu_placeholders.mjs +0 -62
package/fesm2022/compiler.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v17.0.
|
|
2
|
+
* @license Angular v17.0.8
|
|
3
3
|
* (c) 2010-2022 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -2308,7 +2308,9 @@ class ConstantPool {
|
|
|
2308
2308
|
}))));
|
|
2309
2309
|
}
|
|
2310
2310
|
}
|
|
2311
|
-
|
|
2311
|
+
// TODO: useUniqueName(false) is necessary for naming compatibility with
|
|
2312
|
+
// TemplateDefinitionBuilder, but should be removed once Template Pipeline is the default.
|
|
2313
|
+
getSharedFunctionReference(fn, prefix, useUniqueName = true) {
|
|
2312
2314
|
const isArrow = fn instanceof ArrowFunctionExpr;
|
|
2313
2315
|
for (const current of this.statements) {
|
|
2314
2316
|
// Arrow functions are saved as variables so we check if the
|
|
@@ -2323,7 +2325,7 @@ class ConstantPool {
|
|
|
2323
2325
|
}
|
|
2324
2326
|
}
|
|
2325
2327
|
// Otherwise declare the function.
|
|
2326
|
-
const name = this.uniqueName(prefix);
|
|
2328
|
+
const name = useUniqueName ? this.uniqueName(prefix) : prefix;
|
|
2327
2329
|
this.statements.push(fn.toDeclStmt(name, StmtModifier.Final));
|
|
2328
2330
|
return variable(name);
|
|
2329
2331
|
}
|
|
@@ -3685,13 +3687,18 @@ var TagContentType;
|
|
|
3685
3687
|
TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
|
|
3686
3688
|
TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
|
|
3687
3689
|
})(TagContentType || (TagContentType = {}));
|
|
3688
|
-
function splitNsName(elementName) {
|
|
3690
|
+
function splitNsName(elementName, fatal = true) {
|
|
3689
3691
|
if (elementName[0] != ':') {
|
|
3690
3692
|
return [null, elementName];
|
|
3691
3693
|
}
|
|
3692
3694
|
const colonIndex = elementName.indexOf(':', 1);
|
|
3693
3695
|
if (colonIndex === -1) {
|
|
3694
|
-
|
|
3696
|
+
if (fatal) {
|
|
3697
|
+
throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
|
|
3698
|
+
}
|
|
3699
|
+
else {
|
|
3700
|
+
return [null, elementName];
|
|
3701
|
+
}
|
|
3695
3702
|
}
|
|
3696
3703
|
return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
|
|
3697
3704
|
}
|
|
@@ -8966,14 +8973,18 @@ var OpKind;
|
|
|
8966
8973
|
* An instruction to update an ICU expression.
|
|
8967
8974
|
*/
|
|
8968
8975
|
OpKind[OpKind["IcuEnd"] = 42] = "IcuEnd";
|
|
8976
|
+
/**
|
|
8977
|
+
* An instruction representing a placeholder in an ICU expression.
|
|
8978
|
+
*/
|
|
8979
|
+
OpKind[OpKind["IcuPlaceholder"] = 43] = "IcuPlaceholder";
|
|
8969
8980
|
/**
|
|
8970
8981
|
* An i18n context containing information needed to generate an i18n message.
|
|
8971
8982
|
*/
|
|
8972
|
-
OpKind[OpKind["I18nContext"] =
|
|
8983
|
+
OpKind[OpKind["I18nContext"] = 44] = "I18nContext";
|
|
8973
8984
|
/**
|
|
8974
8985
|
* A creation op that corresponds to i18n attributes on an element.
|
|
8975
8986
|
*/
|
|
8976
|
-
OpKind[OpKind["I18nAttributes"] =
|
|
8987
|
+
OpKind[OpKind["I18nAttributes"] = 45] = "I18nAttributes";
|
|
8977
8988
|
})(OpKind || (OpKind = {}));
|
|
8978
8989
|
/**
|
|
8979
8990
|
* Distinguishes different kinds of IR expressions.
|
|
@@ -9064,23 +9075,27 @@ var ExpressionKind;
|
|
|
9064
9075
|
* An expression representing a sanitizer function.
|
|
9065
9076
|
*/
|
|
9066
9077
|
ExpressionKind[ExpressionKind["SanitizerExpr"] = 20] = "SanitizerExpr";
|
|
9078
|
+
/**
|
|
9079
|
+
* An expression representing a function to create trusted values.
|
|
9080
|
+
*/
|
|
9081
|
+
ExpressionKind[ExpressionKind["TrustedValueFnExpr"] = 21] = "TrustedValueFnExpr";
|
|
9067
9082
|
/**
|
|
9068
9083
|
* An expression that will cause a literal slot index to be emitted.
|
|
9069
9084
|
*/
|
|
9070
|
-
ExpressionKind[ExpressionKind["SlotLiteralExpr"] =
|
|
9085
|
+
ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 22] = "SlotLiteralExpr";
|
|
9071
9086
|
/**
|
|
9072
9087
|
* A test expression for a conditional op.
|
|
9073
9088
|
*/
|
|
9074
|
-
ExpressionKind[ExpressionKind["ConditionalCase"] =
|
|
9089
|
+
ExpressionKind[ExpressionKind["ConditionalCase"] = 23] = "ConditionalCase";
|
|
9075
9090
|
/**
|
|
9076
9091
|
* A variable for use inside a repeater, providing one of the ambiently-available context
|
|
9077
9092
|
* properties ($even, $first, etc.).
|
|
9078
9093
|
*/
|
|
9079
|
-
ExpressionKind[ExpressionKind["DerivedRepeaterVar"] =
|
|
9094
|
+
ExpressionKind[ExpressionKind["DerivedRepeaterVar"] = 24] = "DerivedRepeaterVar";
|
|
9080
9095
|
/**
|
|
9081
9096
|
* An expression that will be automatically extracted to the component const array.
|
|
9082
9097
|
*/
|
|
9083
|
-
ExpressionKind[ExpressionKind["ConstCollected"] =
|
|
9098
|
+
ExpressionKind[ExpressionKind["ConstCollected"] = 25] = "ConstCollected";
|
|
9084
9099
|
})(ExpressionKind || (ExpressionKind = {}));
|
|
9085
9100
|
var VariableFlags;
|
|
9086
9101
|
(function (VariableFlags) {
|
|
@@ -9124,18 +9139,6 @@ var CompatibilityMode;
|
|
|
9124
9139
|
CompatibilityMode[CompatibilityMode["Normal"] = 0] = "Normal";
|
|
9125
9140
|
CompatibilityMode[CompatibilityMode["TemplateDefinitionBuilder"] = 1] = "TemplateDefinitionBuilder";
|
|
9126
9141
|
})(CompatibilityMode || (CompatibilityMode = {}));
|
|
9127
|
-
/**
|
|
9128
|
-
* Represents functions used to sanitize different pieces of a template.
|
|
9129
|
-
*/
|
|
9130
|
-
var SanitizerFn;
|
|
9131
|
-
(function (SanitizerFn) {
|
|
9132
|
-
SanitizerFn[SanitizerFn["Html"] = 0] = "Html";
|
|
9133
|
-
SanitizerFn[SanitizerFn["Script"] = 1] = "Script";
|
|
9134
|
-
SanitizerFn[SanitizerFn["Style"] = 2] = "Style";
|
|
9135
|
-
SanitizerFn[SanitizerFn["Url"] = 3] = "Url";
|
|
9136
|
-
SanitizerFn[SanitizerFn["ResourceUrl"] = 4] = "ResourceUrl";
|
|
9137
|
-
SanitizerFn[SanitizerFn["IframeAttribute"] = 5] = "IframeAttribute";
|
|
9138
|
-
})(SanitizerFn || (SanitizerFn = {}));
|
|
9139
9142
|
/**
|
|
9140
9143
|
* Enumeration of the different kinds of `@defer` secondary blocks.
|
|
9141
9144
|
*/
|
|
@@ -9414,7 +9417,7 @@ class Interpolation {
|
|
|
9414
9417
|
/**
|
|
9415
9418
|
* Create a `BindingOp`, not yet transformed into a particular type of binding.
|
|
9416
9419
|
*/
|
|
9417
|
-
function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute,
|
|
9420
|
+
function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
|
|
9418
9421
|
return {
|
|
9419
9422
|
kind: OpKind.Binding,
|
|
9420
9423
|
bindingKind: kind,
|
|
@@ -9424,8 +9427,10 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
|
|
|
9424
9427
|
unit,
|
|
9425
9428
|
securityContext,
|
|
9426
9429
|
isTextAttribute,
|
|
9427
|
-
|
|
9428
|
-
|
|
9430
|
+
isStructuralTemplateAttribute,
|
|
9431
|
+
templateKind,
|
|
9432
|
+
i18nContext: null,
|
|
9433
|
+
i18nMessage,
|
|
9429
9434
|
sourceSpan,
|
|
9430
9435
|
...NEW_OP,
|
|
9431
9436
|
};
|
|
@@ -9433,7 +9438,7 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
|
|
|
9433
9438
|
/**
|
|
9434
9439
|
* Create a `PropertyOp`.
|
|
9435
9440
|
*/
|
|
9436
|
-
function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext,
|
|
9441
|
+
function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext, isStructuralTemplateAttribute, templateKind, i18nContext, i18nMessage, sourceSpan) {
|
|
9437
9442
|
return {
|
|
9438
9443
|
kind: OpKind.Property,
|
|
9439
9444
|
target,
|
|
@@ -9442,8 +9447,10 @@ function createPropertyOp(target, name, expression, isAnimationTrigger, security
|
|
|
9442
9447
|
isAnimationTrigger,
|
|
9443
9448
|
securityContext,
|
|
9444
9449
|
sanitizer: null,
|
|
9445
|
-
|
|
9450
|
+
isStructuralTemplateAttribute,
|
|
9451
|
+
templateKind,
|
|
9446
9452
|
i18nContext,
|
|
9453
|
+
i18nMessage,
|
|
9447
9454
|
sourceSpan,
|
|
9448
9455
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9449
9456
|
...TRAIT_CONSUMES_VARS,
|
|
@@ -9508,7 +9515,7 @@ function createClassMapOp(xref, expression, sourceSpan) {
|
|
|
9508
9515
|
/**
|
|
9509
9516
|
* Create an `AttributeOp`.
|
|
9510
9517
|
*/
|
|
9511
|
-
function createAttributeOp(target, name, expression, securityContext, isTextAttribute,
|
|
9518
|
+
function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
|
|
9512
9519
|
return {
|
|
9513
9520
|
kind: OpKind.Attribute,
|
|
9514
9521
|
target,
|
|
@@ -9517,8 +9524,10 @@ function createAttributeOp(target, name, expression, securityContext, isTextAttr
|
|
|
9517
9524
|
securityContext,
|
|
9518
9525
|
sanitizer: null,
|
|
9519
9526
|
isTextAttribute,
|
|
9520
|
-
|
|
9521
|
-
|
|
9527
|
+
isStructuralTemplateAttribute,
|
|
9528
|
+
templateKind,
|
|
9529
|
+
i18nContext: null,
|
|
9530
|
+
i18nMessage,
|
|
9522
9531
|
sourceSpan,
|
|
9523
9532
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9524
9533
|
...TRAIT_CONSUMES_VARS,
|
|
@@ -9574,12 +9583,13 @@ function createDeferWhenOp(target, expr, prefetch, sourceSpan) {
|
|
|
9574
9583
|
sourceSpan,
|
|
9575
9584
|
...NEW_OP,
|
|
9576
9585
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9586
|
+
...TRAIT_CONSUMES_VARS,
|
|
9577
9587
|
};
|
|
9578
9588
|
}
|
|
9579
9589
|
/**
|
|
9580
9590
|
* Create an i18n expression op.
|
|
9581
9591
|
*/
|
|
9582
|
-
function createI18nExpressionOp(context, target, i18nOwner, handle, expression, i18nPlaceholder, resolutionTime, usage, name, sourceSpan) {
|
|
9592
|
+
function createI18nExpressionOp(context, target, i18nOwner, handle, expression, icuPlaceholder, i18nPlaceholder, resolutionTime, usage, name, sourceSpan) {
|
|
9583
9593
|
return {
|
|
9584
9594
|
kind: OpKind.I18nExpression,
|
|
9585
9595
|
context,
|
|
@@ -9587,6 +9597,7 @@ function createI18nExpressionOp(context, target, i18nOwner, handle, expression,
|
|
|
9587
9597
|
i18nOwner,
|
|
9588
9598
|
handle,
|
|
9589
9599
|
expression,
|
|
9600
|
+
icuPlaceholder,
|
|
9590
9601
|
i18nPlaceholder,
|
|
9591
9602
|
resolutionTime,
|
|
9592
9603
|
usage,
|
|
@@ -10152,24 +10163,6 @@ class ReadTemporaryExpr extends ExpressionBase {
|
|
|
10152
10163
|
return r;
|
|
10153
10164
|
}
|
|
10154
10165
|
}
|
|
10155
|
-
class SanitizerExpr extends ExpressionBase {
|
|
10156
|
-
constructor(fn) {
|
|
10157
|
-
super();
|
|
10158
|
-
this.fn = fn;
|
|
10159
|
-
this.kind = ExpressionKind.SanitizerExpr;
|
|
10160
|
-
}
|
|
10161
|
-
visitExpression(visitor, context) { }
|
|
10162
|
-
isEquivalent(e) {
|
|
10163
|
-
return e instanceof SanitizerExpr && e.fn === this.fn;
|
|
10164
|
-
}
|
|
10165
|
-
isConstant() {
|
|
10166
|
-
return true;
|
|
10167
|
-
}
|
|
10168
|
-
clone() {
|
|
10169
|
-
return new SanitizerExpr(this.fn);
|
|
10170
|
-
}
|
|
10171
|
-
transformInternalExpressions() { }
|
|
10172
|
-
}
|
|
10173
10166
|
class SlotLiteralExpr extends ExpressionBase {
|
|
10174
10167
|
constructor(slot) {
|
|
10175
10168
|
super();
|
|
@@ -10300,7 +10293,6 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10300
10293
|
case OpKind.ClassProp:
|
|
10301
10294
|
case OpKind.ClassMap:
|
|
10302
10295
|
case OpKind.Binding:
|
|
10303
|
-
case OpKind.HostProperty:
|
|
10304
10296
|
if (op.expression instanceof Interpolation) {
|
|
10305
10297
|
transformExpressionsInInterpolation(op.expression, transform, flags);
|
|
10306
10298
|
}
|
|
@@ -10309,6 +10301,7 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10309
10301
|
}
|
|
10310
10302
|
break;
|
|
10311
10303
|
case OpKind.Property:
|
|
10304
|
+
case OpKind.HostProperty:
|
|
10312
10305
|
case OpKind.Attribute:
|
|
10313
10306
|
if (op.expression instanceof Interpolation) {
|
|
10314
10307
|
transformExpressionsInInterpolation(op.expression, transform, flags);
|
|
@@ -10354,6 +10347,8 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10354
10347
|
case OpKind.ExtractedAttribute:
|
|
10355
10348
|
op.expression =
|
|
10356
10349
|
op.expression && transformExpressionsInExpression(op.expression, transform, flags);
|
|
10350
|
+
op.trustedValueFn = op.trustedValueFn &&
|
|
10351
|
+
transformExpressionsInExpression(op.trustedValueFn, transform, flags);
|
|
10357
10352
|
break;
|
|
10358
10353
|
case OpKind.RepeaterCreate:
|
|
10359
10354
|
op.track = transformExpressionsInExpression(op.track, transform, flags);
|
|
@@ -10372,6 +10367,9 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10372
10367
|
op.placeholderConfig =
|
|
10373
10368
|
transformExpressionsInExpression(op.placeholderConfig, transform, flags);
|
|
10374
10369
|
}
|
|
10370
|
+
if (op.resolverFn !== null) {
|
|
10371
|
+
op.resolverFn = transformExpressionsInExpression(op.resolverFn, transform, flags);
|
|
10372
|
+
}
|
|
10375
10373
|
break;
|
|
10376
10374
|
case OpKind.I18nMessage:
|
|
10377
10375
|
for (const [placeholder, expr] of op.params) {
|
|
@@ -10408,6 +10406,7 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10408
10406
|
case OpKind.Template:
|
|
10409
10407
|
case OpKind.Text:
|
|
10410
10408
|
case OpKind.I18nAttributes:
|
|
10409
|
+
case OpKind.IcuPlaceholder:
|
|
10411
10410
|
// These operations contain no expressions.
|
|
10412
10411
|
break;
|
|
10413
10412
|
default:
|
|
@@ -10485,6 +10484,14 @@ function transformExpressionsInExpression(expr, transform, flags) {
|
|
|
10485
10484
|
else if (expr instanceof NotExpr) {
|
|
10486
10485
|
expr.condition = transformExpressionsInExpression(expr.condition, transform, flags);
|
|
10487
10486
|
}
|
|
10487
|
+
else if (expr instanceof TaggedTemplateExpr) {
|
|
10488
|
+
expr.tag = transformExpressionsInExpression(expr.tag, transform, flags);
|
|
10489
|
+
expr.template.expressions =
|
|
10490
|
+
expr.template.expressions.map(e => transformExpressionsInExpression(e, transform, flags));
|
|
10491
|
+
}
|
|
10492
|
+
else if (expr instanceof WrappedNodeExpr) {
|
|
10493
|
+
// TODO: Do we need to transform any TS nodes nested inside of this expression?
|
|
10494
|
+
}
|
|
10488
10495
|
else if (expr instanceof ReadVarExpr || expr instanceof ExternalExpr ||
|
|
10489
10496
|
expr instanceof LiteralExpr) {
|
|
10490
10497
|
// No action for these types.
|
|
@@ -10807,7 +10814,7 @@ function isElementOrContainerOp(op) {
|
|
|
10807
10814
|
/**
|
|
10808
10815
|
* Create an `ElementStartOp`.
|
|
10809
10816
|
*/
|
|
10810
|
-
function createElementStartOp(tag, xref, namespace, i18nPlaceholder,
|
|
10817
|
+
function createElementStartOp(tag, xref, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) {
|
|
10811
10818
|
return {
|
|
10812
10819
|
kind: OpKind.ElementStart,
|
|
10813
10820
|
xref,
|
|
@@ -10818,7 +10825,8 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
|
|
|
10818
10825
|
nonBindable: false,
|
|
10819
10826
|
namespace,
|
|
10820
10827
|
i18nPlaceholder,
|
|
10821
|
-
|
|
10828
|
+
startSourceSpan,
|
|
10829
|
+
wholeSourceSpan,
|
|
10822
10830
|
...TRAIT_CONSUMES_SLOT,
|
|
10823
10831
|
...NEW_OP,
|
|
10824
10832
|
};
|
|
@@ -10826,7 +10834,7 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
|
|
|
10826
10834
|
/**
|
|
10827
10835
|
* Create a `TemplateOp`.
|
|
10828
10836
|
*/
|
|
10829
|
-
function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder,
|
|
10837
|
+
function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, startSourceSpan, wholeSourceSpan) {
|
|
10830
10838
|
return {
|
|
10831
10839
|
kind: OpKind.Template,
|
|
10832
10840
|
xref,
|
|
@@ -10841,12 +10849,13 @@ function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace
|
|
|
10841
10849
|
nonBindable: false,
|
|
10842
10850
|
namespace,
|
|
10843
10851
|
i18nPlaceholder,
|
|
10844
|
-
|
|
10852
|
+
startSourceSpan,
|
|
10853
|
+
wholeSourceSpan,
|
|
10845
10854
|
...TRAIT_CONSUMES_SLOT,
|
|
10846
10855
|
...NEW_OP,
|
|
10847
10856
|
};
|
|
10848
10857
|
}
|
|
10849
|
-
function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames,
|
|
10858
|
+
function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, i18nPlaceholder, emptyI18nPlaceholder, startSourceSpan, wholeSourceSpan) {
|
|
10850
10859
|
return {
|
|
10851
10860
|
kind: OpKind.RepeaterCreate,
|
|
10852
10861
|
attributes: null,
|
|
@@ -10864,9 +10873,13 @@ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, so
|
|
|
10864
10873
|
vars: null,
|
|
10865
10874
|
varNames,
|
|
10866
10875
|
usesComponentInstance: false,
|
|
10867
|
-
|
|
10876
|
+
i18nPlaceholder,
|
|
10877
|
+
emptyI18nPlaceholder,
|
|
10878
|
+
startSourceSpan,
|
|
10879
|
+
wholeSourceSpan,
|
|
10868
10880
|
...TRAIT_CONSUMES_SLOT,
|
|
10869
10881
|
...NEW_OP,
|
|
10882
|
+
...TRAIT_CONSUMES_VARS,
|
|
10870
10883
|
numSlotsUsed: emptyView === null ? 2 : 3,
|
|
10871
10884
|
};
|
|
10872
10885
|
}
|
|
@@ -10898,12 +10911,13 @@ function createEnableBindingsOp(xref) {
|
|
|
10898
10911
|
/**
|
|
10899
10912
|
* Create a `TextOp`.
|
|
10900
10913
|
*/
|
|
10901
|
-
function createTextOp(xref, initialValue, sourceSpan) {
|
|
10914
|
+
function createTextOp(xref, initialValue, icuPlaceholder, sourceSpan) {
|
|
10902
10915
|
return {
|
|
10903
10916
|
kind: OpKind.Text,
|
|
10904
10917
|
xref,
|
|
10905
10918
|
handle: new SlotHandle(),
|
|
10906
10919
|
initialValue,
|
|
10920
|
+
icuPlaceholder,
|
|
10907
10921
|
sourceSpan,
|
|
10908
10922
|
...TRAIT_CONSUMES_SLOT,
|
|
10909
10923
|
...NEW_OP,
|
|
@@ -10912,7 +10926,9 @@ function createTextOp(xref, initialValue, sourceSpan) {
|
|
|
10912
10926
|
/**
|
|
10913
10927
|
* Create a `ListenerOp`. Host bindings reuse all the listener logic.
|
|
10914
10928
|
*/
|
|
10915
|
-
function createListenerOp(target, targetSlot, name, tag, animationPhase, hostListener, sourceSpan) {
|
|
10929
|
+
function createListenerOp(target, targetSlot, name, tag, handlerOps, animationPhase, eventTarget, hostListener, sourceSpan) {
|
|
10930
|
+
const handlerList = new OpList();
|
|
10931
|
+
handlerList.push(handlerOps);
|
|
10916
10932
|
return {
|
|
10917
10933
|
kind: OpKind.Listener,
|
|
10918
10934
|
target,
|
|
@@ -10920,11 +10936,12 @@ function createListenerOp(target, targetSlot, name, tag, animationPhase, hostLis
|
|
|
10920
10936
|
tag,
|
|
10921
10937
|
hostListener,
|
|
10922
10938
|
name,
|
|
10923
|
-
handlerOps:
|
|
10939
|
+
handlerOps: handlerList,
|
|
10924
10940
|
handlerFnName: null,
|
|
10925
10941
|
consumesDollarEvent: false,
|
|
10926
10942
|
isAnimationListener: animationPhase !== null,
|
|
10927
|
-
animationPhase
|
|
10943
|
+
animationPhase,
|
|
10944
|
+
eventTarget,
|
|
10928
10945
|
sourceSpan,
|
|
10929
10946
|
...NEW_OP,
|
|
10930
10947
|
};
|
|
@@ -10971,7 +10988,7 @@ function createProjectionOp(xref, selector, i18nPlaceholder, attributes, sourceS
|
|
|
10971
10988
|
/**
|
|
10972
10989
|
* Create an `ExtractedAttributeOp`.
|
|
10973
10990
|
*/
|
|
10974
|
-
function createExtractedAttributeOp(target, bindingKind, name, expression, i18nContext) {
|
|
10991
|
+
function createExtractedAttributeOp(target, bindingKind, name, expression, i18nContext, i18nMessage, securityContext) {
|
|
10975
10992
|
return {
|
|
10976
10993
|
kind: OpKind.ExtractedAttribute,
|
|
10977
10994
|
target,
|
|
@@ -10979,6 +10996,9 @@ function createExtractedAttributeOp(target, bindingKind, name, expression, i18nC
|
|
|
10979
10996
|
name,
|
|
10980
10997
|
expression,
|
|
10981
10998
|
i18nContext,
|
|
10999
|
+
i18nMessage,
|
|
11000
|
+
securityContext,
|
|
11001
|
+
trustedValueFn: null,
|
|
10982
11002
|
...NEW_OP,
|
|
10983
11003
|
};
|
|
10984
11004
|
}
|
|
@@ -11087,6 +11107,19 @@ function createIcuEndOp(xref) {
|
|
|
11087
11107
|
...NEW_OP,
|
|
11088
11108
|
};
|
|
11089
11109
|
}
|
|
11110
|
+
/**
|
|
11111
|
+
* Creates an ICU placeholder op.
|
|
11112
|
+
*/
|
|
11113
|
+
function createIcuPlaceholderOp(xref, name, strings) {
|
|
11114
|
+
return {
|
|
11115
|
+
kind: OpKind.IcuPlaceholder,
|
|
11116
|
+
xref,
|
|
11117
|
+
name,
|
|
11118
|
+
strings,
|
|
11119
|
+
expressionPlaceholders: [],
|
|
11120
|
+
...NEW_OP,
|
|
11121
|
+
};
|
|
11122
|
+
}
|
|
11090
11123
|
function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan) {
|
|
11091
11124
|
if (i18nBlock === null && contextKind !== I18nContextKind.Attr) {
|
|
11092
11125
|
throw new Error('AssertionError: i18nBlock must be provided for non-attribute contexts.');
|
|
@@ -11121,13 +11154,15 @@ function literalOrArrayLiteral$1(value) {
|
|
|
11121
11154
|
return literal(value, INFERRED_TYPE);
|
|
11122
11155
|
}
|
|
11123
11156
|
|
|
11124
|
-
function createHostPropertyOp(name, expression, isAnimationTrigger, i18nContext, sourceSpan) {
|
|
11157
|
+
function createHostPropertyOp(name, expression, isAnimationTrigger, i18nContext, securityContext, sourceSpan) {
|
|
11125
11158
|
return {
|
|
11126
11159
|
kind: OpKind.HostProperty,
|
|
11127
11160
|
name,
|
|
11128
11161
|
expression,
|
|
11129
11162
|
isAnimationTrigger,
|
|
11130
11163
|
i18nContext,
|
|
11164
|
+
securityContext,
|
|
11165
|
+
sanitizer: null,
|
|
11131
11166
|
sourceSpan,
|
|
11132
11167
|
...TRAIT_CONSUMES_VARS,
|
|
11133
11168
|
...NEW_OP,
|
|
@@ -11407,68 +11442,52 @@ function needsApplication(i18nContexts, op) {
|
|
|
11407
11442
|
* after the last update instruction that depends on that slot.
|
|
11408
11443
|
*/
|
|
11409
11444
|
function assignI18nSlotDependencies(job) {
|
|
11410
|
-
const i18nLastSlotConsumers = new Map();
|
|
11411
|
-
let lastSlotConsumer = null;
|
|
11412
|
-
let currentI18nOp = null;
|
|
11413
11445
|
for (const unit of job.units) {
|
|
11414
|
-
//
|
|
11415
|
-
|
|
11416
|
-
|
|
11417
|
-
|
|
11446
|
+
// The first update op.
|
|
11447
|
+
let updateOp = unit.update.head;
|
|
11448
|
+
// I18n expressions currently being moved during the iteration.
|
|
11449
|
+
let i18nExpressionsInProgress = [];
|
|
11450
|
+
// Non-null while we are iterating through an i18nStart/i18nEnd pair
|
|
11451
|
+
let state = null;
|
|
11452
|
+
for (const createOp of unit.create) {
|
|
11453
|
+
if (createOp.kind === OpKind.I18nStart) {
|
|
11454
|
+
state = {
|
|
11455
|
+
blockXref: createOp.xref,
|
|
11456
|
+
lastSlotConsumer: createOp.xref,
|
|
11457
|
+
};
|
|
11418
11458
|
}
|
|
11419
|
-
|
|
11420
|
-
|
|
11421
|
-
|
|
11422
|
-
|
|
11423
|
-
|
|
11424
|
-
|
|
11425
|
-
|
|
11459
|
+
else if (createOp.kind === OpKind.I18nEnd) {
|
|
11460
|
+
for (const op of i18nExpressionsInProgress) {
|
|
11461
|
+
op.target = state.lastSlotConsumer;
|
|
11462
|
+
OpList.insertBefore(op, updateOp);
|
|
11463
|
+
}
|
|
11464
|
+
i18nExpressionsInProgress.length = 0;
|
|
11465
|
+
state = null;
|
|
11466
|
+
}
|
|
11467
|
+
if (hasConsumesSlotTrait(createOp)) {
|
|
11468
|
+
if (state !== null) {
|
|
11469
|
+
state.lastSlotConsumer = createOp.xref;
|
|
11470
|
+
}
|
|
11471
|
+
while (true) {
|
|
11472
|
+
if (updateOp.next === null) {
|
|
11473
|
+
break;
|
|
11426
11474
|
}
|
|
11427
|
-
if (
|
|
11428
|
-
|
|
11475
|
+
if (state !== null && updateOp.kind === OpKind.I18nExpression &&
|
|
11476
|
+
updateOp.usage === I18nExpressionFor.I18nText &&
|
|
11477
|
+
updateOp.i18nOwner === state.blockXref) {
|
|
11478
|
+
const opToRemove = updateOp;
|
|
11479
|
+
updateOp = updateOp.next;
|
|
11480
|
+
OpList.remove(opToRemove);
|
|
11481
|
+
i18nExpressionsInProgress.push(opToRemove);
|
|
11482
|
+
continue;
|
|
11429
11483
|
}
|
|
11430
|
-
|
|
11431
|
-
|
|
11432
|
-
|
|
11433
|
-
|
|
11434
|
-
}
|
|
11435
|
-
// Expresions that are currently being moved.
|
|
11436
|
-
let opsToMove = [];
|
|
11437
|
-
// Previously we found the last slot-consuming create mode op in the i18n block. That op will be
|
|
11438
|
-
// the new target for any moved i18n expresion inside the i18n block, and that op's slot is
|
|
11439
|
-
// stored here.
|
|
11440
|
-
let moveAfterTarget = null;
|
|
11441
|
-
// This is the last target in the create IR that we saw during iteration. Eventally, it will be
|
|
11442
|
-
// equal to moveAfterTarget. But wait! We need to find the *last* such op whose target is equal
|
|
11443
|
-
// to `moveAfterTarget`.
|
|
11444
|
-
let previousTarget = null;
|
|
11445
|
-
for (const op of unit.update) {
|
|
11446
|
-
if (hasDependsOnSlotContextTrait(op)) {
|
|
11447
|
-
// We've found an op that depends on another slot other than the one that we want to move
|
|
11448
|
-
// the expressions to, after previously having seen the one we want to move to.
|
|
11449
|
-
if (moveAfterTarget !== null && previousTarget === moveAfterTarget &&
|
|
11450
|
-
op.target !== previousTarget) {
|
|
11451
|
-
OpList.insertBefore(opsToMove, op);
|
|
11452
|
-
moveAfterTarget = null;
|
|
11453
|
-
opsToMove = [];
|
|
11454
|
-
}
|
|
11455
|
-
previousTarget = op.target;
|
|
11456
|
-
}
|
|
11457
|
-
if (op.kind === OpKind.I18nExpression && op.usage === I18nExpressionFor.I18nText) {
|
|
11458
|
-
// This is an I18nExpressionOps that is used for text (not attributes).
|
|
11459
|
-
OpList.remove(op);
|
|
11460
|
-
opsToMove.push(op);
|
|
11461
|
-
const target = i18nLastSlotConsumers.get(op.i18nOwner);
|
|
11462
|
-
if (target === undefined) {
|
|
11463
|
-
throw new Error('AssertionError: Expected to find a last slot consumer for an I18nExpressionOp');
|
|
11484
|
+
if (hasDependsOnSlotContextTrait(updateOp) && updateOp.target !== createOp.xref) {
|
|
11485
|
+
break;
|
|
11486
|
+
}
|
|
11487
|
+
updateOp = updateOp.next;
|
|
11464
11488
|
}
|
|
11465
|
-
op.target = target;
|
|
11466
|
-
moveAfterTarget = op.target;
|
|
11467
11489
|
}
|
|
11468
11490
|
}
|
|
11469
|
-
if (moveAfterTarget !== null) {
|
|
11470
|
-
unit.update.push(opsToMove);
|
|
11471
|
-
}
|
|
11472
11491
|
}
|
|
11473
11492
|
}
|
|
11474
11493
|
|
|
@@ -11501,19 +11520,21 @@ function extractAttributes(job) {
|
|
|
11501
11520
|
case OpKind.Property:
|
|
11502
11521
|
if (!op.isAnimationTrigger) {
|
|
11503
11522
|
let bindingKind;
|
|
11504
|
-
if (op.
|
|
11523
|
+
if (op.i18nMessage !== null && op.templateKind === null) {
|
|
11505
11524
|
// If the binding has an i18n context, it is an i18n attribute, and should have that
|
|
11506
11525
|
// kind in the consts array.
|
|
11507
11526
|
bindingKind = BindingKind.I18n;
|
|
11508
11527
|
}
|
|
11509
|
-
else if (op.
|
|
11510
|
-
// TODO: How do i18n attributes on templates work?!
|
|
11528
|
+
else if (op.isStructuralTemplateAttribute) {
|
|
11511
11529
|
bindingKind = BindingKind.Template;
|
|
11512
11530
|
}
|
|
11513
11531
|
else {
|
|
11514
11532
|
bindingKind = BindingKind.Property;
|
|
11515
11533
|
}
|
|
11516
|
-
OpList.insertBefore(
|
|
11534
|
+
OpList.insertBefore(
|
|
11535
|
+
// Deliberaly null i18nMessage value
|
|
11536
|
+
createExtractedAttributeOp(op.target, bindingKind, op.name, /* expression */ null, /* i18nContext */ null,
|
|
11537
|
+
/* i18nMessage */ null, op.securityContext), lookupElement$2(elements, op.target));
|
|
11517
11538
|
}
|
|
11518
11539
|
break;
|
|
11519
11540
|
case OpKind.StyleProp:
|
|
@@ -11524,12 +11545,16 @@ function extractAttributes(job) {
|
|
|
11524
11545
|
// mode.
|
|
11525
11546
|
if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
|
|
11526
11547
|
op.expression instanceof EmptyExpr) {
|
|
11527
|
-
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name,
|
|
11548
|
+
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, /* expression */ null,
|
|
11549
|
+
/* i18nContext */ null,
|
|
11550
|
+
/* i18nMessage */ null, SecurityContext.STYLE), lookupElement$2(elements, op.target));
|
|
11528
11551
|
}
|
|
11529
11552
|
break;
|
|
11530
11553
|
case OpKind.Listener:
|
|
11531
11554
|
if (!op.isAnimationListener) {
|
|
11532
|
-
const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null,
|
|
11555
|
+
const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, /* expression */ null,
|
|
11556
|
+
/* i18nContext */ null,
|
|
11557
|
+
/* i18nMessage */ null, SecurityContext.NONE);
|
|
11533
11558
|
if (job.kind === CompilationJobKind.Host) {
|
|
11534
11559
|
// This attribute will apply to the enclosing host binding compilation unit, so order
|
|
11535
11560
|
// doesn't matter.
|
|
@@ -11561,24 +11586,14 @@ function extractAttributeOp(unit, op, elements) {
|
|
|
11561
11586
|
if (op.expression instanceof Interpolation) {
|
|
11562
11587
|
return;
|
|
11563
11588
|
}
|
|
11564
|
-
let extractable = op.expression.isConstant();
|
|
11589
|
+
let extractable = op.isTextAttribute || op.expression.isConstant();
|
|
11565
11590
|
if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
|
|
11566
|
-
// TemplateDefinitionBuilder only
|
|
11567
|
-
|
|
11568
|
-
|
|
11569
|
-
// For style and class attributes, TemplateDefinitionBuilder only extracted them if they were
|
|
11570
|
-
// text attributes. For example, `[attr.class]="'my-class'"` was not extracted despite being a
|
|
11571
|
-
// string literal, because it is not a text attribute.
|
|
11572
|
-
extractable &&= op.isTextAttribute;
|
|
11573
|
-
}
|
|
11574
|
-
if (unit.job.kind === CompilationJobKind.Host) {
|
|
11575
|
-
// TemplateDefinitionBuilder also does not seem to extract string literals if they are part of
|
|
11576
|
-
// a host attribute.
|
|
11577
|
-
extractable &&= op.isTextAttribute;
|
|
11578
|
-
}
|
|
11591
|
+
// TemplateDefinitionBuilder only extracts text attributes. It does not extract attriibute
|
|
11592
|
+
// bindings, even if they are constants.
|
|
11593
|
+
extractable &&= op.isTextAttribute;
|
|
11579
11594
|
}
|
|
11580
11595
|
if (extractable) {
|
|
11581
|
-
const extractedAttributeOp = createExtractedAttributeOp(op.target, op.
|
|
11596
|
+
const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isStructuralTemplateAttribute ? BindingKind.Template : BindingKind.Attribute, op.name, op.expression, op.i18nContext, op.i18nMessage, op.securityContext);
|
|
11582
11597
|
if (unit.job.kind === CompilationJobKind.Host) {
|
|
11583
11598
|
// This attribute will apply to the enclosing host binding compilation unit, so order doesn't
|
|
11584
11599
|
// matter.
|
|
@@ -11625,16 +11640,16 @@ function specializeBindings(job) {
|
|
|
11625
11640
|
target.nonBindable = true;
|
|
11626
11641
|
}
|
|
11627
11642
|
else {
|
|
11628
|
-
OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.
|
|
11643
|
+
OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.isStructuralTemplateAttribute, op.templateKind, op.i18nMessage, op.sourceSpan));
|
|
11629
11644
|
}
|
|
11630
11645
|
break;
|
|
11631
11646
|
case BindingKind.Property:
|
|
11632
11647
|
case BindingKind.Animation:
|
|
11633
11648
|
if (job.kind === CompilationJobKind.Host) {
|
|
11634
|
-
OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.i18nContext, op.sourceSpan));
|
|
11649
|
+
OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.i18nContext, op.securityContext, op.sourceSpan));
|
|
11635
11650
|
}
|
|
11636
11651
|
else {
|
|
11637
|
-
OpList.replace(op, createPropertyOp(op.target, op.name, op.expression, op.bindingKind === BindingKind.Animation, op.securityContext, op.
|
|
11652
|
+
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));
|
|
11638
11653
|
}
|
|
11639
11654
|
break;
|
|
11640
11655
|
case BindingKind.I18n:
|
|
@@ -11869,9 +11884,9 @@ function collectElementConsts(job) {
|
|
|
11869
11884
|
for (const unit of job.units) {
|
|
11870
11885
|
for (const op of unit.create) {
|
|
11871
11886
|
if (op.kind === OpKind.ExtractedAttribute) {
|
|
11872
|
-
const attributes = allElementAttributes.get(op.target) || new ElementAttributes();
|
|
11887
|
+
const attributes = allElementAttributes.get(op.target) || new ElementAttributes(job.compatibility);
|
|
11873
11888
|
allElementAttributes.set(op.target, attributes);
|
|
11874
|
-
attributes.add(op.bindingKind, op.name, op.expression);
|
|
11889
|
+
attributes.add(op.bindingKind, op.name, op.expression, op.trustedValueFn);
|
|
11875
11890
|
OpList.remove(op);
|
|
11876
11891
|
}
|
|
11877
11892
|
}
|
|
@@ -11914,11 +11929,6 @@ const FLYWEIGHT_ARRAY = Object.freeze([]);
|
|
|
11914
11929
|
* Container for all of the various kinds of attributes which are applied on an element.
|
|
11915
11930
|
*/
|
|
11916
11931
|
class ElementAttributes {
|
|
11917
|
-
constructor() {
|
|
11918
|
-
this.known = new Set();
|
|
11919
|
-
this.byKind = new Map;
|
|
11920
|
-
this.projectAs = null;
|
|
11921
|
-
}
|
|
11922
11932
|
get attributes() {
|
|
11923
11933
|
return this.byKind.get(BindingKind.Attribute) ?? FLYWEIGHT_ARRAY;
|
|
11924
11934
|
}
|
|
@@ -11937,11 +11947,32 @@ class ElementAttributes {
|
|
|
11937
11947
|
get i18n() {
|
|
11938
11948
|
return this.byKind.get(BindingKind.I18n) ?? FLYWEIGHT_ARRAY;
|
|
11939
11949
|
}
|
|
11940
|
-
|
|
11941
|
-
|
|
11950
|
+
constructor(compatibility) {
|
|
11951
|
+
this.compatibility = compatibility;
|
|
11952
|
+
this.known = new Map();
|
|
11953
|
+
this.byKind = new Map;
|
|
11954
|
+
this.projectAs = null;
|
|
11955
|
+
}
|
|
11956
|
+
isKnown(kind, name, value) {
|
|
11957
|
+
const nameToValue = this.known.get(kind) ?? new Set();
|
|
11958
|
+
this.known.set(kind, nameToValue);
|
|
11959
|
+
if (nameToValue.has(name)) {
|
|
11960
|
+
return true;
|
|
11961
|
+
}
|
|
11962
|
+
nameToValue.add(name);
|
|
11963
|
+
return false;
|
|
11964
|
+
}
|
|
11965
|
+
add(kind, name, value, trustedValueFn) {
|
|
11966
|
+
// TemplateDefinitionBuilder puts duplicate attribute, class, and style values into the consts
|
|
11967
|
+
// array. This seems inefficient, we can probably keep just the first one or the last value
|
|
11968
|
+
// (whichever actually gets applied when multiple values are listed for the same attribute).
|
|
11969
|
+
const allowDuplicates = this.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
|
|
11970
|
+
(kind === BindingKind.Attribute || kind === BindingKind.ClassName ||
|
|
11971
|
+
kind === BindingKind.StyleProperty);
|
|
11972
|
+
if (!allowDuplicates && this.isKnown(kind, name, value)) {
|
|
11942
11973
|
return;
|
|
11943
11974
|
}
|
|
11944
|
-
this
|
|
11975
|
+
// TODO: Can this be its own phase
|
|
11945
11976
|
if (name === 'ngProjectAs') {
|
|
11946
11977
|
if (value === null || !(value instanceof LiteralExpr) || (value.value == null) ||
|
|
11947
11978
|
(typeof value.value?.toString() !== 'string')) {
|
|
@@ -11957,7 +11988,15 @@ class ElementAttributes {
|
|
|
11957
11988
|
if (value === null) {
|
|
11958
11989
|
throw Error('Attribute, i18n attribute, & style element attributes must have a value');
|
|
11959
11990
|
}
|
|
11960
|
-
|
|
11991
|
+
if (trustedValueFn !== null) {
|
|
11992
|
+
if (!isStringLiteral(value)) {
|
|
11993
|
+
throw Error('AssertionError: extracted attribute value should be string literal');
|
|
11994
|
+
}
|
|
11995
|
+
array.push(taggedTemplate(trustedValueFn, new TemplateLiteral([new TemplateLiteralElement(value.value)], []), undefined, value.sourceSpan));
|
|
11996
|
+
}
|
|
11997
|
+
else {
|
|
11998
|
+
array.push(value);
|
|
11999
|
+
}
|
|
11961
12000
|
}
|
|
11962
12001
|
}
|
|
11963
12002
|
arrayFor(kind) {
|
|
@@ -11971,7 +12010,7 @@ class ElementAttributes {
|
|
|
11971
12010
|
* Gets an array of literal expressions representing the attribute's namespaced name.
|
|
11972
12011
|
*/
|
|
11973
12012
|
function getAttributeNameLiterals$1(name) {
|
|
11974
|
-
const [attributeNamespace, attributeName] = splitNsName(name);
|
|
12013
|
+
const [attributeNamespace, attributeName] = splitNsName(name, false);
|
|
11975
12014
|
const nameLiteral = literal(attributeName);
|
|
11976
12015
|
if (attributeNamespace) {
|
|
11977
12016
|
return [
|
|
@@ -12044,7 +12083,7 @@ function convertI18nBindings(job) {
|
|
|
12044
12083
|
if (op.expression.i18nPlaceholders.length !== op.expression.expressions.length) {
|
|
12045
12084
|
throw new Error(`AssertionError: An i18n attribute binding instruction requires the same number of expressions and placeholders, but found ${op.expression.i18nPlaceholders.length} placeholders and ${op.expression.expressions.length} expressions`);
|
|
12046
12085
|
}
|
|
12047
|
-
ops.push(createI18nExpressionOp(op.i18nContext, i18nAttributesForElem.target, i18nAttributesForElem.xref, i18nAttributesForElem.handle, expr, op.expression.i18nPlaceholders[i], I18nParamResolutionTime.Creation, I18nExpressionFor.I18nAttribute, op.name, op.sourceSpan));
|
|
12086
|
+
ops.push(createI18nExpressionOp(op.i18nContext, i18nAttributesForElem.target, i18nAttributesForElem.xref, i18nAttributesForElem.handle, expr, null, op.expression.i18nPlaceholders[i], I18nParamResolutionTime.Creation, I18nExpressionFor.I18nAttribute, op.name, op.sourceSpan));
|
|
12048
12087
|
}
|
|
12049
12088
|
OpList.replaceWithMany(op, ops);
|
|
12050
12089
|
break;
|
|
@@ -12081,7 +12120,8 @@ function createDeferDepsFns(job) {
|
|
|
12081
12120
|
if (op.handle.slot === null) {
|
|
12082
12121
|
throw new Error('AssertionError: slot must be assigned bfore extracting defer deps functions');
|
|
12083
12122
|
}
|
|
12084
|
-
op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${job.componentName}_Defer_${op.handle.slot}_DepsFn
|
|
12123
|
+
op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${job.componentName}_Defer_${op.handle.slot}_DepsFn`,
|
|
12124
|
+
/* Don't use unique names for TDB compatibility */ false);
|
|
12085
12125
|
}
|
|
12086
12126
|
}
|
|
12087
12127
|
}
|
|
@@ -12098,53 +12138,119 @@ function createDeferDepsFns(job) {
|
|
|
12098
12138
|
* message.)
|
|
12099
12139
|
*/
|
|
12100
12140
|
function createI18nContexts(job) {
|
|
12101
|
-
|
|
12102
|
-
|
|
12103
|
-
|
|
12141
|
+
// Create i18n context ops for i18n attrs.
|
|
12142
|
+
const attrContextByMessage = new Map();
|
|
12143
|
+
for (const unit of job.units) {
|
|
12144
|
+
for (const op of unit.ops()) {
|
|
12145
|
+
switch (op.kind) {
|
|
12146
|
+
case OpKind.Binding:
|
|
12147
|
+
case OpKind.Property:
|
|
12148
|
+
case OpKind.Attribute:
|
|
12149
|
+
case OpKind.ExtractedAttribute:
|
|
12150
|
+
if (op.i18nMessage === null) {
|
|
12151
|
+
continue;
|
|
12152
|
+
}
|
|
12153
|
+
if (!attrContextByMessage.has(op.i18nMessage)) {
|
|
12154
|
+
const i18nContext = createI18nContextOp(I18nContextKind.Attr, job.allocateXrefId(), null, op.i18nMessage, null);
|
|
12155
|
+
unit.create.push(i18nContext);
|
|
12156
|
+
attrContextByMessage.set(op.i18nMessage, i18nContext.xref);
|
|
12157
|
+
}
|
|
12158
|
+
op.i18nContext = attrContextByMessage.get(op.i18nMessage);
|
|
12159
|
+
break;
|
|
12160
|
+
}
|
|
12161
|
+
}
|
|
12162
|
+
}
|
|
12163
|
+
// Create i18n context ops for root i18n blocks.
|
|
12164
|
+
const blockContextByI18nBlock = new Map();
|
|
12104
12165
|
for (const unit of job.units) {
|
|
12105
12166
|
for (const op of unit.create) {
|
|
12106
12167
|
switch (op.kind) {
|
|
12107
12168
|
case OpKind.I18nStart:
|
|
12108
|
-
currentI18nOp = op;
|
|
12109
|
-
// Each root i18n block gets its own context, child ones refer to the context for their
|
|
12110
|
-
// root block.
|
|
12111
12169
|
if (op.xref === op.root) {
|
|
12112
|
-
|
|
12113
|
-
unit.create.push(
|
|
12114
|
-
op.context = xref;
|
|
12115
|
-
|
|
12170
|
+
const contextOp = createI18nContextOp(I18nContextKind.RootI18n, job.allocateXrefId(), op.xref, op.message, null);
|
|
12171
|
+
unit.create.push(contextOp);
|
|
12172
|
+
op.context = contextOp.xref;
|
|
12173
|
+
blockContextByI18nBlock.set(op.xref, contextOp);
|
|
12116
12174
|
}
|
|
12117
12175
|
break;
|
|
12176
|
+
}
|
|
12177
|
+
}
|
|
12178
|
+
}
|
|
12179
|
+
// Assign i18n contexts for child i18n blocks. These don't need their own conext, instead they
|
|
12180
|
+
// should inherit from their root i18n block.
|
|
12181
|
+
for (const unit of job.units) {
|
|
12182
|
+
for (const op of unit.create) {
|
|
12183
|
+
if (op.kind === OpKind.I18nStart && op.xref !== op.root) {
|
|
12184
|
+
const rootContext = blockContextByI18nBlock.get(op.root);
|
|
12185
|
+
if (rootContext === undefined) {
|
|
12186
|
+
throw Error('AssertionError: Root i18n block i18n context should have been created.');
|
|
12187
|
+
}
|
|
12188
|
+
op.context = rootContext.xref;
|
|
12189
|
+
blockContextByI18nBlock.set(op.xref, rootContext);
|
|
12190
|
+
}
|
|
12191
|
+
}
|
|
12192
|
+
}
|
|
12193
|
+
// Create or assign i18n contexts for ICUs.
|
|
12194
|
+
let currentI18nOp = null;
|
|
12195
|
+
for (const unit of job.units) {
|
|
12196
|
+
for (const op of unit.create) {
|
|
12197
|
+
switch (op.kind) {
|
|
12198
|
+
case OpKind.I18nStart:
|
|
12199
|
+
currentI18nOp = op;
|
|
12200
|
+
break;
|
|
12118
12201
|
case OpKind.I18nEnd:
|
|
12119
12202
|
currentI18nOp = null;
|
|
12120
12203
|
break;
|
|
12121
12204
|
case OpKind.IcuStart:
|
|
12122
|
-
// If an ICU represents a different message than its containing block, we give it its own
|
|
12123
|
-
// i18n context.
|
|
12124
12205
|
if (currentI18nOp === null) {
|
|
12125
|
-
throw Error('Unexpected ICU outside of an i18n block.');
|
|
12206
|
+
throw Error('AssertionError: Unexpected ICU outside of an i18n block.');
|
|
12126
12207
|
}
|
|
12127
12208
|
if (op.message.id !== currentI18nOp.message.id) {
|
|
12128
|
-
//
|
|
12129
|
-
|
|
12130
|
-
|
|
12131
|
-
|
|
12209
|
+
// This ICU is a sub-message inside its parent i18n block message. We need to give it
|
|
12210
|
+
// its own context.
|
|
12211
|
+
const contextOp = createI18nContextOp(I18nContextKind.Icu, job.allocateXrefId(), currentI18nOp.xref, op.message, null);
|
|
12212
|
+
unit.create.push(contextOp);
|
|
12213
|
+
op.context = contextOp.xref;
|
|
12132
12214
|
}
|
|
12133
12215
|
else {
|
|
12134
|
-
//
|
|
12135
|
-
// the
|
|
12216
|
+
// This ICU is the only translatable content in its parent i18n block. We need to
|
|
12217
|
+
// convert the parent's context into an ICU context.
|
|
12136
12218
|
op.context = currentI18nOp.context;
|
|
12219
|
+
blockContextByI18nBlock.get(currentI18nOp.xref).contextKind = I18nContextKind.Icu;
|
|
12137
12220
|
}
|
|
12138
12221
|
break;
|
|
12139
12222
|
}
|
|
12140
12223
|
}
|
|
12141
12224
|
}
|
|
12142
|
-
|
|
12143
|
-
|
|
12225
|
+
}
|
|
12226
|
+
|
|
12227
|
+
/**
|
|
12228
|
+
* Deduplicate text bindings, e.g. <div class="cls1" class="cls2">
|
|
12229
|
+
*/
|
|
12230
|
+
function deduplicateTextBindings(job) {
|
|
12231
|
+
const seen = new Map();
|
|
12144
12232
|
for (const unit of job.units) {
|
|
12145
|
-
for (const op of unit.
|
|
12146
|
-
if (op.kind === OpKind.
|
|
12147
|
-
|
|
12233
|
+
for (const op of unit.update.reversed()) {
|
|
12234
|
+
if (op.kind === OpKind.Binding && op.isTextAttribute) {
|
|
12235
|
+
const seenForElement = seen.get(op.target) || new Set();
|
|
12236
|
+
if (seenForElement.has(op.name)) {
|
|
12237
|
+
if (job.compatibility === CompatibilityMode.TemplateDefinitionBuilder) {
|
|
12238
|
+
// For most duplicated attributes, TemplateDefinitionBuilder lists all of the values in
|
|
12239
|
+
// the consts array. However, for style and class attributes it only keeps the last one.
|
|
12240
|
+
// We replicate that behavior here since it has actual consequences for apps with
|
|
12241
|
+
// duplicate class or style attrs.
|
|
12242
|
+
if (op.name === 'style' || op.name === 'class') {
|
|
12243
|
+
OpList.remove(op);
|
|
12244
|
+
}
|
|
12245
|
+
}
|
|
12246
|
+
else {
|
|
12247
|
+
// TODO: Determine the correct behavior. It would probably make sense to merge multiple
|
|
12248
|
+
// style and class attributes. Alternatively we could just throw an error, as HTML
|
|
12249
|
+
// doesn't permit duplicate attributes.
|
|
12250
|
+
}
|
|
12251
|
+
}
|
|
12252
|
+
seenForElement.add(op.name);
|
|
12253
|
+
seen.set(op.target, seenForElement);
|
|
12148
12254
|
}
|
|
12149
12255
|
}
|
|
12150
12256
|
}
|
|
@@ -12530,13 +12636,18 @@ const LIST_DELIMITER = '|';
|
|
|
12530
12636
|
* used in the final output.
|
|
12531
12637
|
*/
|
|
12532
12638
|
function extractI18nMessages(job) {
|
|
12533
|
-
//
|
|
12534
|
-
|
|
12639
|
+
// Create an i18n message for each context.
|
|
12640
|
+
// TODO: Merge the context op with the message op since they're 1:1 anyways.
|
|
12641
|
+
const i18nMessagesByContext = new Map();
|
|
12535
12642
|
const i18nBlocks = new Map();
|
|
12643
|
+
const i18nContexts = new Map();
|
|
12536
12644
|
for (const unit of job.units) {
|
|
12537
12645
|
for (const op of unit.create) {
|
|
12538
12646
|
switch (op.kind) {
|
|
12539
12647
|
case OpKind.I18nContext:
|
|
12648
|
+
const i18nMessageOp = createI18nMessage(job, op);
|
|
12649
|
+
unit.create.push(i18nMessageOp);
|
|
12650
|
+
i18nMessagesByContext.set(op.xref, i18nMessageOp);
|
|
12540
12651
|
i18nContexts.set(op.xref, op);
|
|
12541
12652
|
break;
|
|
12542
12653
|
case OpKind.I18nStart:
|
|
@@ -12545,54 +12656,47 @@ function extractI18nMessages(job) {
|
|
|
12545
12656
|
}
|
|
12546
12657
|
}
|
|
12547
12658
|
}
|
|
12548
|
-
//
|
|
12549
|
-
//
|
|
12550
|
-
|
|
12551
|
-
for (const unit of job.units) {
|
|
12552
|
-
for (const op of unit.create) {
|
|
12553
|
-
if (op.kind !== OpKind.I18nContext || op.contextKind !== I18nContextKind.Attr) {
|
|
12554
|
-
continue;
|
|
12555
|
-
}
|
|
12556
|
-
const i18nMessageOp = createI18nMessage(job, op);
|
|
12557
|
-
unit.create.push(i18nMessageOp);
|
|
12558
|
-
}
|
|
12559
|
-
}
|
|
12560
|
-
// Extract messages from root i18n blocks.
|
|
12561
|
-
const i18nBlockMessages = new Map();
|
|
12562
|
-
for (const unit of job.units) {
|
|
12563
|
-
for (const op of unit.create) {
|
|
12564
|
-
if (op.kind === OpKind.I18nStart && op.xref === op.root) {
|
|
12565
|
-
if (!op.context) {
|
|
12566
|
-
throw Error('I18n start op should have its context set.');
|
|
12567
|
-
}
|
|
12568
|
-
const i18nMessageOp = createI18nMessage(job, i18nContexts.get(op.context));
|
|
12569
|
-
i18nBlockMessages.set(op.xref, i18nMessageOp);
|
|
12570
|
-
unit.create.push(i18nMessageOp);
|
|
12571
|
-
}
|
|
12572
|
-
}
|
|
12573
|
-
}
|
|
12574
|
-
// Extract messages from ICUs with their own sub-context.
|
|
12659
|
+
// Associate sub-messages for ICUs with their root message. At this point we can also remove the
|
|
12660
|
+
// ICU start/end ops, as they are no longer needed.
|
|
12661
|
+
let currentIcu = null;
|
|
12575
12662
|
for (const unit of job.units) {
|
|
12576
12663
|
for (const op of unit.create) {
|
|
12577
12664
|
switch (op.kind) {
|
|
12578
12665
|
case OpKind.IcuStart:
|
|
12579
|
-
|
|
12580
|
-
|
|
12666
|
+
currentIcu = op;
|
|
12667
|
+
OpList.remove(op);
|
|
12668
|
+
// Skip any contexts not associated with an ICU.
|
|
12669
|
+
const icuContext = i18nContexts.get(op.context);
|
|
12670
|
+
if (icuContext.contextKind !== I18nContextKind.Icu) {
|
|
12671
|
+
continue;
|
|
12581
12672
|
}
|
|
12582
|
-
|
|
12583
|
-
|
|
12584
|
-
|
|
12585
|
-
|
|
12586
|
-
|
|
12587
|
-
const subMessage = createI18nMessage(job, i18nContext, op.messagePlaceholder);
|
|
12588
|
-
unit.create.push(subMessage);
|
|
12589
|
-
const rootI18nId = i18nBlocks.get(i18nContext.i18nBlock).root;
|
|
12590
|
-
const parentMessage = i18nBlockMessages.get(rootI18nId);
|
|
12591
|
-
parentMessage?.subMessages.push(subMessage.xref);
|
|
12673
|
+
// Skip ICUs that share a context with their i18n message. These represent root-level
|
|
12674
|
+
// ICUs, not sub-messages.
|
|
12675
|
+
const i18nBlock = i18nBlocks.get(icuContext.i18nBlock);
|
|
12676
|
+
if (i18nBlock.context === icuContext.xref) {
|
|
12677
|
+
continue;
|
|
12592
12678
|
}
|
|
12593
|
-
|
|
12679
|
+
// Find the root message and push this ICUs message as a sub-message.
|
|
12680
|
+
const rootI18nBlock = i18nBlocks.get(i18nBlock.root);
|
|
12681
|
+
const rootMessage = i18nMessagesByContext.get(rootI18nBlock.context);
|
|
12682
|
+
if (rootMessage === undefined) {
|
|
12683
|
+
throw Error('AssertionError: ICU sub-message should belong to a root message.');
|
|
12684
|
+
}
|
|
12685
|
+
const subMessage = i18nMessagesByContext.get(icuContext.xref);
|
|
12686
|
+
subMessage.messagePlaceholder = op.messagePlaceholder;
|
|
12687
|
+
rootMessage.subMessages.push(subMessage.xref);
|
|
12594
12688
|
break;
|
|
12595
12689
|
case OpKind.IcuEnd:
|
|
12690
|
+
currentIcu = null;
|
|
12691
|
+
OpList.remove(op);
|
|
12692
|
+
break;
|
|
12693
|
+
case OpKind.IcuPlaceholder:
|
|
12694
|
+
// Add ICU placeholders to the message, then remove the ICU placeholder ops.
|
|
12695
|
+
if (currentIcu === null || currentIcu.context == null) {
|
|
12696
|
+
throw Error('AssertionError: Unexpected ICU placeholder outside of i18n context');
|
|
12697
|
+
}
|
|
12698
|
+
const msg = i18nMessagesByContext.get(currentIcu.context);
|
|
12699
|
+
msg.postprocessingParams.set(op.name, literal(formatIcuPlaceholder(op)));
|
|
12596
12700
|
OpList.remove(op);
|
|
12597
12701
|
break;
|
|
12598
12702
|
}
|
|
@@ -12605,14 +12709,19 @@ function extractI18nMessages(job) {
|
|
|
12605
12709
|
function createI18nMessage(job, context, messagePlaceholder) {
|
|
12606
12710
|
let formattedParams = formatParams(context.params);
|
|
12607
12711
|
const formattedPostprocessingParams = formatParams(context.postprocessingParams);
|
|
12608
|
-
let needsPostprocessing =
|
|
12609
|
-
for (const values of context.params.values()) {
|
|
12610
|
-
if (values.length > 1) {
|
|
12611
|
-
needsPostprocessing = true;
|
|
12612
|
-
}
|
|
12613
|
-
}
|
|
12712
|
+
let needsPostprocessing = [...context.params.values()].some(v => v.length > 1);
|
|
12614
12713
|
return createI18nMessageOp(job.allocateXrefId(), context.xref, context.i18nBlock, context.message, messagePlaceholder ?? null, formattedParams, formattedPostprocessingParams, needsPostprocessing);
|
|
12615
12714
|
}
|
|
12715
|
+
/**
|
|
12716
|
+
* Formats an ICU placeholder into a single string with expression placeholders.
|
|
12717
|
+
*/
|
|
12718
|
+
function formatIcuPlaceholder(op) {
|
|
12719
|
+
if (op.strings.length !== op.expressionPlaceholders.length + 1) {
|
|
12720
|
+
throw Error(`AsserionError: Invalid ICU placeholder with ${op.strings.length} strings and ${op.expressionPlaceholders.length} expressions`);
|
|
12721
|
+
}
|
|
12722
|
+
const values = op.expressionPlaceholders.map(formatValue);
|
|
12723
|
+
return op.strings.flatMap((str, i) => [str, values[i] || '']).join('');
|
|
12724
|
+
}
|
|
12616
12725
|
/**
|
|
12617
12726
|
* Formats a map of `I18nParamValue[]` values into a map of `Expression` values.
|
|
12618
12727
|
*/
|
|
@@ -12812,9 +12921,15 @@ function recursivelyProcessView(view, parentScope) {
|
|
|
12812
12921
|
for (const op of view.create) {
|
|
12813
12922
|
switch (op.kind) {
|
|
12814
12923
|
case OpKind.Template:
|
|
12924
|
+
// Descend into child embedded views.
|
|
12925
|
+
recursivelyProcessView(view.job.views.get(op.xref), scope);
|
|
12926
|
+
break;
|
|
12815
12927
|
case OpKind.RepeaterCreate:
|
|
12816
12928
|
// Descend into child embedded views.
|
|
12817
12929
|
recursivelyProcessView(view.job.views.get(op.xref), scope);
|
|
12930
|
+
if (op.emptyView) {
|
|
12931
|
+
recursivelyProcessView(view.job.views.get(op.emptyView), scope);
|
|
12932
|
+
}
|
|
12818
12933
|
break;
|
|
12819
12934
|
case OpKind.Listener:
|
|
12820
12935
|
// Prepend variables to listener handler functions.
|
|
@@ -12945,7 +13060,7 @@ const BANG_IMPORTANT = '!important';
|
|
|
12945
13060
|
*/
|
|
12946
13061
|
function parseHostStyleProperties(job) {
|
|
12947
13062
|
for (const op of job.root.update) {
|
|
12948
|
-
if (op.kind
|
|
13063
|
+
if (!(op.kind === OpKind.Binding && op.bindingKind === BindingKind.Property)) {
|
|
12949
13064
|
continue;
|
|
12950
13065
|
}
|
|
12951
13066
|
if (op.name.endsWith(BANG_IMPORTANT)) {
|
|
@@ -19954,7 +20069,7 @@ const ESCAPE = '\uFFFD';
|
|
|
19954
20069
|
function collectI18nConsts(job) {
|
|
19955
20070
|
const fileBasedI18nSuffix = job.relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_').toUpperCase() + '_';
|
|
19956
20071
|
// Step One: Build up various lookup maps we need to collect all the consts.
|
|
19957
|
-
// Context Xref -> Extracted Attribute
|
|
20072
|
+
// Context Xref -> Extracted Attribute Ops
|
|
19958
20073
|
const extractedAttributesByI18nContext = new Map();
|
|
19959
20074
|
// Element/ElementStart Xref -> I18n Attributes config op
|
|
19960
20075
|
const i18nAttributesByElement = new Map();
|
|
@@ -19965,7 +20080,9 @@ function collectI18nConsts(job) {
|
|
|
19965
20080
|
for (const unit of job.units) {
|
|
19966
20081
|
for (const op of unit.ops()) {
|
|
19967
20082
|
if (op.kind === OpKind.ExtractedAttribute && op.i18nContext !== null) {
|
|
19968
|
-
extractedAttributesByI18nContext.
|
|
20083
|
+
const attributes = extractedAttributesByI18nContext.get(op.i18nContext) ?? [];
|
|
20084
|
+
attributes.push(op);
|
|
20085
|
+
extractedAttributesByI18nContext.set(op.i18nContext, attributes);
|
|
19969
20086
|
}
|
|
19970
20087
|
else if (op.kind === OpKind.I18nAttributes) {
|
|
19971
20088
|
i18nAttributesByElement.set(op.target, op);
|
|
@@ -20010,9 +20127,11 @@ function collectI18nConsts(job) {
|
|
|
20010
20127
|
i18nValuesByContext.set(op.i18nContext, mainVar);
|
|
20011
20128
|
// This i18n message may correspond to an individual extracted attribute. If so, The
|
|
20012
20129
|
// value of that attribute is updated to read the extracted i18n variable.
|
|
20013
|
-
const
|
|
20014
|
-
if (
|
|
20015
|
-
|
|
20130
|
+
const attributesForMessage = extractedAttributesByI18nContext.get(op.i18nContext);
|
|
20131
|
+
if (attributesForMessage !== undefined) {
|
|
20132
|
+
for (const attr of attributesForMessage) {
|
|
20133
|
+
attr.expression = mainVar.clone();
|
|
20134
|
+
}
|
|
20016
20135
|
}
|
|
20017
20136
|
}
|
|
20018
20137
|
}
|
|
@@ -20099,7 +20218,7 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
|
|
|
20099
20218
|
let transformFn = undefined;
|
|
20100
20219
|
// If nescessary, add a post-processing step and resolve any placeholder params that are
|
|
20101
20220
|
// set in post-processing.
|
|
20102
|
-
if (messageOp.needsPostprocessing) {
|
|
20221
|
+
if (messageOp.needsPostprocessing || messageOp.postprocessingParams.size > 0) {
|
|
20103
20222
|
// Sort the post-processing params for consistency with TemaplateDefinitionBuilder output.
|
|
20104
20223
|
const postprocessingParams = Object.fromEntries([...messageOp.postprocessingParams.entries()].sort());
|
|
20105
20224
|
const formattedPostprocessingParams = formatI18nPlaceholderNamesInMap(postprocessingParams, /* useCamelCase */ false);
|
|
@@ -20129,7 +20248,6 @@ function addSubMessageParams(messageOp, subMessagePlaceholders) {
|
|
|
20129
20248
|
else {
|
|
20130
20249
|
messageOp.params.set(placeholder, literal(`${ESCAPE}${I18N_ICU_MAPPING_PREFIX}${placeholder}${ESCAPE}`));
|
|
20131
20250
|
messageOp.postprocessingParams.set(placeholder, literalArr(subMessages));
|
|
20132
|
-
messageOp.needsPostprocessing = true;
|
|
20133
20251
|
}
|
|
20134
20252
|
}
|
|
20135
20253
|
}
|
|
@@ -20215,6 +20333,7 @@ function convertI18nText(job) {
|
|
|
20215
20333
|
let currentIcu = null;
|
|
20216
20334
|
const textNodeI18nBlocks = new Map();
|
|
20217
20335
|
const textNodeIcus = new Map();
|
|
20336
|
+
const icuPlaceholderByText = new Map();
|
|
20218
20337
|
for (const op of unit.create) {
|
|
20219
20338
|
switch (op.kind) {
|
|
20220
20339
|
case OpKind.I18nStart:
|
|
@@ -20239,7 +20358,19 @@ function convertI18nText(job) {
|
|
|
20239
20358
|
if (currentI18n !== null) {
|
|
20240
20359
|
textNodeI18nBlocks.set(op.xref, currentI18n);
|
|
20241
20360
|
textNodeIcus.set(op.xref, currentIcu);
|
|
20242
|
-
|
|
20361
|
+
if (op.icuPlaceholder !== null) {
|
|
20362
|
+
// Create an op to represent the ICU placeholder. Initially set its static text to the
|
|
20363
|
+
// value of the text op, though this may be overwritten later if this text op is a
|
|
20364
|
+
// placeholder for an interpolation.
|
|
20365
|
+
const icuPlaceholderOp = createIcuPlaceholderOp(job.allocateXrefId(), op.icuPlaceholder, [op.initialValue]);
|
|
20366
|
+
OpList.replace(op, icuPlaceholderOp);
|
|
20367
|
+
icuPlaceholderByText.set(op.xref, icuPlaceholderOp);
|
|
20368
|
+
}
|
|
20369
|
+
else {
|
|
20370
|
+
// Otherwise just remove the text op, since its value is already accounted for in the
|
|
20371
|
+
// translated message.
|
|
20372
|
+
OpList.remove(op);
|
|
20373
|
+
}
|
|
20243
20374
|
}
|
|
20244
20375
|
break;
|
|
20245
20376
|
}
|
|
@@ -20254,6 +20385,7 @@ function convertI18nText(job) {
|
|
|
20254
20385
|
}
|
|
20255
20386
|
const i18nOp = textNodeI18nBlocks.get(op.target);
|
|
20256
20387
|
const icuOp = textNodeIcus.get(op.target);
|
|
20388
|
+
const icuPlaceholder = icuPlaceholderByText.get(op.target);
|
|
20257
20389
|
const contextId = icuOp ? icuOp.context : i18nOp.context;
|
|
20258
20390
|
const resolutionTime = icuOp ? I18nParamResolutionTime.Postproccessing :
|
|
20259
20391
|
I18nParamResolutionTime.Creation;
|
|
@@ -20262,9 +20394,14 @@ function convertI18nText(job) {
|
|
|
20262
20394
|
const expr = op.interpolation.expressions[i];
|
|
20263
20395
|
// For now, this i18nExpression depends on the slot context of the enclosing i18n block.
|
|
20264
20396
|
// Later, we will modify this, and advance to a different point.
|
|
20265
|
-
ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.xref, i18nOp.handle, expr, op.interpolation.i18nPlaceholders[i], resolutionTime, I18nExpressionFor.I18nText, '', expr.sourceSpan ?? op.sourceSpan));
|
|
20397
|
+
ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.xref, i18nOp.handle, expr, icuPlaceholder?.xref ?? null, op.interpolation.i18nPlaceholders[i] ?? null, resolutionTime, I18nExpressionFor.I18nText, '', expr.sourceSpan ?? op.sourceSpan));
|
|
20266
20398
|
}
|
|
20267
20399
|
OpList.replaceWithMany(op, ops);
|
|
20400
|
+
// If this interpolation is part of an ICU placeholder, add the strings and expressions to
|
|
20401
|
+
// the placeholder.
|
|
20402
|
+
if (icuPlaceholder !== undefined) {
|
|
20403
|
+
icuPlaceholder.strings = op.interpolation.strings;
|
|
20404
|
+
}
|
|
20268
20405
|
break;
|
|
20269
20406
|
}
|
|
20270
20407
|
}
|
|
@@ -20793,21 +20930,39 @@ function keepLast(ops) {
|
|
|
20793
20930
|
* class property.
|
|
20794
20931
|
*/
|
|
20795
20932
|
function parseExtractedStyles(job) {
|
|
20933
|
+
const elements = new Map();
|
|
20934
|
+
for (const unit of job.units) {
|
|
20935
|
+
for (const op of unit.create) {
|
|
20936
|
+
if (isElementOrContainerOp(op)) {
|
|
20937
|
+
elements.set(op.xref, op);
|
|
20938
|
+
}
|
|
20939
|
+
}
|
|
20940
|
+
}
|
|
20796
20941
|
for (const unit of job.units) {
|
|
20797
20942
|
for (const op of unit.create) {
|
|
20798
20943
|
if (op.kind === OpKind.ExtractedAttribute && op.bindingKind === BindingKind.Attribute &&
|
|
20799
20944
|
isStringLiteral(op.expression)) {
|
|
20945
|
+
const target = elements.get(op.target);
|
|
20946
|
+
if (target !== undefined && target.kind === OpKind.Template &&
|
|
20947
|
+
target.templateKind === TemplateKind.Structural) {
|
|
20948
|
+
// TemplateDefinitionBuilder will not apply class and style bindings to structural
|
|
20949
|
+
// directives; instead, it will leave them as attributes.
|
|
20950
|
+
// (It's not clear what that would mean, anyway -- classes and styles on a structural
|
|
20951
|
+
// element should probably be a parse error.)
|
|
20952
|
+
// TODO: We may be able to remove this once Template Pipeline is the default.
|
|
20953
|
+
continue;
|
|
20954
|
+
}
|
|
20800
20955
|
if (op.name === 'style') {
|
|
20801
20956
|
const parsedStyles = parse(op.expression.value);
|
|
20802
20957
|
for (let i = 0; i < parsedStyles.length - 1; i += 2) {
|
|
20803
|
-
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1]), null), op);
|
|
20958
|
+
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1]), null, null, SecurityContext.STYLE), op);
|
|
20804
20959
|
}
|
|
20805
20960
|
OpList.remove(op);
|
|
20806
20961
|
}
|
|
20807
20962
|
else if (op.name === 'class') {
|
|
20808
20963
|
const parsedClasses = op.expression.value.trim().split(/\s+/g);
|
|
20809
20964
|
for (const parsedClass of parsedClasses) {
|
|
20810
|
-
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null, null), op);
|
|
20965
|
+
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null, null, null, SecurityContext.NONE), op);
|
|
20811
20966
|
}
|
|
20812
20967
|
OpList.remove(op);
|
|
20813
20968
|
}
|
|
@@ -20973,22 +21128,37 @@ function propagateI18nBlocksToTemplates(unit, subTemplateIndex) {
|
|
|
20973
21128
|
i18nBlock = null;
|
|
20974
21129
|
break;
|
|
20975
21130
|
case OpKind.Template:
|
|
20976
|
-
|
|
20977
|
-
|
|
20978
|
-
|
|
20979
|
-
|
|
20980
|
-
|
|
20981
|
-
|
|
20982
|
-
|
|
20983
|
-
|
|
20984
|
-
|
|
21131
|
+
subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.xref), i18nBlock, op.i18nPlaceholder, subTemplateIndex);
|
|
21132
|
+
break;
|
|
21133
|
+
case OpKind.RepeaterCreate:
|
|
21134
|
+
// Propagate i18n blocks to the @for template.
|
|
21135
|
+
const forView = unit.job.views.get(op.xref);
|
|
21136
|
+
subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.xref), i18nBlock, op.i18nPlaceholder, subTemplateIndex);
|
|
21137
|
+
// Then if there's an @empty template, propagate the i18n blocks for it as well.
|
|
21138
|
+
if (op.emptyView !== null) {
|
|
21139
|
+
subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.emptyView), i18nBlock, op.emptyI18nPlaceholder, subTemplateIndex);
|
|
20985
21140
|
}
|
|
20986
|
-
|
|
20987
|
-
subTemplateIndex = propagateI18nBlocksToTemplates(templateView, subTemplateIndex);
|
|
21141
|
+
break;
|
|
20988
21142
|
}
|
|
20989
21143
|
}
|
|
20990
21144
|
return subTemplateIndex;
|
|
20991
21145
|
}
|
|
21146
|
+
/**
|
|
21147
|
+
* Propagate i18n blocks for a view.
|
|
21148
|
+
*/
|
|
21149
|
+
function propagateI18nBlocksForView(view, i18nBlock, i18nPlaceholder, subTemplateIndex) {
|
|
21150
|
+
// We found an <ng-template> inside an i18n block; increment the sub-template counter and
|
|
21151
|
+
// wrap the template's view in a child i18n block.
|
|
21152
|
+
if (i18nPlaceholder !== undefined) {
|
|
21153
|
+
if (i18nBlock === null) {
|
|
21154
|
+
throw Error('Expected template with i18n placeholder to be in an i18n block.');
|
|
21155
|
+
}
|
|
21156
|
+
subTemplateIndex++;
|
|
21157
|
+
wrapTemplateWithI18n(view, i18nBlock);
|
|
21158
|
+
}
|
|
21159
|
+
// Continue traversing inside the template's view.
|
|
21160
|
+
return propagateI18nBlocksToTemplates(view, subTemplateIndex);
|
|
21161
|
+
}
|
|
20992
21162
|
/**
|
|
20993
21163
|
* Wraps a template view with i18n start and end ops.
|
|
20994
21164
|
*/
|
|
@@ -21154,17 +21324,13 @@ function disableBindings() {
|
|
|
21154
21324
|
function enableBindings() {
|
|
21155
21325
|
return call(Identifiers.enableBindings, [], null);
|
|
21156
21326
|
}
|
|
21157
|
-
function listener(name, handlerFn, sourceSpan) {
|
|
21158
|
-
|
|
21159
|
-
|
|
21160
|
-
|
|
21161
|
-
|
|
21162
|
-
}
|
|
21163
|
-
|
|
21164
|
-
return call(Identifiers.syntheticHostListener, [
|
|
21165
|
-
literal(name),
|
|
21166
|
-
handlerFn,
|
|
21167
|
-
], sourceSpan);
|
|
21327
|
+
function listener(name, handlerFn, eventTargetResolver, syntheticHost, sourceSpan) {
|
|
21328
|
+
const args = [literal(name), handlerFn];
|
|
21329
|
+
if (eventTargetResolver !== null) {
|
|
21330
|
+
args.push(literal(false)); // `useCapture` flag, defaults to `false`
|
|
21331
|
+
args.push(importExpr(eventTargetResolver));
|
|
21332
|
+
}
|
|
21333
|
+
return call(syntheticHost ? Identifiers.syntheticHostListener : Identifiers.listener, args, sourceSpan);
|
|
21168
21334
|
}
|
|
21169
21335
|
function pipe(slot, name) {
|
|
21170
21336
|
return call(Identifiers.pipe, [
|
|
@@ -21425,8 +21591,12 @@ function classMapInterpolate(strings, expressions, sourceSpan) {
|
|
|
21425
21591
|
const interpolationArgs = collateInterpolationArgs(strings, expressions);
|
|
21426
21592
|
return callVariadicInstruction(CLASS_MAP_INTERPOLATE_CONFIG, [], interpolationArgs, [], sourceSpan);
|
|
21427
21593
|
}
|
|
21428
|
-
function hostProperty(name, expression, sourceSpan) {
|
|
21429
|
-
|
|
21594
|
+
function hostProperty(name, expression, sanitizer, sourceSpan) {
|
|
21595
|
+
const args = [literal(name), expression];
|
|
21596
|
+
if (sanitizer !== null) {
|
|
21597
|
+
args.push(sanitizer);
|
|
21598
|
+
}
|
|
21599
|
+
return call(Identifiers.hostProperty, args, sourceSpan);
|
|
21430
21600
|
}
|
|
21431
21601
|
function syntheticHostProperty(name, expression, sourceSpan) {
|
|
21432
21602
|
return call(Identifiers.syntheticHostProperty, [literal(name), expression], sourceSpan);
|
|
@@ -21644,14 +21814,12 @@ function callVariadicInstruction(config, baseArgs, interpolationArgs, extraArgs,
|
|
|
21644
21814
|
}
|
|
21645
21815
|
|
|
21646
21816
|
/**
|
|
21647
|
-
* Map of
|
|
21817
|
+
* Map of target resolvers for event listeners.
|
|
21648
21818
|
*/
|
|
21649
|
-
const
|
|
21650
|
-
[
|
|
21651
|
-
[
|
|
21652
|
-
[
|
|
21653
|
-
[SanitizerFn.Script, Identifiers.sanitizeScript],
|
|
21654
|
-
[SanitizerFn.Style, Identifiers.sanitizeStyle], [SanitizerFn.Url, Identifiers.sanitizeUrl]
|
|
21819
|
+
const GLOBAL_TARGET_RESOLVERS$1 = new Map([
|
|
21820
|
+
['window', Identifiers.resolveWindow],
|
|
21821
|
+
['document', Identifiers.resolveDocument],
|
|
21822
|
+
['body', Identifiers.resolveBody],
|
|
21655
21823
|
]);
|
|
21656
21824
|
/**
|
|
21657
21825
|
* Compiles semantic operations across all views and generates output `o.Statement`s with actual
|
|
@@ -21675,19 +21843,19 @@ function reifyCreateOperations(unit, ops) {
|
|
|
21675
21843
|
OpList.replace(op, text(op.handle.slot, op.initialValue, op.sourceSpan));
|
|
21676
21844
|
break;
|
|
21677
21845
|
case OpKind.ElementStart:
|
|
21678
|
-
OpList.replace(op, elementStart(op.handle.slot, op.tag, op.attributes, op.localRefs, op.
|
|
21846
|
+
OpList.replace(op, elementStart(op.handle.slot, op.tag, op.attributes, op.localRefs, op.startSourceSpan));
|
|
21679
21847
|
break;
|
|
21680
21848
|
case OpKind.Element:
|
|
21681
|
-
OpList.replace(op, element(op.handle.slot, op.tag, op.attributes, op.localRefs, op.
|
|
21849
|
+
OpList.replace(op, element(op.handle.slot, op.tag, op.attributes, op.localRefs, op.wholeSourceSpan));
|
|
21682
21850
|
break;
|
|
21683
21851
|
case OpKind.ElementEnd:
|
|
21684
21852
|
OpList.replace(op, elementEnd(op.sourceSpan));
|
|
21685
21853
|
break;
|
|
21686
21854
|
case OpKind.ContainerStart:
|
|
21687
|
-
OpList.replace(op, elementContainerStart(op.handle.slot, op.attributes, op.localRefs, op.
|
|
21855
|
+
OpList.replace(op, elementContainerStart(op.handle.slot, op.attributes, op.localRefs, op.startSourceSpan));
|
|
21688
21856
|
break;
|
|
21689
21857
|
case OpKind.Container:
|
|
21690
|
-
OpList.replace(op, elementContainer(op.handle.slot, op.attributes, op.localRefs, op.
|
|
21858
|
+
OpList.replace(op, elementContainer(op.handle.slot, op.attributes, op.localRefs, op.wholeSourceSpan));
|
|
21691
21859
|
break;
|
|
21692
21860
|
case OpKind.ContainerEnd:
|
|
21693
21861
|
OpList.replace(op, elementContainerEnd());
|
|
@@ -21715,7 +21883,7 @@ function reifyCreateOperations(unit, ops) {
|
|
|
21715
21883
|
throw new Error(`AssertionError: local refs array should have been extracted into a constant`);
|
|
21716
21884
|
}
|
|
21717
21885
|
const childView = unit.job.views.get(op.xref);
|
|
21718
|
-
OpList.replace(op, template(op.handle.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes, op.localRefs, op.
|
|
21886
|
+
OpList.replace(op, template(op.handle.slot, variable(childView.fnName), childView.decls, childView.vars, op.tag, op.attributes, op.localRefs, op.startSourceSpan));
|
|
21719
21887
|
break;
|
|
21720
21888
|
case OpKind.DisableBindings:
|
|
21721
21889
|
OpList.replace(op, disableBindings());
|
|
@@ -21728,10 +21896,11 @@ function reifyCreateOperations(unit, ops) {
|
|
|
21728
21896
|
break;
|
|
21729
21897
|
case OpKind.Listener:
|
|
21730
21898
|
const listenerFn = reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, op.consumesDollarEvent);
|
|
21731
|
-
const
|
|
21732
|
-
|
|
21733
|
-
|
|
21734
|
-
|
|
21899
|
+
const eventTargetResolver = op.eventTarget ? GLOBAL_TARGET_RESOLVERS$1.get(op.eventTarget) : null;
|
|
21900
|
+
if (eventTargetResolver === undefined) {
|
|
21901
|
+
throw new Error(`Unexpected global target '${op.eventTarget}' defined for '${op.name}' event. Supported list of global targets: window,document,body.`);
|
|
21902
|
+
}
|
|
21903
|
+
OpList.replace(op, listener(op.name, listenerFn, eventTargetResolver, op.hostListener && op.isAnimationListener, op.sourceSpan));
|
|
21735
21904
|
break;
|
|
21736
21905
|
case OpKind.Variable:
|
|
21737
21906
|
if (op.variable.name === null) {
|
|
@@ -21816,7 +21985,7 @@ function reifyCreateOperations(unit, ops) {
|
|
|
21816
21985
|
emptyDecls = emptyView.decls;
|
|
21817
21986
|
emptyVars = emptyView.vars;
|
|
21818
21987
|
}
|
|
21819
|
-
OpList.replace(op, repeaterCreate(op.handle.slot, repeaterView.fnName, op.decls, op.vars, op.tag, op.attributes, op.trackByFn, op.usesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, op.
|
|
21988
|
+
OpList.replace(op, repeaterCreate(op.handle.slot, repeaterView.fnName, op.decls, op.vars, op.tag, op.attributes, op.trackByFn, op.usesComponentInstance, emptyViewFnName, emptyDecls, emptyVars, op.wholeSourceSpan));
|
|
21820
21989
|
break;
|
|
21821
21990
|
case OpKind.Statement:
|
|
21822
21991
|
// Pass statement operations directly through.
|
|
@@ -21894,7 +22063,7 @@ function reifyUpdateOperations(_unit, ops) {
|
|
|
21894
22063
|
OpList.replace(op, syntheticHostProperty(op.name, op.expression, op.sourceSpan));
|
|
21895
22064
|
}
|
|
21896
22065
|
else {
|
|
21897
|
-
OpList.replace(op, hostProperty(op.name, op.expression, op.sourceSpan));
|
|
22066
|
+
OpList.replace(op, hostProperty(op.name, op.expression, op.sanitizer, op.sourceSpan));
|
|
21898
22067
|
}
|
|
21899
22068
|
}
|
|
21900
22069
|
break;
|
|
@@ -21973,8 +22142,6 @@ function reifyIrExpression(expr) {
|
|
|
21973
22142
|
return pipeBind(expr.targetSlot.slot, expr.varOffset, expr.args);
|
|
21974
22143
|
case ExpressionKind.PipeBindingVariadic:
|
|
21975
22144
|
return pipeBindV(expr.targetSlot.slot, expr.varOffset, expr.args);
|
|
21976
|
-
case ExpressionKind.SanitizerExpr:
|
|
21977
|
-
return importExpr(sanitizerIdentifierMap.get(expr.fn));
|
|
21978
22145
|
case ExpressionKind.SlotLiteralExpr:
|
|
21979
22146
|
return literal(expr.slot.slot);
|
|
21980
22147
|
default:
|
|
@@ -22267,10 +22434,11 @@ function resolvePlaceholdersForView(job, unit, i18nContexts, elements, pendingSt
|
|
|
22267
22434
|
}
|
|
22268
22435
|
break;
|
|
22269
22436
|
case OpKind.Template:
|
|
22437
|
+
const view = job.views.get(op.xref);
|
|
22270
22438
|
if (op.i18nPlaceholder === undefined) {
|
|
22271
22439
|
// If there is no i18n placeholder, just recurse into the view in case it contains i18n
|
|
22272
22440
|
// blocks.
|
|
22273
|
-
resolvePlaceholdersForView(job,
|
|
22441
|
+
resolvePlaceholdersForView(job, view, i18nContexts, elements);
|
|
22274
22442
|
}
|
|
22275
22443
|
else {
|
|
22276
22444
|
if (currentOps === null) {
|
|
@@ -22281,14 +22449,59 @@ function resolvePlaceholdersForView(job, unit, i18nContexts, elements, pendingSt
|
|
|
22281
22449
|
// the current template as a pending structural directive to be recorded when we find
|
|
22282
22450
|
// the element, content, or template it belongs to. This allows us to create combined
|
|
22283
22451
|
// values that represent, e.g. the start of a template and element at the same time.
|
|
22284
|
-
resolvePlaceholdersForView(job,
|
|
22452
|
+
resolvePlaceholdersForView(job, view, i18nContexts, elements, op);
|
|
22285
22453
|
}
|
|
22286
22454
|
else {
|
|
22287
22455
|
// If this is some other kind of template, we can record its start, recurse into its
|
|
22288
22456
|
// view, and then record its end.
|
|
22289
|
-
recordTemplateStart(job, op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22290
|
-
resolvePlaceholdersForView(job,
|
|
22291
|
-
recordTemplateClose(job, op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22457
|
+
recordTemplateStart(job, view, op.handle.slot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22458
|
+
resolvePlaceholdersForView(job, view, i18nContexts, elements);
|
|
22459
|
+
recordTemplateClose(job, view, op.handle.slot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22460
|
+
pendingStructuralDirective = undefined;
|
|
22461
|
+
}
|
|
22462
|
+
}
|
|
22463
|
+
break;
|
|
22464
|
+
case OpKind.RepeaterCreate:
|
|
22465
|
+
if (pendingStructuralDirective !== undefined) {
|
|
22466
|
+
throw Error('AssertionError: Unexpected structural directive associated with @for block');
|
|
22467
|
+
}
|
|
22468
|
+
// RepeaterCreate has 3 slots: the first is for the op itself, the second is for the @for
|
|
22469
|
+
// template and the (optional) third is for the @empty template.
|
|
22470
|
+
const forSlot = op.handle.slot + 1;
|
|
22471
|
+
const forView = job.views.get(op.xref);
|
|
22472
|
+
// First record all of the placeholders for the @for template.
|
|
22473
|
+
if (op.i18nPlaceholder === undefined) {
|
|
22474
|
+
// If there is no i18n placeholder, just recurse into the view in case it contains i18n
|
|
22475
|
+
// blocks.
|
|
22476
|
+
resolvePlaceholdersForView(job, forView, i18nContexts, elements);
|
|
22477
|
+
}
|
|
22478
|
+
else {
|
|
22479
|
+
if (currentOps === null) {
|
|
22480
|
+
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
22481
|
+
}
|
|
22482
|
+
recordTemplateStart(job, forView, forSlot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22483
|
+
resolvePlaceholdersForView(job, forView, i18nContexts, elements);
|
|
22484
|
+
recordTemplateClose(job, forView, forSlot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22485
|
+
pendingStructuralDirective = undefined;
|
|
22486
|
+
}
|
|
22487
|
+
// Then if there's an @empty template, add its placeholders as well.
|
|
22488
|
+
if (op.emptyView !== null) {
|
|
22489
|
+
// RepeaterCreate has 3 slots: the first is for the op itself, the second is for the @for
|
|
22490
|
+
// template and the (optional) third is for the @empty template.
|
|
22491
|
+
const emptySlot = op.handle.slot + 2;
|
|
22492
|
+
const emptyView = job.views.get(op.emptyView);
|
|
22493
|
+
if (op.emptyI18nPlaceholder === undefined) {
|
|
22494
|
+
// If there is no i18n placeholder, just recurse into the view in case it contains i18n
|
|
22495
|
+
// blocks.
|
|
22496
|
+
resolvePlaceholdersForView(job, emptyView, i18nContexts, elements);
|
|
22497
|
+
}
|
|
22498
|
+
else {
|
|
22499
|
+
if (currentOps === null) {
|
|
22500
|
+
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
22501
|
+
}
|
|
22502
|
+
recordTemplateStart(job, emptyView, emptySlot, op.emptyI18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22503
|
+
resolvePlaceholdersForView(job, emptyView, i18nContexts, elements);
|
|
22504
|
+
recordTemplateClose(job, emptyView, emptySlot, op.emptyI18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22292
22505
|
pendingStructuralDirective = undefined;
|
|
22293
22506
|
}
|
|
22294
22507
|
}
|
|
@@ -22336,8 +22549,8 @@ function recordElementClose(op, i18nContext, i18nBlock, structuralDirective) {
|
|
|
22336
22549
|
/**
|
|
22337
22550
|
* Records an i18n param value for the start of a template.
|
|
22338
22551
|
*/
|
|
22339
|
-
function recordTemplateStart(job,
|
|
22340
|
-
let { startName, closeName } =
|
|
22552
|
+
function recordTemplateStart(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) {
|
|
22553
|
+
let { startName, closeName } = i18nPlaceholder;
|
|
22341
22554
|
let flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.OpenTag;
|
|
22342
22555
|
// For self-closing tags, there is no close tag placeholder. Instead, the start tag
|
|
22343
22556
|
// placeholder accounts for the start and close of the element.
|
|
@@ -22352,20 +22565,20 @@ function recordTemplateStart(job, op, i18nContext, i18nBlock, structuralDirectiv
|
|
|
22352
22565
|
}
|
|
22353
22566
|
// Record the start of the template. For the sub-template index, pass the index for the template's
|
|
22354
22567
|
// view, rather than the current i18n block's index.
|
|
22355
|
-
addParam(i18nContext.params, startName,
|
|
22568
|
+
addParam(i18nContext.params, startName, slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, view), flags);
|
|
22356
22569
|
}
|
|
22357
22570
|
/**
|
|
22358
22571
|
* Records an i18n param value for the closing of a template.
|
|
22359
22572
|
*/
|
|
22360
|
-
function recordTemplateClose(job,
|
|
22361
|
-
const { startName, closeName } =
|
|
22573
|
+
function recordTemplateClose(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) {
|
|
22574
|
+
const { startName, closeName } = i18nPlaceholder;
|
|
22362
22575
|
const flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag;
|
|
22363
22576
|
// Self-closing tags don't have a closing tag placeholder, instead the template's closing is
|
|
22364
22577
|
// recorded via an additional flag on the template start value.
|
|
22365
22578
|
if (closeName) {
|
|
22366
22579
|
// Record the closing of the template. For the sub-template index, pass the index for the
|
|
22367
22580
|
// template's view, rather than the current i18n block's index.
|
|
22368
|
-
addParam(i18nContext.params, closeName,
|
|
22581
|
+
addParam(i18nContext.params, closeName, slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, view), flags);
|
|
22369
22582
|
// If the template is associated with a structural directive, record the structural directive's
|
|
22370
22583
|
// closing after. Since this template must be in the structural directive's view, we can just
|
|
22371
22584
|
// directly use the current i18n block's sub-template index.
|
|
@@ -22378,8 +22591,8 @@ function recordTemplateClose(job, op, i18nContext, i18nBlock, structuralDirectiv
|
|
|
22378
22591
|
* Get the subTemplateIndex for the given template op. For template ops, use the subTemplateIndex of
|
|
22379
22592
|
* the child i18n block inside the template.
|
|
22380
22593
|
*/
|
|
22381
|
-
function getSubTemplateIndexForTemplateTag(job, i18nOp,
|
|
22382
|
-
for (const childOp of
|
|
22594
|
+
function getSubTemplateIndexForTemplateTag(job, i18nOp, view) {
|
|
22595
|
+
for (const childOp of view.create) {
|
|
22383
22596
|
if (childOp.kind === OpKind.I18nStart) {
|
|
22384
22597
|
return childOp.subTemplateIndex;
|
|
22385
22598
|
}
|
|
@@ -22402,6 +22615,7 @@ function resolveI18nExpressionPlaceholders(job) {
|
|
|
22402
22615
|
// Record all of the i18n context ops, and the sub-template index for each i18n op.
|
|
22403
22616
|
const subTemplateIndicies = new Map();
|
|
22404
22617
|
const i18nContexts = new Map();
|
|
22618
|
+
const icuPlaceholders = new Map();
|
|
22405
22619
|
for (const unit of job.units) {
|
|
22406
22620
|
for (const op of unit.create) {
|
|
22407
22621
|
switch (op.kind) {
|
|
@@ -22411,6 +22625,9 @@ function resolveI18nExpressionPlaceholders(job) {
|
|
|
22411
22625
|
case OpKind.I18nContext:
|
|
22412
22626
|
i18nContexts.set(op.xref, op);
|
|
22413
22627
|
break;
|
|
22628
|
+
case OpKind.IcuPlaceholder:
|
|
22629
|
+
icuPlaceholders.set(op.xref, op);
|
|
22630
|
+
break;
|
|
22414
22631
|
}
|
|
22415
22632
|
}
|
|
22416
22633
|
}
|
|
@@ -22424,76 +22641,32 @@ function resolveI18nExpressionPlaceholders(job) {
|
|
|
22424
22641
|
for (const unit of job.units) {
|
|
22425
22642
|
for (const op of unit.update) {
|
|
22426
22643
|
if (op.kind === OpKind.I18nExpression) {
|
|
22427
|
-
const i18nContext = i18nContexts.get(op.context);
|
|
22428
22644
|
const index = expressionIndices.get(referenceIndex(op)) || 0;
|
|
22429
22645
|
const subTemplateIndex = subTemplateIndicies.get(op.i18nOwner) ?? null;
|
|
22430
|
-
|
|
22431
|
-
const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
|
|
22432
|
-
i18nContext.params :
|
|
22433
|
-
i18nContext.postprocessingParams;
|
|
22434
|
-
const values = params.get(op.i18nPlaceholder) || [];
|
|
22435
|
-
values.push({
|
|
22646
|
+
const value = {
|
|
22436
22647
|
value: index,
|
|
22437
22648
|
subTemplateIndex: subTemplateIndex,
|
|
22438
22649
|
flags: I18nParamValueFlags.ExpressionIndex
|
|
22439
|
-
}
|
|
22440
|
-
|
|
22650
|
+
};
|
|
22651
|
+
updatePlaceholder(op, value, i18nContexts, icuPlaceholders);
|
|
22441
22652
|
expressionIndices.set(referenceIndex(op), index + 1);
|
|
22442
22653
|
}
|
|
22443
22654
|
}
|
|
22444
22655
|
}
|
|
22445
22656
|
}
|
|
22446
|
-
|
|
22447
|
-
|
|
22448
|
-
|
|
22449
|
-
|
|
22450
|
-
|
|
22451
|
-
|
|
22452
|
-
|
|
22453
|
-
|
|
22454
|
-
|
|
22455
|
-
node.visit(new ResolveIcuPlaceholdersVisitor(op.postprocessingParams));
|
|
22456
|
-
}
|
|
22457
|
-
}
|
|
22458
|
-
}
|
|
22657
|
+
function updatePlaceholder(op, value, i18nContexts, icuPlaceholders) {
|
|
22658
|
+
if (op.i18nPlaceholder !== null) {
|
|
22659
|
+
const i18nContext = i18nContexts.get(op.context);
|
|
22660
|
+
const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
|
|
22661
|
+
i18nContext.params :
|
|
22662
|
+
i18nContext.postprocessingParams;
|
|
22663
|
+
const values = params.get(op.i18nPlaceholder) || [];
|
|
22664
|
+
values.push(value);
|
|
22665
|
+
params.set(op.i18nPlaceholder, values);
|
|
22459
22666
|
}
|
|
22460
|
-
|
|
22461
|
-
|
|
22462
|
-
|
|
22463
|
-
*/
|
|
22464
|
-
class ResolveIcuPlaceholdersVisitor extends RecurseVisitor {
|
|
22465
|
-
constructor(params) {
|
|
22466
|
-
super();
|
|
22467
|
-
this.params = params;
|
|
22468
|
-
}
|
|
22469
|
-
visitContainerPlaceholder(placeholder) {
|
|
22470
|
-
// Add the start and end source span for container placeholders. These need to be recorded for
|
|
22471
|
-
// elements inside ICUs. The slots for the nodes were recorded separately under the i18n
|
|
22472
|
-
// block's context as part of the `resolveI18nElementPlaceholders` phase.
|
|
22473
|
-
if (placeholder.startName && placeholder.startSourceSpan &&
|
|
22474
|
-
!this.params.has(placeholder.startName)) {
|
|
22475
|
-
this.params.set(placeholder.startName, [{
|
|
22476
|
-
value: placeholder.startSourceSpan?.toString(),
|
|
22477
|
-
subTemplateIndex: null,
|
|
22478
|
-
flags: I18nParamValueFlags.None
|
|
22479
|
-
}]);
|
|
22480
|
-
}
|
|
22481
|
-
if (placeholder.closeName && placeholder.endSourceSpan &&
|
|
22482
|
-
!this.params.has(placeholder.closeName)) {
|
|
22483
|
-
this.params.set(placeholder.closeName, [{
|
|
22484
|
-
value: placeholder.endSourceSpan?.toString(),
|
|
22485
|
-
subTemplateIndex: null,
|
|
22486
|
-
flags: I18nParamValueFlags.None
|
|
22487
|
-
}]);
|
|
22488
|
-
}
|
|
22489
|
-
}
|
|
22490
|
-
visitTagPlaceholder(placeholder) {
|
|
22491
|
-
super.visitTagPlaceholder(placeholder);
|
|
22492
|
-
this.visitContainerPlaceholder(placeholder);
|
|
22493
|
-
}
|
|
22494
|
-
visitBlockPlaceholder(placeholder) {
|
|
22495
|
-
super.visitBlockPlaceholder(placeholder);
|
|
22496
|
-
this.visitContainerPlaceholder(placeholder);
|
|
22667
|
+
if (op.icuPlaceholder !== null) {
|
|
22668
|
+
const icuPlaceholderOp = icuPlaceholders.get(op.icuPlaceholder);
|
|
22669
|
+
icuPlaceholderOp?.expressionPlaceholders.push(value);
|
|
22497
22670
|
}
|
|
22498
22671
|
}
|
|
22499
22672
|
|
|
@@ -22596,12 +22769,20 @@ function processLexicalScope(unit, ops, savedView) {
|
|
|
22596
22769
|
}
|
|
22597
22770
|
|
|
22598
22771
|
/**
|
|
22599
|
-
*
|
|
22772
|
+
* Map of security contexts to their sanitizer function.
|
|
22600
22773
|
*/
|
|
22601
|
-
const
|
|
22602
|
-
[SecurityContext.HTML,
|
|
22603
|
-
[SecurityContext.
|
|
22604
|
-
[SecurityContext.
|
|
22774
|
+
const sanitizerFns = new Map([
|
|
22775
|
+
[SecurityContext.HTML, Identifiers.sanitizeHtml],
|
|
22776
|
+
[SecurityContext.RESOURCE_URL, Identifiers.sanitizeResourceUrl],
|
|
22777
|
+
[SecurityContext.SCRIPT, Identifiers.sanitizeScript],
|
|
22778
|
+
[SecurityContext.STYLE, Identifiers.sanitizeStyle], [SecurityContext.URL, Identifiers.sanitizeUrl]
|
|
22779
|
+
]);
|
|
22780
|
+
/**
|
|
22781
|
+
* Map of security contexts to their trusted value function.
|
|
22782
|
+
*/
|
|
22783
|
+
const trustedValueFns = new Map([
|
|
22784
|
+
[SecurityContext.HTML, Identifiers.trustConstantHtml],
|
|
22785
|
+
[SecurityContext.RESOURCE_URL, Identifiers.trustConstantResourceUrl],
|
|
22605
22786
|
]);
|
|
22606
22787
|
/**
|
|
22607
22788
|
* Resolves sanitization functions for ops that need them.
|
|
@@ -22609,24 +22790,61 @@ const sanitizers = new Map([
|
|
|
22609
22790
|
function resolveSanitizers(job) {
|
|
22610
22791
|
for (const unit of job.units) {
|
|
22611
22792
|
const elements = createOpXrefMap(unit);
|
|
22612
|
-
|
|
22793
|
+
// For normal element bindings we create trusted values for security sensitive constant
|
|
22794
|
+
// attributes. However, for host bindings we skip this step (this matches what
|
|
22795
|
+
// TemplateDefinitionBuilder does).
|
|
22796
|
+
// TODO: Is the TDB behavior correct here?
|
|
22797
|
+
if (job.kind !== CompilationJobKind.Host) {
|
|
22798
|
+
for (const op of unit.create) {
|
|
22799
|
+
if (op.kind === OpKind.ExtractedAttribute) {
|
|
22800
|
+
const trustedValueFn = trustedValueFns.get(getOnlySecurityContext(op.securityContext)) ?? null;
|
|
22801
|
+
op.trustedValueFn = trustedValueFn !== null ? importExpr(trustedValueFn) : null;
|
|
22802
|
+
}
|
|
22803
|
+
}
|
|
22804
|
+
}
|
|
22613
22805
|
for (const op of unit.update) {
|
|
22614
22806
|
switch (op.kind) {
|
|
22615
22807
|
case OpKind.Property:
|
|
22616
22808
|
case OpKind.Attribute:
|
|
22617
|
-
|
|
22618
|
-
|
|
22809
|
+
case OpKind.HostProperty:
|
|
22810
|
+
let sanitizerFn = null;
|
|
22811
|
+
if (Array.isArray(op.securityContext) && op.securityContext.length === 2 &&
|
|
22812
|
+
op.securityContext.indexOf(SecurityContext.URL) > -1 &&
|
|
22813
|
+
op.securityContext.indexOf(SecurityContext.RESOURCE_URL) > -1) {
|
|
22814
|
+
// When the host element isn't known, some URL attributes (such as "src" and "href") may
|
|
22815
|
+
// be part of multiple different security contexts. In this case we use special
|
|
22816
|
+
// sanitization function and select the actual sanitizer at runtime based on a tag name
|
|
22817
|
+
// that is provided while invoking sanitization function.
|
|
22818
|
+
sanitizerFn = Identifiers.sanitizeUrlOrResourceUrl;
|
|
22819
|
+
}
|
|
22820
|
+
else {
|
|
22821
|
+
sanitizerFn = sanitizerFns.get(getOnlySecurityContext(op.securityContext)) ?? null;
|
|
22822
|
+
}
|
|
22823
|
+
op.sanitizer = sanitizerFn !== null ? importExpr(sanitizerFn) : null;
|
|
22619
22824
|
// If there was no sanitization function found based on the security context of an
|
|
22620
22825
|
// attribute/property, check whether this attribute/property is one of the
|
|
22621
22826
|
// security-sensitive <iframe> attributes (and that the current element is actually an
|
|
22622
22827
|
// <iframe>).
|
|
22623
22828
|
if (op.sanitizer === null) {
|
|
22624
|
-
|
|
22625
|
-
if (
|
|
22626
|
-
|
|
22829
|
+
let isIframe = false;
|
|
22830
|
+
if (job.kind === CompilationJobKind.Host || op.kind === OpKind.HostProperty) {
|
|
22831
|
+
// Note: for host bindings defined on a directive, we do not try to find all
|
|
22832
|
+
// possible places where it can be matched, so we can not determine whether
|
|
22833
|
+
// the host element is an <iframe>. In this case, we just assume it is and append a
|
|
22834
|
+
// validation function, which is invoked at runtime and would have access to the
|
|
22835
|
+
// underlying DOM element to check if it's an <iframe> and if so - run extra checks.
|
|
22836
|
+
isIframe = true;
|
|
22627
22837
|
}
|
|
22628
|
-
|
|
22629
|
-
|
|
22838
|
+
else {
|
|
22839
|
+
// For a normal binding we can just check if the element its on is an iframe.
|
|
22840
|
+
const ownerOp = elements.get(op.target);
|
|
22841
|
+
if (ownerOp === undefined || !isElementOrContainerOp(ownerOp)) {
|
|
22842
|
+
throw Error('Property should have an element-like owner');
|
|
22843
|
+
}
|
|
22844
|
+
isIframe = isIframeElement$1(ownerOp);
|
|
22845
|
+
}
|
|
22846
|
+
if (isIframe && isIframeSecuritySensitiveAttr(op.name)) {
|
|
22847
|
+
op.sanitizer = importExpr(Identifiers.validateIframeAttribute);
|
|
22630
22848
|
}
|
|
22631
22849
|
}
|
|
22632
22850
|
break;
|
|
@@ -22640,6 +22858,22 @@ function resolveSanitizers(job) {
|
|
|
22640
22858
|
function isIframeElement$1(op) {
|
|
22641
22859
|
return op.kind === OpKind.ElementStart && op.tag?.toLowerCase() === 'iframe';
|
|
22642
22860
|
}
|
|
22861
|
+
/**
|
|
22862
|
+
* Asserts that there is only a single security context and returns it.
|
|
22863
|
+
*/
|
|
22864
|
+
function getOnlySecurityContext(securityContext) {
|
|
22865
|
+
if (Array.isArray(securityContext)) {
|
|
22866
|
+
if (securityContext.length > 1) {
|
|
22867
|
+
// TODO: What should we do here? TDB just took the first one, but this feels like something we
|
|
22868
|
+
// would want to know about and create a special case for like we did for Url/ResourceUrl. My
|
|
22869
|
+
// guess is that, outside of the Url/ResourceUrl case, this never actually happens. If there
|
|
22870
|
+
// do turn out to be other cases, throwing an error until we can address it feels safer.
|
|
22871
|
+
throw Error(`AssertionError: Ambiguous security context`);
|
|
22872
|
+
}
|
|
22873
|
+
return securityContext[0] || SecurityContext.NONE;
|
|
22874
|
+
}
|
|
22875
|
+
return securityContext;
|
|
22876
|
+
}
|
|
22643
22877
|
|
|
22644
22878
|
/**
|
|
22645
22879
|
* When inside of a listener, we may need access to one or more enclosing views. Therefore, each
|
|
@@ -22743,6 +22977,8 @@ function allocateSlots(job) {
|
|
|
22743
22977
|
// operation itself, so it can be emitted later.
|
|
22744
22978
|
const childView = job.views.get(op.xref);
|
|
22745
22979
|
op.decls = childView.decls;
|
|
22980
|
+
// TODO: currently we handle the decls for the RepeaterCreate empty template in the reify
|
|
22981
|
+
// phase. We should handle that here instead.
|
|
22746
22982
|
}
|
|
22747
22983
|
}
|
|
22748
22984
|
}
|
|
@@ -23066,6 +23302,8 @@ function countVariables(job) {
|
|
|
23066
23302
|
}
|
|
23067
23303
|
const childView = job.views.get(op.xref);
|
|
23068
23304
|
op.vars = childView.vars;
|
|
23305
|
+
// TODO: currently we handle the vars for the RepeaterCreate empty template in the reify
|
|
23306
|
+
// phase. We should handle that here instead.
|
|
23069
23307
|
}
|
|
23070
23308
|
}
|
|
23071
23309
|
}
|
|
@@ -23103,7 +23341,14 @@ function varsUsedByOp(op) {
|
|
|
23103
23341
|
return op.interpolation.expressions.length;
|
|
23104
23342
|
case OpKind.I18nExpression:
|
|
23105
23343
|
case OpKind.Conditional:
|
|
23344
|
+
case OpKind.DeferWhen:
|
|
23106
23345
|
return 1;
|
|
23346
|
+
case OpKind.RepeaterCreate:
|
|
23347
|
+
// Repeaters may require an extra variable binding slot, if they have an empty view, for the
|
|
23348
|
+
// empty block tracking.
|
|
23349
|
+
// TODO: It's a bit odd to have a create mode instruction consume variable slots. Maybe we can
|
|
23350
|
+
// find a way to use the Repeater update op instead.
|
|
23351
|
+
return op.emptyView ? 1 : 0;
|
|
23107
23352
|
default:
|
|
23108
23353
|
throw new Error(`Unhandled op: ${OpKind[op.kind]}`);
|
|
23109
23354
|
}
|
|
@@ -23587,10 +23832,11 @@ const phases = [
|
|
|
23587
23832
|
{ kind: CompilationJobKind.Tmpl, fn: emitNamespaceChanges },
|
|
23588
23833
|
{ kind: CompilationJobKind.Tmpl, fn: propagateI18nBlocks },
|
|
23589
23834
|
{ kind: CompilationJobKind.Tmpl, fn: wrapI18nIcus },
|
|
23590
|
-
{ kind: CompilationJobKind.
|
|
23835
|
+
{ kind: CompilationJobKind.Both, fn: deduplicateTextBindings },
|
|
23591
23836
|
{ kind: CompilationJobKind.Both, fn: specializeStyleBindings },
|
|
23592
23837
|
{ kind: CompilationJobKind.Both, fn: specializeBindings },
|
|
23593
23838
|
{ kind: CompilationJobKind.Both, fn: extractAttributes },
|
|
23839
|
+
{ kind: CompilationJobKind.Tmpl, fn: createI18nContexts },
|
|
23594
23840
|
{ kind: CompilationJobKind.Both, fn: parseExtractedStyles },
|
|
23595
23841
|
{ kind: CompilationJobKind.Tmpl, fn: removeEmptyBindings },
|
|
23596
23842
|
{ kind: CompilationJobKind.Both, fn: collapseSingletonInterpolations },
|
|
@@ -23608,7 +23854,7 @@ const phases = [
|
|
|
23608
23854
|
{ kind: CompilationJobKind.Tmpl, fn: generateProjectionDefs },
|
|
23609
23855
|
{ kind: CompilationJobKind.Tmpl, fn: generateVariables },
|
|
23610
23856
|
{ kind: CompilationJobKind.Tmpl, fn: saveAndRestoreView },
|
|
23611
|
-
{ kind: CompilationJobKind.
|
|
23857
|
+
{ kind: CompilationJobKind.Both, fn: deleteAnyCasts },
|
|
23612
23858
|
{ kind: CompilationJobKind.Both, fn: resolveDollarEvent },
|
|
23613
23859
|
{ kind: CompilationJobKind.Tmpl, fn: generateRepeaterDerivedVars },
|
|
23614
23860
|
{ kind: CompilationJobKind.Tmpl, fn: generateTrackVariables },
|
|
@@ -23616,7 +23862,7 @@ const phases = [
|
|
|
23616
23862
|
{ kind: CompilationJobKind.Tmpl, fn: resolveDeferTargetNames },
|
|
23617
23863
|
{ kind: CompilationJobKind.Tmpl, fn: optimizeTrackFns },
|
|
23618
23864
|
{ kind: CompilationJobKind.Both, fn: resolveContexts },
|
|
23619
|
-
{ kind: CompilationJobKind.
|
|
23865
|
+
{ kind: CompilationJobKind.Both, fn: resolveSanitizers },
|
|
23620
23866
|
{ kind: CompilationJobKind.Tmpl, fn: liftLocalRefs },
|
|
23621
23867
|
{ kind: CompilationJobKind.Both, fn: generateNullishCoalesceExpressions },
|
|
23622
23868
|
{ kind: CompilationJobKind.Both, fn: expandSafeReads },
|
|
@@ -23625,7 +23871,6 @@ const phases = [
|
|
|
23625
23871
|
{ kind: CompilationJobKind.Tmpl, fn: createDeferDepsFns },
|
|
23626
23872
|
{ kind: CompilationJobKind.Tmpl, fn: resolveI18nElementPlaceholders },
|
|
23627
23873
|
{ kind: CompilationJobKind.Tmpl, fn: resolveI18nExpressionPlaceholders },
|
|
23628
|
-
{ kind: CompilationJobKind.Tmpl, fn: resolveI18nIcuPlaceholders },
|
|
23629
23874
|
{ kind: CompilationJobKind.Tmpl, fn: extractI18nMessages },
|
|
23630
23875
|
{ kind: CompilationJobKind.Tmpl, fn: generateTrackFns },
|
|
23631
23876
|
{ kind: CompilationJobKind.Tmpl, fn: collectI18nConsts },
|
|
@@ -23752,6 +23997,10 @@ function emitHostBindingFunction(job) {
|
|
|
23752
23997
|
}
|
|
23753
23998
|
|
|
23754
23999
|
const compatibilityMode = CompatibilityMode.TemplateDefinitionBuilder;
|
|
24000
|
+
// Schema containing DOM elements and their properties.
|
|
24001
|
+
const domSchema = new DomElementSchemaRegistry();
|
|
24002
|
+
// Tag name of the `ng-template` element.
|
|
24003
|
+
const NG_TEMPLATE_TAG_NAME$1 = 'ng-template';
|
|
23755
24004
|
/**
|
|
23756
24005
|
* Process a template AST and convert it into a `ComponentCompilation` in the intermediate
|
|
23757
24006
|
* representation.
|
|
@@ -23769,10 +24018,24 @@ function ingestComponent(componentName, template, constantPool, relativeContextF
|
|
|
23769
24018
|
function ingestHostBinding(input, bindingParser, constantPool) {
|
|
23770
24019
|
const job = new HostBindingCompilationJob(input.componentName, constantPool, compatibilityMode);
|
|
23771
24020
|
for (const property of input.properties ?? []) {
|
|
23772
|
-
|
|
24021
|
+
let bindingKind = BindingKind.Property;
|
|
24022
|
+
// TODO: this should really be handled in the parser.
|
|
24023
|
+
if (property.name.startsWith('attr.')) {
|
|
24024
|
+
property.name = property.name.substring('attr.'.length);
|
|
24025
|
+
bindingKind = BindingKind.Attribute;
|
|
24026
|
+
}
|
|
24027
|
+
if (property.isAnimation) {
|
|
24028
|
+
bindingKind = BindingKind.Animation;
|
|
24029
|
+
}
|
|
24030
|
+
const securityContexts = bindingParser
|
|
24031
|
+
.calcPossibleSecurityContexts(input.componentSelector, property.name, bindingKind === BindingKind.Attribute)
|
|
24032
|
+
.filter(context => context !== SecurityContext.NONE);
|
|
24033
|
+
ingestHostProperty(job, property, bindingKind, securityContexts);
|
|
23773
24034
|
}
|
|
23774
24035
|
for (const [name, expr] of Object.entries(input.attributes) ?? []) {
|
|
23775
|
-
|
|
24036
|
+
const securityContexts = bindingParser.calcPossibleSecurityContexts(input.componentSelector, name, true)
|
|
24037
|
+
.filter(context => context !== SecurityContext.NONE);
|
|
24038
|
+
ingestHostAttribute(job, name, expr, securityContexts);
|
|
23776
24039
|
}
|
|
23777
24040
|
for (const event of input.events ?? []) {
|
|
23778
24041
|
ingestHostEvent(job, event);
|
|
@@ -23781,7 +24044,7 @@ function ingestHostBinding(input, bindingParser, constantPool) {
|
|
|
23781
24044
|
}
|
|
23782
24045
|
// TODO: We should refactor the parser to use the same types and structures for host bindings as
|
|
23783
24046
|
// with ordinary components. This would allow us to share a lot more ingestion code.
|
|
23784
|
-
function ingestHostProperty(job, property,
|
|
24047
|
+
function ingestHostProperty(job, property, bindingKind, securityContexts) {
|
|
23785
24048
|
let expression;
|
|
23786
24049
|
const ast = property.expression.ast;
|
|
23787
24050
|
if (ast instanceof Interpolation$1) {
|
|
@@ -23790,28 +24053,21 @@ function ingestHostProperty(job, property, isTextAttribute) {
|
|
|
23790
24053
|
else {
|
|
23791
24054
|
expression = convertAst(ast, job, property.sourceSpan);
|
|
23792
24055
|
}
|
|
23793
|
-
|
|
23794
|
-
// TODO: this should really be handled in the parser.
|
|
23795
|
-
if (property.name.startsWith('attr.')) {
|
|
23796
|
-
property.name = property.name.substring('attr.'.length);
|
|
23797
|
-
bindingKind = BindingKind.Attribute;
|
|
23798
|
-
}
|
|
23799
|
-
if (property.isAnimation) {
|
|
23800
|
-
bindingKind = BindingKind.Animation;
|
|
23801
|
-
}
|
|
23802
|
-
job.root.update.push(createBindingOp(job.root.xref, bindingKind, property.name, expression, null, SecurityContext
|
|
23803
|
-
.NONE /* TODO: what should we pass as security context? Passing NONE for now. */, isTextAttribute, false, /* TODO: How do Host bindings handle i18n attrs? */ null, property.sourceSpan));
|
|
24056
|
+
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));
|
|
23804
24057
|
}
|
|
23805
|
-
function ingestHostAttribute(job, name, value) {
|
|
23806
|
-
const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null,
|
|
24058
|
+
function ingestHostAttribute(job, name, value, securityContexts) {
|
|
24059
|
+
const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, securityContexts,
|
|
24060
|
+
/* Host attributes should always be extracted to const hostAttrs, even if they are not
|
|
24061
|
+
*strictly* text literals */
|
|
24062
|
+
true, false, null,
|
|
23807
24063
|
/* TODO */ null,
|
|
23808
|
-
|
|
24064
|
+
/** TODO: May be null? */ value.sourceSpan);
|
|
23809
24065
|
job.root.update.push(attrBinding);
|
|
23810
24066
|
}
|
|
23811
24067
|
function ingestHostEvent(job, event) {
|
|
23812
|
-
const
|
|
23813
|
-
|
|
23814
|
-
eventBinding.
|
|
24068
|
+
const [phase, target] = event.type === 0 /* e.ParsedEventType.Regular */ ? [null, event.targetOrPhase] :
|
|
24069
|
+
[event.targetOrPhase, null];
|
|
24070
|
+
const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, makeListenerHandlerOps(job.root, event.handler, event.handlerSpan), phase, target, true, event.sourceSpan);
|
|
23815
24071
|
job.root.create.push(eventBinding);
|
|
23816
24072
|
}
|
|
23817
24073
|
/**
|
|
@@ -23829,10 +24085,10 @@ function ingestNodes(unit, template) {
|
|
|
23829
24085
|
ingestContent(unit, node);
|
|
23830
24086
|
}
|
|
23831
24087
|
else if (node instanceof Text$3) {
|
|
23832
|
-
ingestText(unit, node);
|
|
24088
|
+
ingestText(unit, node, null);
|
|
23833
24089
|
}
|
|
23834
24090
|
else if (node instanceof BoundText) {
|
|
23835
|
-
ingestBoundText(unit, node);
|
|
24091
|
+
ingestBoundText(unit, node, null);
|
|
23836
24092
|
}
|
|
23837
24093
|
else if (node instanceof IfBlock) {
|
|
23838
24094
|
ingestIfBlock(unit, node);
|
|
@@ -23864,10 +24120,16 @@ function ingestElement(unit, element) {
|
|
|
23864
24120
|
}
|
|
23865
24121
|
const id = unit.job.allocateXrefId();
|
|
23866
24122
|
const [namespaceKey, elementName] = splitNsName(element.name);
|
|
23867
|
-
const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan);
|
|
24123
|
+
const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan, element.sourceSpan);
|
|
23868
24124
|
unit.create.push(startOp);
|
|
23869
|
-
|
|
24125
|
+
ingestElementBindings(unit, startOp, element);
|
|
23870
24126
|
ingestReferences(startOp, element);
|
|
24127
|
+
// Start i18n, if needed, goes after the element create and bindings, but before the nodes
|
|
24128
|
+
let i18nBlockId = null;
|
|
24129
|
+
if (element.i18n instanceof Message) {
|
|
24130
|
+
i18nBlockId = unit.job.allocateXrefId();
|
|
24131
|
+
unit.create.push(createI18nStartOp(i18nBlockId, element.i18n));
|
|
24132
|
+
}
|
|
23871
24133
|
ingestNodes(unit, element.children);
|
|
23872
24134
|
// The source span for the end op is typically the element closing tag. However, if no closing tag
|
|
23873
24135
|
// exists, such as in `<input>`, we use the start source span instead. Usually the start and end
|
|
@@ -23877,9 +24139,7 @@ function ingestElement(unit, element) {
|
|
|
23877
24139
|
const endOp = createElementEndOp(id, element.endSourceSpan ?? element.startSourceSpan);
|
|
23878
24140
|
unit.create.push(endOp);
|
|
23879
24141
|
// If there is an i18n message associated with this element, insert i18n start and end ops.
|
|
23880
|
-
if (
|
|
23881
|
-
const i18nBlockId = unit.job.allocateXrefId();
|
|
23882
|
-
OpList.insertAfter(createI18nStartOp(i18nBlockId, element.i18n), startOp);
|
|
24142
|
+
if (i18nBlockId !== null) {
|
|
23883
24143
|
OpList.insertBefore(createI18nEndOp(i18nBlockId), endOp);
|
|
23884
24144
|
}
|
|
23885
24145
|
}
|
|
@@ -23903,9 +24163,9 @@ function ingestTemplate(unit, tmpl) {
|
|
|
23903
24163
|
'' :
|
|
23904
24164
|
prefixWithNamespace(tagNameWithoutNamespace, namespace);
|
|
23905
24165
|
const templateKind = isPlainTemplate(tmpl) ? TemplateKind.NgTemplate : TemplateKind.Structural;
|
|
23906
|
-
const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan);
|
|
24166
|
+
const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan, tmpl.sourceSpan);
|
|
23907
24167
|
unit.create.push(templateOp);
|
|
23908
|
-
|
|
24168
|
+
ingestTemplateBindings(unit, templateOp, tmpl, templateKind);
|
|
23909
24169
|
ingestReferences(templateOp, tmpl);
|
|
23910
24170
|
ingestNodes(childView, tmpl.children);
|
|
23911
24171
|
for (const { name, value } of tmpl.variables) {
|
|
@@ -23921,7 +24181,7 @@ function ingestTemplate(unit, tmpl) {
|
|
|
23921
24181
|
}
|
|
23922
24182
|
}
|
|
23923
24183
|
/**
|
|
23924
|
-
* Ingest a
|
|
24184
|
+
* Ingest a content node from the AST into the given `ViewCompilation`.
|
|
23925
24185
|
*/
|
|
23926
24186
|
function ingestContent(unit, content) {
|
|
23927
24187
|
if (content.i18n !== undefined && !(content.i18n instanceof TagPlaceholder)) {
|
|
@@ -23930,20 +24190,21 @@ function ingestContent(unit, content) {
|
|
|
23930
24190
|
const attrs = content.attributes.flatMap(a => [a.name, a.value]);
|
|
23931
24191
|
const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.i18n, attrs, content.sourceSpan);
|
|
23932
24192
|
for (const attr of content.attributes) {
|
|
23933
|
-
|
|
24193
|
+
const securityContext = domSchema.securityContext(content.name, attr.name, true);
|
|
24194
|
+
unit.update.push(createBindingOp(op.xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
|
|
23934
24195
|
}
|
|
23935
24196
|
unit.create.push(op);
|
|
23936
24197
|
}
|
|
23937
24198
|
/**
|
|
23938
24199
|
* Ingest a literal text node from the AST into the given `ViewCompilation`.
|
|
23939
24200
|
*/
|
|
23940
|
-
function ingestText(unit, text) {
|
|
23941
|
-
unit.create.push(createTextOp(unit.job.allocateXrefId(), text.value, text.sourceSpan));
|
|
24201
|
+
function ingestText(unit, text, icuPlaceholder) {
|
|
24202
|
+
unit.create.push(createTextOp(unit.job.allocateXrefId(), text.value, icuPlaceholder, text.sourceSpan));
|
|
23942
24203
|
}
|
|
23943
24204
|
/**
|
|
23944
24205
|
* Ingest an interpolated text node from the AST into the given `ViewCompilation`.
|
|
23945
24206
|
*/
|
|
23946
|
-
function ingestBoundText(unit, text,
|
|
24207
|
+
function ingestBoundText(unit, text, icuPlaceholder) {
|
|
23947
24208
|
let value = text.value;
|
|
23948
24209
|
if (value instanceof ASTWithSource) {
|
|
23949
24210
|
value = value.ast;
|
|
@@ -23954,19 +24215,16 @@ function ingestBoundText(unit, text, i18nPlaceholders) {
|
|
|
23954
24215
|
if (text.i18n !== undefined && !(text.i18n instanceof Container)) {
|
|
23955
24216
|
throw Error(`Unhandled i18n metadata type for text interpolation: ${text.i18n?.constructor.name}`);
|
|
23956
24217
|
}
|
|
23957
|
-
|
|
23958
|
-
|
|
23959
|
-
|
|
23960
|
-
|
|
23961
|
-
|
|
23962
|
-
.map(placeholder => placeholder.name) :
|
|
23963
|
-
[];
|
|
23964
|
-
}
|
|
24218
|
+
const i18nPlaceholders = text.i18n instanceof Container ?
|
|
24219
|
+
text.i18n.children
|
|
24220
|
+
.filter((node) => node instanceof Placeholder)
|
|
24221
|
+
.map(placeholder => placeholder.name) :
|
|
24222
|
+
[];
|
|
23965
24223
|
if (i18nPlaceholders.length > 0 && i18nPlaceholders.length !== value.expressions.length) {
|
|
23966
24224
|
throw Error(`Unexpected number of i18n placeholders (${value.expressions.length}) for BoundText with ${value.expressions.length} expressions`);
|
|
23967
24225
|
}
|
|
23968
24226
|
const textXref = unit.job.allocateXrefId();
|
|
23969
|
-
unit.create.push(createTextOp(textXref, '', text.sourceSpan));
|
|
24227
|
+
unit.create.push(createTextOp(textXref, '', icuPlaceholder, text.sourceSpan));
|
|
23970
24228
|
// TemplateDefinitionBuilder does not generate source maps for sub-expressions inside an
|
|
23971
24229
|
// interpolation. We copy that behavior in compatibility mode.
|
|
23972
24230
|
// TODO: is it actually correct to generate these extra maps in modern mode?
|
|
@@ -23999,7 +24257,7 @@ function ingestIfBlock(unit, ifBlock) {
|
|
|
23999
24257
|
}
|
|
24000
24258
|
ifCaseI18nMeta = ifCase.i18n;
|
|
24001
24259
|
}
|
|
24002
|
-
const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, tagName, 'Conditional', Namespace.HTML, ifCaseI18nMeta, ifCase.sourceSpan);
|
|
24260
|
+
const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, tagName, 'Conditional', Namespace.HTML, ifCaseI18nMeta, ifCase.startSourceSpan, ifCase.sourceSpan);
|
|
24003
24261
|
unit.create.push(templateOp);
|
|
24004
24262
|
if (firstXref === null) {
|
|
24005
24263
|
firstXref = cView.xref;
|
|
@@ -24029,7 +24287,7 @@ function ingestSwitchBlock(unit, switchBlock) {
|
|
|
24029
24287
|
}
|
|
24030
24288
|
switchCaseI18nMeta = switchCase.i18n;
|
|
24031
24289
|
}
|
|
24032
|
-
const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, null, 'Case', Namespace.HTML, switchCaseI18nMeta, switchCase.sourceSpan);
|
|
24290
|
+
const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, null, 'Case', Namespace.HTML, switchCaseI18nMeta, switchCase.startSourceSpan, switchCase.sourceSpan);
|
|
24033
24291
|
unit.create.push(templateOp);
|
|
24034
24292
|
if (firstXref === null) {
|
|
24035
24293
|
firstXref = cView.xref;
|
|
@@ -24045,13 +24303,16 @@ function ingestSwitchBlock(unit, switchBlock) {
|
|
|
24045
24303
|
const conditional = createConditionalOp(firstXref, firstSlotHandle, convertAst(switchBlock.expression, unit.job, null), conditions, switchBlock.sourceSpan);
|
|
24046
24304
|
unit.update.push(conditional);
|
|
24047
24305
|
}
|
|
24048
|
-
function ingestDeferView(unit, suffix, children, sourceSpan) {
|
|
24306
|
+
function ingestDeferView(unit, suffix, i18nMeta, children, sourceSpan) {
|
|
24307
|
+
if (i18nMeta !== undefined && !(i18nMeta instanceof BlockPlaceholder)) {
|
|
24308
|
+
throw Error('Unhandled i18n metadata type for defer block');
|
|
24309
|
+
}
|
|
24049
24310
|
if (children === undefined) {
|
|
24050
24311
|
return null;
|
|
24051
24312
|
}
|
|
24052
24313
|
const secondaryView = unit.job.allocateView(unit.xref);
|
|
24053
24314
|
ingestNodes(secondaryView, children);
|
|
24054
|
-
const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML,
|
|
24315
|
+
const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, i18nMeta, sourceSpan, sourceSpan);
|
|
24055
24316
|
unit.create.push(templateOp);
|
|
24056
24317
|
return templateOp;
|
|
24057
24318
|
}
|
|
@@ -24061,10 +24322,10 @@ function ingestDeferBlock(unit, deferBlock) {
|
|
|
24061
24322
|
throw new Error(`AssertionError: unable to find metadata for deferred block`);
|
|
24062
24323
|
}
|
|
24063
24324
|
// Generate the defer main view and all secondary views.
|
|
24064
|
-
const main = ingestDeferView(unit, '', deferBlock.children, deferBlock.sourceSpan);
|
|
24065
|
-
const loading = ingestDeferView(unit, 'Loading', deferBlock.loading?.children, deferBlock.loading?.sourceSpan);
|
|
24066
|
-
const placeholder = ingestDeferView(unit, 'Placeholder', deferBlock.placeholder?.children, deferBlock.placeholder?.sourceSpan);
|
|
24067
|
-
const error = ingestDeferView(unit, 'Error', deferBlock.error?.children, deferBlock.error?.sourceSpan);
|
|
24325
|
+
const main = ingestDeferView(unit, '', deferBlock.i18n, deferBlock.children, deferBlock.sourceSpan);
|
|
24326
|
+
const loading = ingestDeferView(unit, 'Loading', deferBlock.loading?.i18n, deferBlock.loading?.children, deferBlock.loading?.sourceSpan);
|
|
24327
|
+
const placeholder = ingestDeferView(unit, 'Placeholder', deferBlock.placeholder?.i18n, deferBlock.placeholder?.children, deferBlock.placeholder?.sourceSpan);
|
|
24328
|
+
const error = ingestDeferView(unit, 'Error', deferBlock.error?.i18n, deferBlock.error?.children, deferBlock.error?.sourceSpan);
|
|
24068
24329
|
// Create the main defer op, and ops for all secondary views.
|
|
24069
24330
|
const deferXref = unit.job.allocateXrefId();
|
|
24070
24331
|
const deferOp = createDeferOp(deferXref, main.xref, main.handle, blockMeta, deferBlock.sourceSpan);
|
|
@@ -24129,6 +24390,11 @@ function ingestDeferBlock(unit, deferBlock) {
|
|
|
24129
24390
|
deferOnOps.push(deferOnOp);
|
|
24130
24391
|
}
|
|
24131
24392
|
if (triggers.when !== undefined) {
|
|
24393
|
+
if (triggers.when.value instanceof Interpolation$1) {
|
|
24394
|
+
// TemplateDefinitionBuilder supports this case, but it's very strange to me. What would it
|
|
24395
|
+
// even mean?
|
|
24396
|
+
throw new Error(`Unexpected interpolation in defer block when trigger`);
|
|
24397
|
+
}
|
|
24132
24398
|
const deferOnOp = createDeferWhenOp(deferXref, convertAst(triggers.when.value, unit.job, triggers.when.sourceSpan), prefetch, triggers.when.sourceSpan);
|
|
24133
24399
|
deferWhenOps.push(deferOnOp);
|
|
24134
24400
|
}
|
|
@@ -24148,10 +24414,10 @@ function ingestIcu(unit, icu) {
|
|
|
24148
24414
|
unit.create.push(createIcuStartOp(xref, icu.i18n, icuFromI18nMessage(icu.i18n).name, null));
|
|
24149
24415
|
for (const [placeholder, text] of Object.entries({ ...icu.vars, ...icu.placeholders })) {
|
|
24150
24416
|
if (text instanceof BoundText) {
|
|
24151
|
-
ingestBoundText(unit, text,
|
|
24417
|
+
ingestBoundText(unit, text, placeholder);
|
|
24152
24418
|
}
|
|
24153
24419
|
else {
|
|
24154
|
-
ingestText(unit, text);
|
|
24420
|
+
ingestText(unit, text, placeholder);
|
|
24155
24421
|
}
|
|
24156
24422
|
}
|
|
24157
24423
|
unit.create.push(createIcuEndOp(xref));
|
|
@@ -24198,8 +24464,17 @@ function ingestForBlock(unit, forBlock) {
|
|
|
24198
24464
|
$odd: forBlock.contextVariables.$odd.name,
|
|
24199
24465
|
$implicit: forBlock.item.name,
|
|
24200
24466
|
};
|
|
24467
|
+
if (forBlock.i18n !== undefined && !(forBlock.i18n instanceof BlockPlaceholder)) {
|
|
24468
|
+
throw Error('AssertionError: Unhandled i18n metadata type or @for');
|
|
24469
|
+
}
|
|
24470
|
+
if (forBlock.empty?.i18n !== undefined &&
|
|
24471
|
+
!(forBlock.empty.i18n instanceof BlockPlaceholder)) {
|
|
24472
|
+
throw Error('AssertionError: Unhandled i18n metadata type or @empty');
|
|
24473
|
+
}
|
|
24474
|
+
const i18nPlaceholder = forBlock.i18n;
|
|
24475
|
+
const emptyI18nPlaceholder = forBlock.empty?.i18n;
|
|
24201
24476
|
const tagName = ingestControlFlowInsertionPoint(unit, repeaterView.xref, forBlock);
|
|
24202
|
-
const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, forBlock.sourceSpan);
|
|
24477
|
+
const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, i18nPlaceholder, emptyI18nPlaceholder, forBlock.startSourceSpan, forBlock.sourceSpan);
|
|
24203
24478
|
unit.create.push(repeaterCreate);
|
|
24204
24479
|
const expression = convertAst(forBlock.expression, unit.job, convertSourceSpan(forBlock.expression.span, forBlock.sourceSpan));
|
|
24205
24480
|
const repeater = createRepeaterOp(repeaterCreate.xref, repeaterCreate.handle, expression, forBlock.sourceSpan);
|
|
@@ -24317,6 +24592,27 @@ function convertAst(ast, job, baseSourceSpan) {
|
|
|
24317
24592
|
throw new Error(`Unhandled expression type "${ast.constructor.name}" in file "${baseSourceSpan?.start.file.url}"`);
|
|
24318
24593
|
}
|
|
24319
24594
|
}
|
|
24595
|
+
function convertAstWithInterpolation(job, value, i18nMeta, sourceSpan) {
|
|
24596
|
+
let expression;
|
|
24597
|
+
if (value instanceof Interpolation$1) {
|
|
24598
|
+
expression = new Interpolation(value.strings, value.expressions.map(e => convertAst(e, job, sourceSpan ?? null)), Object.keys(asMessage(i18nMeta)?.placeholders ?? {}));
|
|
24599
|
+
}
|
|
24600
|
+
else if (value instanceof AST) {
|
|
24601
|
+
expression = convertAst(value, job, sourceSpan ?? null);
|
|
24602
|
+
}
|
|
24603
|
+
else {
|
|
24604
|
+
expression = literal(value);
|
|
24605
|
+
}
|
|
24606
|
+
return expression;
|
|
24607
|
+
}
|
|
24608
|
+
// TODO: Can we populate Template binding kinds in ingest?
|
|
24609
|
+
const BINDING_KINDS = new Map([
|
|
24610
|
+
[0 /* e.BindingType.Property */, BindingKind.Property],
|
|
24611
|
+
[1 /* e.BindingType.Attribute */, BindingKind.Attribute],
|
|
24612
|
+
[2 /* e.BindingType.Class */, BindingKind.ClassName],
|
|
24613
|
+
[3 /* e.BindingType.Style */, BindingKind.StyleProperty],
|
|
24614
|
+
[4 /* e.BindingType.Animation */, BindingKind.Animation],
|
|
24615
|
+
]);
|
|
24320
24616
|
/**
|
|
24321
24617
|
* Checks whether the given template is a plain ng-template (as opposed to another kind of template
|
|
24322
24618
|
* such as a structural directive template or control flow template). This is checked based on the
|
|
@@ -24335,149 +24631,184 @@ function convertAst(ast, job, baseSourceSpan) {
|
|
|
24335
24631
|
* | `<ng-template *ngIf>` (structural) | null |
|
|
24336
24632
|
*/
|
|
24337
24633
|
function isPlainTemplate(tmpl) {
|
|
24338
|
-
return splitNsName(tmpl.tagName ?? '')[1] ===
|
|
24634
|
+
return splitNsName(tmpl.tagName ?? '')[1] === NG_TEMPLATE_TAG_NAME$1;
|
|
24339
24635
|
}
|
|
24340
24636
|
/**
|
|
24341
|
-
*
|
|
24342
|
-
* to their IR representation.
|
|
24637
|
+
* Ensures that the i18nMeta, if provided, is an i18n.Message.
|
|
24343
24638
|
*/
|
|
24344
|
-
function
|
|
24345
|
-
|
|
24346
|
-
|
|
24347
|
-
if (element instanceof Template) {
|
|
24348
|
-
flags |= BindingFlags.OnNgTemplateElement;
|
|
24349
|
-
if (element instanceof Template && isPlainTemplate(element)) {
|
|
24350
|
-
flags |= BindingFlags.BindingTargetsTemplate;
|
|
24351
|
-
}
|
|
24352
|
-
const templateAttrFlags = flags | BindingFlags.BindingTargetsTemplate | BindingFlags.IsStructuralTemplateAttribute;
|
|
24353
|
-
for (const attr of element.templateAttrs) {
|
|
24354
|
-
if (attr instanceof TextAttribute) {
|
|
24355
|
-
ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, templateAttrFlags | BindingFlags.TextValue, attr.i18n);
|
|
24356
|
-
hasI18nAttributes ||= attr.i18n !== undefined;
|
|
24357
|
-
}
|
|
24358
|
-
else {
|
|
24359
|
-
ingestBinding(unit, op.xref, attr.name, attr.value, attr.type, attr.unit, attr.securityContext, attr.sourceSpan, templateAttrFlags, attr.i18n);
|
|
24360
|
-
hasI18nAttributes ||= attr.i18n !== undefined;
|
|
24361
|
-
}
|
|
24362
|
-
}
|
|
24639
|
+
function asMessage(i18nMeta) {
|
|
24640
|
+
if (i18nMeta == null) {
|
|
24641
|
+
return null;
|
|
24363
24642
|
}
|
|
24643
|
+
if (!(i18nMeta instanceof Message)) {
|
|
24644
|
+
throw Error(`Expected i18n meta to be a Message, but got: ${i18nMeta.constructor.name}`);
|
|
24645
|
+
}
|
|
24646
|
+
return i18nMeta;
|
|
24647
|
+
}
|
|
24648
|
+
/**
|
|
24649
|
+
* Process all of the bindings on an element in the template AST and convert them to their IR
|
|
24650
|
+
* representation.
|
|
24651
|
+
*/
|
|
24652
|
+
function ingestElementBindings(unit, op, element) {
|
|
24653
|
+
let bindings = new Array();
|
|
24364
24654
|
for (const attr of element.attributes) {
|
|
24365
|
-
//
|
|
24366
|
-
|
|
24367
|
-
|
|
24368
|
-
ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, flags | BindingFlags.TextValue, attr.i18n);
|
|
24369
|
-
hasI18nAttributes ||= attr.i18n !== undefined;
|
|
24655
|
+
// Attribute literal bindings, such as `attr.foo="bar"`.
|
|
24656
|
+
const securityContext = domSchema.securityContext(element.name, attr.name, true);
|
|
24657
|
+
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));
|
|
24370
24658
|
}
|
|
24371
24659
|
for (const input of element.inputs) {
|
|
24372
|
-
|
|
24373
|
-
|
|
24660
|
+
// All dynamic bindings (both attribute and property bindings).
|
|
24661
|
+
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));
|
|
24374
24662
|
}
|
|
24663
|
+
unit.create.push(bindings.filter((b) => b?.kind === OpKind.ExtractedAttribute));
|
|
24664
|
+
unit.update.push(bindings.filter((b) => b?.kind === OpKind.Binding));
|
|
24375
24665
|
for (const output of element.outputs) {
|
|
24376
|
-
|
|
24377
|
-
|
|
24378
|
-
if (output.phase === null) {
|
|
24379
|
-
throw Error('Animation listener should have a phase');
|
|
24380
|
-
}
|
|
24666
|
+
if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) {
|
|
24667
|
+
throw Error('Animation listener should have a phase');
|
|
24381
24668
|
}
|
|
24382
|
-
|
|
24383
|
-
|
|
24384
|
-
|
|
24385
|
-
|
|
24386
|
-
|
|
24387
|
-
|
|
24388
|
-
|
|
24389
|
-
|
|
24390
|
-
|
|
24391
|
-
|
|
24392
|
-
|
|
24393
|
-
|
|
24394
|
-
|
|
24395
|
-
|
|
24669
|
+
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));
|
|
24670
|
+
}
|
|
24671
|
+
// If any of the bindings on this element have an i18n message, then an i18n attrs configuration
|
|
24672
|
+
// op is also required.
|
|
24673
|
+
if (bindings.some(b => b?.i18nMessage) !== null) {
|
|
24674
|
+
unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
|
|
24675
|
+
}
|
|
24676
|
+
}
|
|
24677
|
+
/**
|
|
24678
|
+
* Process all of the bindings on a template in the template AST and convert them to their IR
|
|
24679
|
+
* representation.
|
|
24680
|
+
*/
|
|
24681
|
+
function ingestTemplateBindings(unit, op, template, templateKind) {
|
|
24682
|
+
let bindings = new Array();
|
|
24683
|
+
for (const attr of template.templateAttrs) {
|
|
24684
|
+
if (attr instanceof TextAttribute) {
|
|
24685
|
+
const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
|
|
24686
|
+
bindings.push(createTemplateBinding(unit, op.xref, 1 /* e.BindingType.Attribute */, attr.name, attr.value, null, securityContext, true, templateKind, asMessage(attr.i18n), attr.sourceSpan));
|
|
24396
24687
|
}
|
|
24397
24688
|
else {
|
|
24398
|
-
|
|
24689
|
+
bindings.push(createTemplateBinding(unit, op.xref, attr.type, attr.name, astOf(attr.value), attr.unit, attr.securityContext, true, templateKind, asMessage(attr.i18n), attr.sourceSpan));
|
|
24690
|
+
}
|
|
24691
|
+
}
|
|
24692
|
+
for (const attr of template.attributes) {
|
|
24693
|
+
// Attribute literal bindings, such as `attr.foo="bar"`.
|
|
24694
|
+
const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
|
|
24695
|
+
bindings.push(createTemplateBinding(unit, op.xref, 1 /* e.BindingType.Attribute */, attr.name, attr.value, null, securityContext, false, templateKind, asMessage(attr.i18n), attr.sourceSpan));
|
|
24696
|
+
}
|
|
24697
|
+
for (const input of template.inputs) {
|
|
24698
|
+
// Dynamic bindings (both attribute and property bindings).
|
|
24699
|
+
bindings.push(createTemplateBinding(unit, op.xref, input.type, input.name, astOf(input.value), input.unit, input.securityContext, false, templateKind, asMessage(input.i18n), input.sourceSpan));
|
|
24700
|
+
}
|
|
24701
|
+
unit.create.push(bindings.filter((b) => b?.kind === OpKind.ExtractedAttribute));
|
|
24702
|
+
unit.update.push(bindings.filter((b) => b?.kind === OpKind.Binding));
|
|
24703
|
+
for (const output of template.outputs) {
|
|
24704
|
+
if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) {
|
|
24705
|
+
throw Error('Animation listener should have a phase');
|
|
24399
24706
|
}
|
|
24400
|
-
if (
|
|
24401
|
-
|
|
24707
|
+
if (templateKind === TemplateKind.NgTemplate) {
|
|
24708
|
+
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));
|
|
24402
24709
|
}
|
|
24403
|
-
|
|
24404
|
-
|
|
24405
|
-
|
|
24406
|
-
const
|
|
24407
|
-
|
|
24710
|
+
if (templateKind === TemplateKind.Structural &&
|
|
24711
|
+
output.type !== 1 /* e.ParsedEventType.Animation */) {
|
|
24712
|
+
// Animation bindings are excluded from the structural template's const array.
|
|
24713
|
+
const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, output.name, false);
|
|
24714
|
+
unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null, null, null, securityContext));
|
|
24408
24715
|
}
|
|
24409
|
-
listenerOp.handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
|
|
24410
|
-
unit.create.push(listenerOp);
|
|
24411
24716
|
}
|
|
24412
24717
|
// TODO: Perhaps we could do this in a phase? (It likely wouldn't change the slot indices.)
|
|
24413
|
-
if (
|
|
24718
|
+
if (bindings.some(b => b?.i18nMessage) !== null) {
|
|
24414
24719
|
unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
|
|
24415
24720
|
}
|
|
24416
24721
|
}
|
|
24417
|
-
|
|
24418
|
-
|
|
24419
|
-
|
|
24420
|
-
|
|
24421
|
-
|
|
24422
|
-
|
|
24423
|
-
|
|
24424
|
-
|
|
24425
|
-
|
|
24426
|
-
|
|
24427
|
-
|
|
24428
|
-
|
|
24429
|
-
|
|
24430
|
-
|
|
24431
|
-
|
|
24432
|
-
|
|
24433
|
-
|
|
24434
|
-
|
|
24435
|
-
|
|
24436
|
-
|
|
24437
|
-
|
|
24438
|
-
|
|
24439
|
-
|
|
24440
|
-
|
|
24441
|
-
|
|
24442
|
-
|
|
24443
|
-
|
|
24444
|
-
|
|
24445
|
-
|
|
24446
|
-
|
|
24447
|
-
|
|
24448
|
-
|
|
24449
|
-
if (
|
|
24450
|
-
if (!
|
|
24451
|
-
|
|
24722
|
+
/**
|
|
24723
|
+
* Helper to ingest an individual binding on a template, either an explicit `ng-template`, or an
|
|
24724
|
+
* implicit template created via structural directive.
|
|
24725
|
+
*
|
|
24726
|
+
* Bindings on templates are *extremely* tricky. I have tried to isolate all of the confusing edge
|
|
24727
|
+
* cases into this function, and to comment it well to document the behavior.
|
|
24728
|
+
*
|
|
24729
|
+
* Some of this behavior is intuitively incorrect, and we should consider changing it in the future.
|
|
24730
|
+
*
|
|
24731
|
+
* @param view The compilation unit for the view containing the template.
|
|
24732
|
+
* @param xref The xref of the template op.
|
|
24733
|
+
* @param type The binding type, according to the parser. This is fairly reasonable, e.g. both
|
|
24734
|
+
* dynamic and static attributes have e.BindingType.Attribute.
|
|
24735
|
+
* @param name The binding's name.
|
|
24736
|
+
* @param value The bindings's value, which will either be an input AST expression, or a string
|
|
24737
|
+
* literal. Note that the input AST expression may or may not be const -- it will only be a
|
|
24738
|
+
* string literal if the parser considered it a text binding.
|
|
24739
|
+
* @param unit If the binding has a unit (e.g. `px` for style bindings), then this is the unit.
|
|
24740
|
+
* @param securityContext The security context of the binding.
|
|
24741
|
+
* @param isStructuralTemplateAttribute Whether this binding actually applies to the structural
|
|
24742
|
+
* ng-template. For example, an `ngFor` would actually apply to the structural template. (Most
|
|
24743
|
+
* bindings on structural elements target the inner element, not the template.)
|
|
24744
|
+
* @param templateKind Whether this is an explicit `ng-template` or an implicit template created by
|
|
24745
|
+
* a structural directive. This should never be a block template.
|
|
24746
|
+
* @param i18nMessage The i18n metadata for the binding, if any.
|
|
24747
|
+
* @param sourceSpan The source span of the binding.
|
|
24748
|
+
* @returns An IR binding op, or null if the binding should be skipped.
|
|
24749
|
+
*/
|
|
24750
|
+
function createTemplateBinding(view, xref, type, name, value, unit, securityContext, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
|
|
24751
|
+
const isTextBinding = typeof value === 'string';
|
|
24752
|
+
// If this is a structural template, then several kinds of bindings should not result in an
|
|
24753
|
+
// update instruction.
|
|
24754
|
+
if (templateKind === TemplateKind.Structural) {
|
|
24755
|
+
if (!isStructuralTemplateAttribute &&
|
|
24756
|
+
(type === 0 /* e.BindingType.Property */ || type === 2 /* e.BindingType.Class */ ||
|
|
24757
|
+
type === 3 /* e.BindingType.Style */)) {
|
|
24758
|
+
// Because this binding doesn't really target the ng-template, it must be a binding on an
|
|
24759
|
+
// inner node of a structural template. We can't skip it entirely, because we still need it on
|
|
24760
|
+
// the ng-template's consts (e.g. for the purposes of directive matching). However, we should
|
|
24761
|
+
// not generate an update instruction for it.
|
|
24762
|
+
return createExtractedAttributeOp(xref, BindingKind.Property, name, null, null, i18nMessage, securityContext);
|
|
24763
|
+
}
|
|
24764
|
+
if (!isTextBinding && (type === 1 /* e.BindingType.Attribute */ || type === 4 /* e.BindingType.Animation */)) {
|
|
24765
|
+
// Again, this binding doesn't really target the ng-template; it actually targets the element
|
|
24766
|
+
// inside the structural template. In the case of non-text attribute or animation bindings,
|
|
24767
|
+
// the binding doesn't even show up on the ng-template const array, so we just skip it
|
|
24768
|
+
// entirely.
|
|
24769
|
+
return null;
|
|
24452
24770
|
}
|
|
24453
|
-
i18nContext = view.job.allocateXrefId();
|
|
24454
|
-
view.create.push(createI18nContextOp(I18nContextKind.Attr, i18nContext, null, i18nMeta, null));
|
|
24455
|
-
}
|
|
24456
|
-
if (flags & BindingFlags.OnNgTemplateElement && !(flags & BindingFlags.BindingTargetsTemplate) &&
|
|
24457
|
-
type === 0 /* e.BindingType.Property */) {
|
|
24458
|
-
// This binding only exists for later const extraction, and is not an actual binding to be
|
|
24459
|
-
// created.
|
|
24460
|
-
view.create.push(createExtractedAttributeOp(xref, BindingKind.Property, name, null, i18nContext));
|
|
24461
|
-
return;
|
|
24462
24771
|
}
|
|
24463
|
-
let
|
|
24464
|
-
|
|
24465
|
-
|
|
24466
|
-
|
|
24467
|
-
|
|
24468
|
-
|
|
24469
|
-
|
|
24772
|
+
let bindingType = BINDING_KINDS.get(type);
|
|
24773
|
+
if (templateKind === TemplateKind.NgTemplate) {
|
|
24774
|
+
// We know we are dealing with bindings directly on an explicit ng-template.
|
|
24775
|
+
// Static attribute bindings should be collected into the const array as k/v pairs. Property
|
|
24776
|
+
// bindings should result in a `property` instruction, and `AttributeMarker.Bindings` const
|
|
24777
|
+
// entries.
|
|
24778
|
+
//
|
|
24779
|
+
// The difficulty is with dynamic attribute, style, and class bindings. These don't really make
|
|
24780
|
+
// sense on an `ng-template` and should probably be parser errors. However,
|
|
24781
|
+
// TemplateDefinitionBuilder generates `property` instructions for them, and so we do that as
|
|
24782
|
+
// well.
|
|
24783
|
+
//
|
|
24784
|
+
// Note that we do have a slight behavior difference with TemplateDefinitionBuilder: although
|
|
24785
|
+
// TDB emits `property` instructions for dynamic attributes, styles, and classes, only styles
|
|
24786
|
+
// and classes also get const collected into the `AttributeMarker.Bindings` field. Dynamic
|
|
24787
|
+
// attribute bindings are missing from the consts entirely. We choose to emit them into the
|
|
24788
|
+
// consts field anyway, to avoid creating special cases for something so arcane and nonsensical.
|
|
24789
|
+
if (type === 2 /* e.BindingType.Class */ || type === 3 /* e.BindingType.Style */ ||
|
|
24790
|
+
(type === 1 /* e.BindingType.Attribute */ && !isTextBinding)) {
|
|
24791
|
+
// TODO: These cases should be parse errors.
|
|
24792
|
+
bindingType = BindingKind.Property;
|
|
24470
24793
|
}
|
|
24471
|
-
expression = new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, view.job, null)), i18nPlaceholders);
|
|
24472
|
-
}
|
|
24473
|
-
else if (value instanceof AST) {
|
|
24474
|
-
expression = convertAst(value, view.job, null);
|
|
24475
24794
|
}
|
|
24476
|
-
|
|
24477
|
-
|
|
24795
|
+
return createBindingOp(xref, bindingType, name, convertAstWithInterpolation(view.job, value, i18nMessage), unit, securityContext, isTextBinding, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan);
|
|
24796
|
+
}
|
|
24797
|
+
function makeListenerHandlerOps(unit, handler, handlerSpan) {
|
|
24798
|
+
handler = astOf(handler);
|
|
24799
|
+
const handlerOps = new Array();
|
|
24800
|
+
let handlerExprs = handler instanceof Chain ? handler.expressions : [handler];
|
|
24801
|
+
if (handlerExprs.length === 0) {
|
|
24802
|
+
throw new Error('Expected listener to have non-empty expression list.');
|
|
24478
24803
|
}
|
|
24479
|
-
const
|
|
24480
|
-
|
|
24804
|
+
const expressions = handlerExprs.map(expr => convertAst(expr, unit.job, handlerSpan));
|
|
24805
|
+
const returnExpr = expressions.pop();
|
|
24806
|
+
handlerOps.push(...expressions.map(e => createStatementOp(new ExpressionStatement(e, e.sourceSpan))));
|
|
24807
|
+
handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
|
|
24808
|
+
return handlerOps;
|
|
24809
|
+
}
|
|
24810
|
+
function astOf(ast) {
|
|
24811
|
+
return ast instanceof ASTWithSource ? ast.ast : ast;
|
|
24481
24812
|
}
|
|
24482
24813
|
/**
|
|
24483
24814
|
* Process all of the local references on an element-like structure in the template AST and
|
|
@@ -24565,11 +24896,12 @@ function ingestControlFlowInsertionPoint(unit, xref, node) {
|
|
|
24565
24896
|
// and they can be used in directive matching (in the case of `Template.templateAttrs`).
|
|
24566
24897
|
if (root !== null) {
|
|
24567
24898
|
for (const attr of root.attributes) {
|
|
24568
|
-
|
|
24899
|
+
const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
|
|
24900
|
+
unit.update.push(createBindingOp(xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
|
|
24569
24901
|
}
|
|
24570
24902
|
const tagName = root instanceof Element$1 ? root.name : root.tagName;
|
|
24571
24903
|
// Don't pass along `ng-template` tag name since it enables directive matching.
|
|
24572
|
-
return tagName ===
|
|
24904
|
+
return tagName === NG_TEMPLATE_TAG_NAME$1 ? null : tagName;
|
|
24573
24905
|
}
|
|
24574
24906
|
return null;
|
|
24575
24907
|
}
|
|
@@ -29241,12 +29573,16 @@ class BindingScope {
|
|
|
29241
29573
|
}
|
|
29242
29574
|
/** Binding scope of a `track` function inside a `for` loop block. */
|
|
29243
29575
|
class TrackByBindingScope extends BindingScope {
|
|
29244
|
-
constructor(parentScope,
|
|
29576
|
+
constructor(parentScope, globalOverrides) {
|
|
29245
29577
|
super(parentScope.bindingLevel + 1, parentScope);
|
|
29246
|
-
this.
|
|
29578
|
+
this.globalOverrides = globalOverrides;
|
|
29247
29579
|
this.componentAccessCount = 0;
|
|
29248
29580
|
}
|
|
29249
29581
|
get(name) {
|
|
29582
|
+
// Intercept any overridden globals.
|
|
29583
|
+
if (this.globalOverrides.hasOwnProperty(name)) {
|
|
29584
|
+
return variable(this.globalOverrides[name]);
|
|
29585
|
+
}
|
|
29250
29586
|
let current = this.parent;
|
|
29251
29587
|
// Prevent accesses of template variables outside the `for` loop.
|
|
29252
29588
|
while (current) {
|
|
@@ -29255,10 +29591,6 @@ class TrackByBindingScope extends BindingScope {
|
|
|
29255
29591
|
}
|
|
29256
29592
|
current = current.parent;
|
|
29257
29593
|
}
|
|
29258
|
-
// Intercept any aliased globals.
|
|
29259
|
-
if (this.globalAliases[name]) {
|
|
29260
|
-
return variable(this.globalAliases[name]);
|
|
29261
|
-
}
|
|
29262
29594
|
// When the component scope is accessed, we redirect it through `this`.
|
|
29263
29595
|
this.componentAccessCount++;
|
|
29264
29596
|
return variable('this').prop(name);
|
|
@@ -29996,6 +30328,7 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
|
|
|
29996
30328
|
}
|
|
29997
30329
|
const hostJob = ingestHostBinding({
|
|
29998
30330
|
componentName: name,
|
|
30331
|
+
componentSelector: selector,
|
|
29999
30332
|
properties: bindings,
|
|
30000
30333
|
events: eventBindings,
|
|
30001
30334
|
attributes: hostBindingsMetadata.attributes,
|
|
@@ -31755,7 +32088,7 @@ function publishFacade(global) {
|
|
|
31755
32088
|
* @description
|
|
31756
32089
|
* Entry point for all public APIs of the compiler package.
|
|
31757
32090
|
*/
|
|
31758
|
-
const VERSION = new Version('17.0.
|
|
32091
|
+
const VERSION = new Version('17.0.8');
|
|
31759
32092
|
|
|
31760
32093
|
class CompilerConfig {
|
|
31761
32094
|
constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
|
|
@@ -33321,7 +33654,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
|
|
|
33321
33654
|
function compileDeclareClassMetadata(metadata) {
|
|
33322
33655
|
const definitionMap = new DefinitionMap();
|
|
33323
33656
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
|
|
33324
|
-
definitionMap.set('version', literal('17.0.
|
|
33657
|
+
definitionMap.set('version', literal('17.0.8'));
|
|
33325
33658
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33326
33659
|
definitionMap.set('type', metadata.type);
|
|
33327
33660
|
definitionMap.set('decorators', metadata.decorators);
|
|
@@ -33429,7 +33762,7 @@ function createDirectiveDefinitionMap(meta) {
|
|
|
33429
33762
|
// in 16.1 is actually used.
|
|
33430
33763
|
const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
|
|
33431
33764
|
definitionMap.set('minVersion', literal(minVersion));
|
|
33432
|
-
definitionMap.set('version', literal('17.0.
|
|
33765
|
+
definitionMap.set('version', literal('17.0.8'));
|
|
33433
33766
|
// e.g. `type: MyDirective`
|
|
33434
33767
|
definitionMap.set('type', meta.type.value);
|
|
33435
33768
|
if (meta.isStandalone) {
|
|
@@ -33706,7 +34039,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
|
|
|
33706
34039
|
function compileDeclareFactoryFunction(meta) {
|
|
33707
34040
|
const definitionMap = new DefinitionMap();
|
|
33708
34041
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
|
|
33709
|
-
definitionMap.set('version', literal('17.0.
|
|
34042
|
+
definitionMap.set('version', literal('17.0.8'));
|
|
33710
34043
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33711
34044
|
definitionMap.set('type', meta.type.value);
|
|
33712
34045
|
definitionMap.set('deps', compileDependencies(meta.deps));
|
|
@@ -33741,7 +34074,7 @@ function compileDeclareInjectableFromMetadata(meta) {
|
|
|
33741
34074
|
function createInjectableDefinitionMap(meta) {
|
|
33742
34075
|
const definitionMap = new DefinitionMap();
|
|
33743
34076
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
|
|
33744
|
-
definitionMap.set('version', literal('17.0.
|
|
34077
|
+
definitionMap.set('version', literal('17.0.8'));
|
|
33745
34078
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33746
34079
|
definitionMap.set('type', meta.type.value);
|
|
33747
34080
|
// Only generate providedIn property if it has a non-null value
|
|
@@ -33792,7 +34125,7 @@ function compileDeclareInjectorFromMetadata(meta) {
|
|
|
33792
34125
|
function createInjectorDefinitionMap(meta) {
|
|
33793
34126
|
const definitionMap = new DefinitionMap();
|
|
33794
34127
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
|
|
33795
|
-
definitionMap.set('version', literal('17.0.
|
|
34128
|
+
definitionMap.set('version', literal('17.0.8'));
|
|
33796
34129
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33797
34130
|
definitionMap.set('type', meta.type.value);
|
|
33798
34131
|
definitionMap.set('providers', meta.providers);
|
|
@@ -33825,7 +34158,7 @@ function createNgModuleDefinitionMap(meta) {
|
|
|
33825
34158
|
throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
|
|
33826
34159
|
}
|
|
33827
34160
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
|
|
33828
|
-
definitionMap.set('version', literal('17.0.
|
|
34161
|
+
definitionMap.set('version', literal('17.0.8'));
|
|
33829
34162
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33830
34163
|
definitionMap.set('type', meta.type.value);
|
|
33831
34164
|
// We only generate the keys in the metadata if the arrays contain values.
|
|
@@ -33876,7 +34209,7 @@ function compileDeclarePipeFromMetadata(meta) {
|
|
|
33876
34209
|
function createPipeDefinitionMap(meta) {
|
|
33877
34210
|
const definitionMap = new DefinitionMap();
|
|
33878
34211
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
|
|
33879
|
-
definitionMap.set('version', literal('17.0.
|
|
34212
|
+
definitionMap.set('version', literal('17.0.8'));
|
|
33880
34213
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33881
34214
|
// e.g. `type: MyPipe`
|
|
33882
34215
|
definitionMap.set('type', meta.type.value);
|