@angular/compiler 17.0.6 → 17.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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/template/pipeline/ir/src/enums.mjs +9 -17
- package/esm2022/src/template/pipeline/ir/src/expression.mjs +12 -20
- package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +14 -6
- package/esm2022/src/template/pipeline/ir/src/ops/host.mjs +4 -2
- package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +15 -9
- package/esm2022/src/template/pipeline/src/emit.mjs +4 -4
- package/esm2022/src/template/pipeline/src/ingest.mjs +240 -155
- 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 +15 -8
- package/esm2022/src/template/pipeline/src/phases/binding_specialization.mjs +4 -4
- package/esm2022/src/template/pipeline/src/phases/const_collection.mjs +13 -4
- package/esm2022/src/template/pipeline/src/phases/create_i18n_contexts.mjs +23 -1
- package/esm2022/src/template/pipeline/src/phases/generate_variables.mjs +7 -1
- package/esm2022/src/template/pipeline/src/phases/i18n_const_collection.mjs +10 -6
- package/esm2022/src/template/pipeline/src/phases/parse_extracted_styles.mjs +4 -3
- package/esm2022/src/template/pipeline/src/phases/propagate_i18n_blocks.mjs +27 -12
- package/esm2022/src/template/pipeline/src/phases/reify.mjs +12 -15
- package/esm2022/src/template/pipeline/src/phases/resolve_i18n_element_placeholders.mjs +60 -14
- 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 +3 -1
- package/esm2022/src/version.mjs +1 -1
- package/fesm2022/compiler.mjs +590 -351
- package/fesm2022/compiler.mjs.map +1 -1
- package/index.d.ts +1 -1
- package/package.json +2 -2
package/fesm2022/compiler.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v17.0.
|
|
2
|
+
* @license Angular v17.0.7
|
|
3
3
|
* (c) 2010-2022 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -9064,23 +9064,27 @@ var ExpressionKind;
|
|
|
9064
9064
|
* An expression representing a sanitizer function.
|
|
9065
9065
|
*/
|
|
9066
9066
|
ExpressionKind[ExpressionKind["SanitizerExpr"] = 20] = "SanitizerExpr";
|
|
9067
|
+
/**
|
|
9068
|
+
* An expression representing a function to create trusted values.
|
|
9069
|
+
*/
|
|
9070
|
+
ExpressionKind[ExpressionKind["TrustedValueFnExpr"] = 21] = "TrustedValueFnExpr";
|
|
9067
9071
|
/**
|
|
9068
9072
|
* An expression that will cause a literal slot index to be emitted.
|
|
9069
9073
|
*/
|
|
9070
|
-
ExpressionKind[ExpressionKind["SlotLiteralExpr"] =
|
|
9074
|
+
ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 22] = "SlotLiteralExpr";
|
|
9071
9075
|
/**
|
|
9072
9076
|
* A test expression for a conditional op.
|
|
9073
9077
|
*/
|
|
9074
|
-
ExpressionKind[ExpressionKind["ConditionalCase"] =
|
|
9078
|
+
ExpressionKind[ExpressionKind["ConditionalCase"] = 23] = "ConditionalCase";
|
|
9075
9079
|
/**
|
|
9076
9080
|
* A variable for use inside a repeater, providing one of the ambiently-available context
|
|
9077
9081
|
* properties ($even, $first, etc.).
|
|
9078
9082
|
*/
|
|
9079
|
-
ExpressionKind[ExpressionKind["DerivedRepeaterVar"] =
|
|
9083
|
+
ExpressionKind[ExpressionKind["DerivedRepeaterVar"] = 24] = "DerivedRepeaterVar";
|
|
9080
9084
|
/**
|
|
9081
9085
|
* An expression that will be automatically extracted to the component const array.
|
|
9082
9086
|
*/
|
|
9083
|
-
ExpressionKind[ExpressionKind["ConstCollected"] =
|
|
9087
|
+
ExpressionKind[ExpressionKind["ConstCollected"] = 25] = "ConstCollected";
|
|
9084
9088
|
})(ExpressionKind || (ExpressionKind = {}));
|
|
9085
9089
|
var VariableFlags;
|
|
9086
9090
|
(function (VariableFlags) {
|
|
@@ -9124,18 +9128,6 @@ var CompatibilityMode;
|
|
|
9124
9128
|
CompatibilityMode[CompatibilityMode["Normal"] = 0] = "Normal";
|
|
9125
9129
|
CompatibilityMode[CompatibilityMode["TemplateDefinitionBuilder"] = 1] = "TemplateDefinitionBuilder";
|
|
9126
9130
|
})(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
9131
|
/**
|
|
9140
9132
|
* Enumeration of the different kinds of `@defer` secondary blocks.
|
|
9141
9133
|
*/
|
|
@@ -9414,7 +9406,7 @@ class Interpolation {
|
|
|
9414
9406
|
/**
|
|
9415
9407
|
* Create a `BindingOp`, not yet transformed into a particular type of binding.
|
|
9416
9408
|
*/
|
|
9417
|
-
function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute,
|
|
9409
|
+
function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
|
|
9418
9410
|
return {
|
|
9419
9411
|
kind: OpKind.Binding,
|
|
9420
9412
|
bindingKind: kind,
|
|
@@ -9424,8 +9416,10 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
|
|
|
9424
9416
|
unit,
|
|
9425
9417
|
securityContext,
|
|
9426
9418
|
isTextAttribute,
|
|
9427
|
-
|
|
9428
|
-
|
|
9419
|
+
isStructuralTemplateAttribute,
|
|
9420
|
+
templateKind,
|
|
9421
|
+
i18nContext: null,
|
|
9422
|
+
i18nMessage,
|
|
9429
9423
|
sourceSpan,
|
|
9430
9424
|
...NEW_OP,
|
|
9431
9425
|
};
|
|
@@ -9433,7 +9427,7 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
|
|
|
9433
9427
|
/**
|
|
9434
9428
|
* Create a `PropertyOp`.
|
|
9435
9429
|
*/
|
|
9436
|
-
function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext,
|
|
9430
|
+
function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext, isStructuralTemplateAttribute, templateKind, i18nContext, i18nMessage, sourceSpan) {
|
|
9437
9431
|
return {
|
|
9438
9432
|
kind: OpKind.Property,
|
|
9439
9433
|
target,
|
|
@@ -9442,8 +9436,10 @@ function createPropertyOp(target, name, expression, isAnimationTrigger, security
|
|
|
9442
9436
|
isAnimationTrigger,
|
|
9443
9437
|
securityContext,
|
|
9444
9438
|
sanitizer: null,
|
|
9445
|
-
|
|
9439
|
+
isStructuralTemplateAttribute,
|
|
9440
|
+
templateKind,
|
|
9446
9441
|
i18nContext,
|
|
9442
|
+
i18nMessage,
|
|
9447
9443
|
sourceSpan,
|
|
9448
9444
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9449
9445
|
...TRAIT_CONSUMES_VARS,
|
|
@@ -9508,7 +9504,7 @@ function createClassMapOp(xref, expression, sourceSpan) {
|
|
|
9508
9504
|
/**
|
|
9509
9505
|
* Create an `AttributeOp`.
|
|
9510
9506
|
*/
|
|
9511
|
-
function createAttributeOp(target, name, expression, securityContext, isTextAttribute,
|
|
9507
|
+
function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
|
|
9512
9508
|
return {
|
|
9513
9509
|
kind: OpKind.Attribute,
|
|
9514
9510
|
target,
|
|
@@ -9517,8 +9513,10 @@ function createAttributeOp(target, name, expression, securityContext, isTextAttr
|
|
|
9517
9513
|
securityContext,
|
|
9518
9514
|
sanitizer: null,
|
|
9519
9515
|
isTextAttribute,
|
|
9520
|
-
|
|
9521
|
-
|
|
9516
|
+
isStructuralTemplateAttribute,
|
|
9517
|
+
templateKind,
|
|
9518
|
+
i18nContext: null,
|
|
9519
|
+
i18nMessage,
|
|
9522
9520
|
sourceSpan,
|
|
9523
9521
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9524
9522
|
...TRAIT_CONSUMES_VARS,
|
|
@@ -10152,24 +10150,6 @@ class ReadTemporaryExpr extends ExpressionBase {
|
|
|
10152
10150
|
return r;
|
|
10153
10151
|
}
|
|
10154
10152
|
}
|
|
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
10153
|
class SlotLiteralExpr extends ExpressionBase {
|
|
10174
10154
|
constructor(slot) {
|
|
10175
10155
|
super();
|
|
@@ -10300,7 +10280,6 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10300
10280
|
case OpKind.ClassProp:
|
|
10301
10281
|
case OpKind.ClassMap:
|
|
10302
10282
|
case OpKind.Binding:
|
|
10303
|
-
case OpKind.HostProperty:
|
|
10304
10283
|
if (op.expression instanceof Interpolation) {
|
|
10305
10284
|
transformExpressionsInInterpolation(op.expression, transform, flags);
|
|
10306
10285
|
}
|
|
@@ -10309,6 +10288,7 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10309
10288
|
}
|
|
10310
10289
|
break;
|
|
10311
10290
|
case OpKind.Property:
|
|
10291
|
+
case OpKind.HostProperty:
|
|
10312
10292
|
case OpKind.Attribute:
|
|
10313
10293
|
if (op.expression instanceof Interpolation) {
|
|
10314
10294
|
transformExpressionsInInterpolation(op.expression, transform, flags);
|
|
@@ -10354,6 +10334,8 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10354
10334
|
case OpKind.ExtractedAttribute:
|
|
10355
10335
|
op.expression =
|
|
10356
10336
|
op.expression && transformExpressionsInExpression(op.expression, transform, flags);
|
|
10337
|
+
op.trustedValueFn = op.trustedValueFn &&
|
|
10338
|
+
transformExpressionsInExpression(op.trustedValueFn, transform, flags);
|
|
10357
10339
|
break;
|
|
10358
10340
|
case OpKind.RepeaterCreate:
|
|
10359
10341
|
op.track = transformExpressionsInExpression(op.track, transform, flags);
|
|
@@ -10485,6 +10467,14 @@ function transformExpressionsInExpression(expr, transform, flags) {
|
|
|
10485
10467
|
else if (expr instanceof NotExpr) {
|
|
10486
10468
|
expr.condition = transformExpressionsInExpression(expr.condition, transform, flags);
|
|
10487
10469
|
}
|
|
10470
|
+
else if (expr instanceof TaggedTemplateExpr) {
|
|
10471
|
+
expr.tag = transformExpressionsInExpression(expr.tag, transform, flags);
|
|
10472
|
+
expr.template.expressions =
|
|
10473
|
+
expr.template.expressions.map(e => transformExpressionsInExpression(e, transform, flags));
|
|
10474
|
+
}
|
|
10475
|
+
else if (expr instanceof WrappedNodeExpr) {
|
|
10476
|
+
// TODO: Do we need to transform any TS nodes nested inside of this expression?
|
|
10477
|
+
}
|
|
10488
10478
|
else if (expr instanceof ReadVarExpr || expr instanceof ExternalExpr ||
|
|
10489
10479
|
expr instanceof LiteralExpr) {
|
|
10490
10480
|
// No action for these types.
|
|
@@ -10846,7 +10836,7 @@ function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace
|
|
|
10846
10836
|
...NEW_OP,
|
|
10847
10837
|
};
|
|
10848
10838
|
}
|
|
10849
|
-
function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, sourceSpan) {
|
|
10839
|
+
function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, i18nPlaceholder, emptyI18nPlaceholder, sourceSpan) {
|
|
10850
10840
|
return {
|
|
10851
10841
|
kind: OpKind.RepeaterCreate,
|
|
10852
10842
|
attributes: null,
|
|
@@ -10864,6 +10854,8 @@ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, so
|
|
|
10864
10854
|
vars: null,
|
|
10865
10855
|
varNames,
|
|
10866
10856
|
usesComponentInstance: false,
|
|
10857
|
+
i18nPlaceholder,
|
|
10858
|
+
emptyI18nPlaceholder,
|
|
10867
10859
|
sourceSpan,
|
|
10868
10860
|
...TRAIT_CONSUMES_SLOT,
|
|
10869
10861
|
...NEW_OP,
|
|
@@ -10912,7 +10904,9 @@ function createTextOp(xref, initialValue, sourceSpan) {
|
|
|
10912
10904
|
/**
|
|
10913
10905
|
* Create a `ListenerOp`. Host bindings reuse all the listener logic.
|
|
10914
10906
|
*/
|
|
10915
|
-
function createListenerOp(target, targetSlot, name, tag, animationPhase, hostListener, sourceSpan) {
|
|
10907
|
+
function createListenerOp(target, targetSlot, name, tag, handlerOps, animationPhase, eventTarget, hostListener, sourceSpan) {
|
|
10908
|
+
const handlerList = new OpList();
|
|
10909
|
+
handlerList.push(handlerOps);
|
|
10916
10910
|
return {
|
|
10917
10911
|
kind: OpKind.Listener,
|
|
10918
10912
|
target,
|
|
@@ -10920,11 +10914,12 @@ function createListenerOp(target, targetSlot, name, tag, animationPhase, hostLis
|
|
|
10920
10914
|
tag,
|
|
10921
10915
|
hostListener,
|
|
10922
10916
|
name,
|
|
10923
|
-
handlerOps:
|
|
10917
|
+
handlerOps: handlerList,
|
|
10924
10918
|
handlerFnName: null,
|
|
10925
10919
|
consumesDollarEvent: false,
|
|
10926
10920
|
isAnimationListener: animationPhase !== null,
|
|
10927
|
-
animationPhase
|
|
10921
|
+
animationPhase,
|
|
10922
|
+
eventTarget,
|
|
10928
10923
|
sourceSpan,
|
|
10929
10924
|
...NEW_OP,
|
|
10930
10925
|
};
|
|
@@ -10971,7 +10966,7 @@ function createProjectionOp(xref, selector, i18nPlaceholder, attributes, sourceS
|
|
|
10971
10966
|
/**
|
|
10972
10967
|
* Create an `ExtractedAttributeOp`.
|
|
10973
10968
|
*/
|
|
10974
|
-
function createExtractedAttributeOp(target, bindingKind, name, expression, i18nContext) {
|
|
10969
|
+
function createExtractedAttributeOp(target, bindingKind, name, expression, i18nContext, i18nMessage, securityContext) {
|
|
10975
10970
|
return {
|
|
10976
10971
|
kind: OpKind.ExtractedAttribute,
|
|
10977
10972
|
target,
|
|
@@ -10979,6 +10974,9 @@ function createExtractedAttributeOp(target, bindingKind, name, expression, i18nC
|
|
|
10979
10974
|
name,
|
|
10980
10975
|
expression,
|
|
10981
10976
|
i18nContext,
|
|
10977
|
+
i18nMessage,
|
|
10978
|
+
securityContext,
|
|
10979
|
+
trustedValueFn: null,
|
|
10982
10980
|
...NEW_OP,
|
|
10983
10981
|
};
|
|
10984
10982
|
}
|
|
@@ -11121,13 +11119,15 @@ function literalOrArrayLiteral$1(value) {
|
|
|
11121
11119
|
return literal(value, INFERRED_TYPE);
|
|
11122
11120
|
}
|
|
11123
11121
|
|
|
11124
|
-
function createHostPropertyOp(name, expression, isAnimationTrigger, i18nContext, sourceSpan) {
|
|
11122
|
+
function createHostPropertyOp(name, expression, isAnimationTrigger, i18nContext, securityContext, sourceSpan) {
|
|
11125
11123
|
return {
|
|
11126
11124
|
kind: OpKind.HostProperty,
|
|
11127
11125
|
name,
|
|
11128
11126
|
expression,
|
|
11129
11127
|
isAnimationTrigger,
|
|
11130
11128
|
i18nContext,
|
|
11129
|
+
securityContext,
|
|
11130
|
+
sanitizer: null,
|
|
11131
11131
|
sourceSpan,
|
|
11132
11132
|
...TRAIT_CONSUMES_VARS,
|
|
11133
11133
|
...NEW_OP,
|
|
@@ -11407,68 +11407,52 @@ function needsApplication(i18nContexts, op) {
|
|
|
11407
11407
|
* after the last update instruction that depends on that slot.
|
|
11408
11408
|
*/
|
|
11409
11409
|
function assignI18nSlotDependencies(job) {
|
|
11410
|
-
const i18nLastSlotConsumers = new Map();
|
|
11411
|
-
let lastSlotConsumer = null;
|
|
11412
|
-
let currentI18nOp = null;
|
|
11413
11410
|
for (const unit of job.units) {
|
|
11414
|
-
//
|
|
11415
|
-
|
|
11416
|
-
|
|
11417
|
-
|
|
11411
|
+
// The first update op.
|
|
11412
|
+
let updateOp = unit.update.head;
|
|
11413
|
+
// I18n expressions currently being moved during the iteration.
|
|
11414
|
+
let i18nExpressionsInProgress = [];
|
|
11415
|
+
// Non-null while we are iterating through an i18nStart/i18nEnd pair
|
|
11416
|
+
let state = null;
|
|
11417
|
+
for (const createOp of unit.create) {
|
|
11418
|
+
if (createOp.kind === OpKind.I18nStart) {
|
|
11419
|
+
state = {
|
|
11420
|
+
blockXref: createOp.xref,
|
|
11421
|
+
lastSlotConsumer: createOp.xref,
|
|
11422
|
+
};
|
|
11418
11423
|
}
|
|
11419
|
-
|
|
11420
|
-
|
|
11421
|
-
|
|
11422
|
-
|
|
11423
|
-
|
|
11424
|
-
|
|
11425
|
-
|
|
11424
|
+
else if (createOp.kind === OpKind.I18nEnd) {
|
|
11425
|
+
for (const op of i18nExpressionsInProgress) {
|
|
11426
|
+
op.target = state.lastSlotConsumer;
|
|
11427
|
+
OpList.insertBefore(op, updateOp);
|
|
11428
|
+
}
|
|
11429
|
+
i18nExpressionsInProgress.length = 0;
|
|
11430
|
+
state = null;
|
|
11431
|
+
}
|
|
11432
|
+
if (hasConsumesSlotTrait(createOp)) {
|
|
11433
|
+
if (state !== null) {
|
|
11434
|
+
state.lastSlotConsumer = createOp.xref;
|
|
11435
|
+
}
|
|
11436
|
+
while (true) {
|
|
11437
|
+
if (updateOp.next === null) {
|
|
11438
|
+
break;
|
|
11426
11439
|
}
|
|
11427
|
-
if (
|
|
11428
|
-
|
|
11440
|
+
if (state !== null && updateOp.kind === OpKind.I18nExpression &&
|
|
11441
|
+
updateOp.usage === I18nExpressionFor.I18nText &&
|
|
11442
|
+
updateOp.i18nOwner === state.blockXref) {
|
|
11443
|
+
const opToRemove = updateOp;
|
|
11444
|
+
updateOp = updateOp.next;
|
|
11445
|
+
OpList.remove(opToRemove);
|
|
11446
|
+
i18nExpressionsInProgress.push(opToRemove);
|
|
11447
|
+
continue;
|
|
11429
11448
|
}
|
|
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');
|
|
11449
|
+
if (hasDependsOnSlotContextTrait(updateOp) && updateOp.target !== createOp.xref) {
|
|
11450
|
+
break;
|
|
11451
|
+
}
|
|
11452
|
+
updateOp = updateOp.next;
|
|
11464
11453
|
}
|
|
11465
|
-
op.target = target;
|
|
11466
|
-
moveAfterTarget = op.target;
|
|
11467
11454
|
}
|
|
11468
11455
|
}
|
|
11469
|
-
if (moveAfterTarget !== null) {
|
|
11470
|
-
unit.update.push(opsToMove);
|
|
11471
|
-
}
|
|
11472
11456
|
}
|
|
11473
11457
|
}
|
|
11474
11458
|
|
|
@@ -11501,19 +11485,21 @@ function extractAttributes(job) {
|
|
|
11501
11485
|
case OpKind.Property:
|
|
11502
11486
|
if (!op.isAnimationTrigger) {
|
|
11503
11487
|
let bindingKind;
|
|
11504
|
-
if (op.
|
|
11488
|
+
if (op.i18nMessage !== null && op.templateKind === null) {
|
|
11505
11489
|
// If the binding has an i18n context, it is an i18n attribute, and should have that
|
|
11506
11490
|
// kind in the consts array.
|
|
11507
11491
|
bindingKind = BindingKind.I18n;
|
|
11508
11492
|
}
|
|
11509
|
-
else if (op.
|
|
11510
|
-
// TODO: How do i18n attributes on templates work?!
|
|
11493
|
+
else if (op.isStructuralTemplateAttribute) {
|
|
11511
11494
|
bindingKind = BindingKind.Template;
|
|
11512
11495
|
}
|
|
11513
11496
|
else {
|
|
11514
11497
|
bindingKind = BindingKind.Property;
|
|
11515
11498
|
}
|
|
11516
|
-
OpList.insertBefore(
|
|
11499
|
+
OpList.insertBefore(
|
|
11500
|
+
// Deliberaly null i18nMessage value
|
|
11501
|
+
createExtractedAttributeOp(op.target, bindingKind, op.name, /* expression */ null, /* i18nContext */ null,
|
|
11502
|
+
/* i18nMessage */ null, op.securityContext), lookupElement$2(elements, op.target));
|
|
11517
11503
|
}
|
|
11518
11504
|
break;
|
|
11519
11505
|
case OpKind.StyleProp:
|
|
@@ -11524,12 +11510,16 @@ function extractAttributes(job) {
|
|
|
11524
11510
|
// mode.
|
|
11525
11511
|
if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
|
|
11526
11512
|
op.expression instanceof EmptyExpr) {
|
|
11527
|
-
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name,
|
|
11513
|
+
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, /* expression */ null,
|
|
11514
|
+
/* i18nContext */ null,
|
|
11515
|
+
/* i18nMessage */ null, SecurityContext.STYLE), lookupElement$2(elements, op.target));
|
|
11528
11516
|
}
|
|
11529
11517
|
break;
|
|
11530
11518
|
case OpKind.Listener:
|
|
11531
11519
|
if (!op.isAnimationListener) {
|
|
11532
|
-
const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null,
|
|
11520
|
+
const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, /* expression */ null,
|
|
11521
|
+
/* i18nContext */ null,
|
|
11522
|
+
/* i18nMessage */ null, SecurityContext.NONE);
|
|
11533
11523
|
if (job.kind === CompilationJobKind.Host) {
|
|
11534
11524
|
// This attribute will apply to the enclosing host binding compilation unit, so order
|
|
11535
11525
|
// doesn't matter.
|
|
@@ -11578,7 +11568,7 @@ function extractAttributeOp(unit, op, elements) {
|
|
|
11578
11568
|
}
|
|
11579
11569
|
}
|
|
11580
11570
|
if (extractable) {
|
|
11581
|
-
const extractedAttributeOp = createExtractedAttributeOp(op.target, op.
|
|
11571
|
+
const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isStructuralTemplateAttribute ? BindingKind.Template : BindingKind.Attribute, op.name, op.expression, op.i18nContext, op.i18nMessage, op.securityContext);
|
|
11582
11572
|
if (unit.job.kind === CompilationJobKind.Host) {
|
|
11583
11573
|
// This attribute will apply to the enclosing host binding compilation unit, so order doesn't
|
|
11584
11574
|
// matter.
|
|
@@ -11625,16 +11615,16 @@ function specializeBindings(job) {
|
|
|
11625
11615
|
target.nonBindable = true;
|
|
11626
11616
|
}
|
|
11627
11617
|
else {
|
|
11628
|
-
OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.
|
|
11618
|
+
OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.isStructuralTemplateAttribute, op.templateKind, op.i18nMessage, op.sourceSpan));
|
|
11629
11619
|
}
|
|
11630
11620
|
break;
|
|
11631
11621
|
case BindingKind.Property:
|
|
11632
11622
|
case BindingKind.Animation:
|
|
11633
11623
|
if (job.kind === CompilationJobKind.Host) {
|
|
11634
|
-
OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.i18nContext, op.sourceSpan));
|
|
11624
|
+
OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.i18nContext, op.securityContext, op.sourceSpan));
|
|
11635
11625
|
}
|
|
11636
11626
|
else {
|
|
11637
|
-
OpList.replace(op, createPropertyOp(op.target, op.name, op.expression, op.bindingKind === BindingKind.Animation, op.securityContext, op.
|
|
11627
|
+
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
11628
|
}
|
|
11639
11629
|
break;
|
|
11640
11630
|
case BindingKind.I18n:
|
|
@@ -11871,7 +11861,7 @@ function collectElementConsts(job) {
|
|
|
11871
11861
|
if (op.kind === OpKind.ExtractedAttribute) {
|
|
11872
11862
|
const attributes = allElementAttributes.get(op.target) || new ElementAttributes();
|
|
11873
11863
|
allElementAttributes.set(op.target, attributes);
|
|
11874
|
-
attributes.add(op.bindingKind, op.name, op.expression);
|
|
11864
|
+
attributes.add(op.bindingKind, op.name, op.expression, op.trustedValueFn);
|
|
11875
11865
|
OpList.remove(op);
|
|
11876
11866
|
}
|
|
11877
11867
|
}
|
|
@@ -11937,11 +11927,12 @@ class ElementAttributes {
|
|
|
11937
11927
|
get i18n() {
|
|
11938
11928
|
return this.byKind.get(BindingKind.I18n) ?? FLYWEIGHT_ARRAY;
|
|
11939
11929
|
}
|
|
11940
|
-
add(kind, name, value) {
|
|
11930
|
+
add(kind, name, value, trustedValueFn) {
|
|
11941
11931
|
if (this.known.has(name)) {
|
|
11942
11932
|
return;
|
|
11943
11933
|
}
|
|
11944
11934
|
this.known.add(name);
|
|
11935
|
+
// TODO: Can this be its own phase
|
|
11945
11936
|
if (name === 'ngProjectAs') {
|
|
11946
11937
|
if (value === null || !(value instanceof LiteralExpr) || (value.value == null) ||
|
|
11947
11938
|
(typeof value.value?.toString() !== 'string')) {
|
|
@@ -11957,7 +11948,15 @@ class ElementAttributes {
|
|
|
11957
11948
|
if (value === null) {
|
|
11958
11949
|
throw Error('Attribute, i18n attribute, & style element attributes must have a value');
|
|
11959
11950
|
}
|
|
11960
|
-
|
|
11951
|
+
if (trustedValueFn !== null) {
|
|
11952
|
+
if (!isStringLiteral(value)) {
|
|
11953
|
+
throw Error('AssertionError: extracted attribute value should be string literal');
|
|
11954
|
+
}
|
|
11955
|
+
array.push(taggedTemplate(trustedValueFn, new TemplateLiteral([new TemplateLiteralElement(value.value)], []), undefined, value.sourceSpan));
|
|
11956
|
+
}
|
|
11957
|
+
else {
|
|
11958
|
+
array.push(value);
|
|
11959
|
+
}
|
|
11961
11960
|
}
|
|
11962
11961
|
}
|
|
11963
11962
|
arrayFor(kind) {
|
|
@@ -12101,6 +12100,9 @@ function createI18nContexts(job) {
|
|
|
12101
12100
|
const rootContexts = new Map();
|
|
12102
12101
|
let currentI18nOp = null;
|
|
12103
12102
|
let xref;
|
|
12103
|
+
// We use the message instead of the message ID, because placeholder values might differ even
|
|
12104
|
+
// when IDs are the same.
|
|
12105
|
+
const messageToContext = new Map();
|
|
12104
12106
|
for (const unit of job.units) {
|
|
12105
12107
|
for (const op of unit.create) {
|
|
12106
12108
|
switch (op.kind) {
|
|
@@ -12138,6 +12140,25 @@ function createI18nContexts(job) {
|
|
|
12138
12140
|
break;
|
|
12139
12141
|
}
|
|
12140
12142
|
}
|
|
12143
|
+
for (const op of unit.ops()) {
|
|
12144
|
+
switch (op.kind) {
|
|
12145
|
+
case OpKind.Binding:
|
|
12146
|
+
case OpKind.Property:
|
|
12147
|
+
case OpKind.Attribute:
|
|
12148
|
+
case OpKind.ExtractedAttribute:
|
|
12149
|
+
if (!op.i18nMessage) {
|
|
12150
|
+
continue;
|
|
12151
|
+
}
|
|
12152
|
+
if (!messageToContext.has(op.i18nMessage)) {
|
|
12153
|
+
// create the context
|
|
12154
|
+
const i18nContext = job.allocateXrefId();
|
|
12155
|
+
unit.create.push(createI18nContextOp(I18nContextKind.Attr, i18nContext, null, op.i18nMessage, null));
|
|
12156
|
+
messageToContext.set(op.i18nMessage, i18nContext);
|
|
12157
|
+
}
|
|
12158
|
+
op.i18nContext = messageToContext.get(op.i18nMessage);
|
|
12159
|
+
break;
|
|
12160
|
+
}
|
|
12161
|
+
}
|
|
12141
12162
|
}
|
|
12142
12163
|
// Assign contexts to child i18n blocks, now that all root i18n blocks have their context
|
|
12143
12164
|
// assigned.
|
|
@@ -12812,9 +12833,15 @@ function recursivelyProcessView(view, parentScope) {
|
|
|
12812
12833
|
for (const op of view.create) {
|
|
12813
12834
|
switch (op.kind) {
|
|
12814
12835
|
case OpKind.Template:
|
|
12836
|
+
// Descend into child embedded views.
|
|
12837
|
+
recursivelyProcessView(view.job.views.get(op.xref), scope);
|
|
12838
|
+
break;
|
|
12815
12839
|
case OpKind.RepeaterCreate:
|
|
12816
12840
|
// Descend into child embedded views.
|
|
12817
12841
|
recursivelyProcessView(view.job.views.get(op.xref), scope);
|
|
12842
|
+
if (op.emptyView) {
|
|
12843
|
+
recursivelyProcessView(view.job.views.get(op.emptyView), scope);
|
|
12844
|
+
}
|
|
12818
12845
|
break;
|
|
12819
12846
|
case OpKind.Listener:
|
|
12820
12847
|
// Prepend variables to listener handler functions.
|
|
@@ -19954,7 +19981,7 @@ const ESCAPE = '\uFFFD';
|
|
|
19954
19981
|
function collectI18nConsts(job) {
|
|
19955
19982
|
const fileBasedI18nSuffix = job.relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_').toUpperCase() + '_';
|
|
19956
19983
|
// Step One: Build up various lookup maps we need to collect all the consts.
|
|
19957
|
-
// Context Xref -> Extracted Attribute
|
|
19984
|
+
// Context Xref -> Extracted Attribute Ops
|
|
19958
19985
|
const extractedAttributesByI18nContext = new Map();
|
|
19959
19986
|
// Element/ElementStart Xref -> I18n Attributes config op
|
|
19960
19987
|
const i18nAttributesByElement = new Map();
|
|
@@ -19965,7 +19992,9 @@ function collectI18nConsts(job) {
|
|
|
19965
19992
|
for (const unit of job.units) {
|
|
19966
19993
|
for (const op of unit.ops()) {
|
|
19967
19994
|
if (op.kind === OpKind.ExtractedAttribute && op.i18nContext !== null) {
|
|
19968
|
-
extractedAttributesByI18nContext.
|
|
19995
|
+
const attributes = extractedAttributesByI18nContext.get(op.i18nContext) ?? [];
|
|
19996
|
+
attributes.push(op);
|
|
19997
|
+
extractedAttributesByI18nContext.set(op.i18nContext, attributes);
|
|
19969
19998
|
}
|
|
19970
19999
|
else if (op.kind === OpKind.I18nAttributes) {
|
|
19971
20000
|
i18nAttributesByElement.set(op.target, op);
|
|
@@ -20010,9 +20039,11 @@ function collectI18nConsts(job) {
|
|
|
20010
20039
|
i18nValuesByContext.set(op.i18nContext, mainVar);
|
|
20011
20040
|
// This i18n message may correspond to an individual extracted attribute. If so, The
|
|
20012
20041
|
// value of that attribute is updated to read the extracted i18n variable.
|
|
20013
|
-
const
|
|
20014
|
-
if (
|
|
20015
|
-
|
|
20042
|
+
const attributesForMessage = extractedAttributesByI18nContext.get(op.i18nContext);
|
|
20043
|
+
if (attributesForMessage !== undefined) {
|
|
20044
|
+
for (const attr of attributesForMessage) {
|
|
20045
|
+
attr.expression = mainVar.clone();
|
|
20046
|
+
}
|
|
20016
20047
|
}
|
|
20017
20048
|
}
|
|
20018
20049
|
}
|
|
@@ -20800,14 +20831,14 @@ function parseExtractedStyles(job) {
|
|
|
20800
20831
|
if (op.name === 'style') {
|
|
20801
20832
|
const parsedStyles = parse(op.expression.value);
|
|
20802
20833
|
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);
|
|
20834
|
+
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1]), null, null, SecurityContext.STYLE), op);
|
|
20804
20835
|
}
|
|
20805
20836
|
OpList.remove(op);
|
|
20806
20837
|
}
|
|
20807
20838
|
else if (op.name === 'class') {
|
|
20808
20839
|
const parsedClasses = op.expression.value.trim().split(/\s+/g);
|
|
20809
20840
|
for (const parsedClass of parsedClasses) {
|
|
20810
|
-
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null, null), op);
|
|
20841
|
+
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null, null, null, SecurityContext.NONE), op);
|
|
20811
20842
|
}
|
|
20812
20843
|
OpList.remove(op);
|
|
20813
20844
|
}
|
|
@@ -20973,22 +21004,37 @@ function propagateI18nBlocksToTemplates(unit, subTemplateIndex) {
|
|
|
20973
21004
|
i18nBlock = null;
|
|
20974
21005
|
break;
|
|
20975
21006
|
case OpKind.Template:
|
|
20976
|
-
|
|
20977
|
-
|
|
20978
|
-
|
|
20979
|
-
|
|
20980
|
-
|
|
20981
|
-
|
|
20982
|
-
|
|
20983
|
-
|
|
20984
|
-
|
|
21007
|
+
subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.xref), i18nBlock, op.i18nPlaceholder, subTemplateIndex);
|
|
21008
|
+
break;
|
|
21009
|
+
case OpKind.RepeaterCreate:
|
|
21010
|
+
// Propagate i18n blocks to the @for template.
|
|
21011
|
+
const forView = unit.job.views.get(op.xref);
|
|
21012
|
+
subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.xref), i18nBlock, op.i18nPlaceholder, subTemplateIndex);
|
|
21013
|
+
// Then if there's an @empty template, propagate the i18n blocks for it as well.
|
|
21014
|
+
if (op.emptyView !== null) {
|
|
21015
|
+
subTemplateIndex = propagateI18nBlocksForView(unit.job.views.get(op.emptyView), i18nBlock, op.emptyI18nPlaceholder, subTemplateIndex);
|
|
20985
21016
|
}
|
|
20986
|
-
|
|
20987
|
-
subTemplateIndex = propagateI18nBlocksToTemplates(templateView, subTemplateIndex);
|
|
21017
|
+
break;
|
|
20988
21018
|
}
|
|
20989
21019
|
}
|
|
20990
21020
|
return subTemplateIndex;
|
|
20991
21021
|
}
|
|
21022
|
+
/**
|
|
21023
|
+
* Propagate i18n blocks for a view.
|
|
21024
|
+
*/
|
|
21025
|
+
function propagateI18nBlocksForView(view, i18nBlock, i18nPlaceholder, subTemplateIndex) {
|
|
21026
|
+
// We found an <ng-template> inside an i18n block; increment the sub-template counter and
|
|
21027
|
+
// wrap the template's view in a child i18n block.
|
|
21028
|
+
if (i18nPlaceholder !== undefined) {
|
|
21029
|
+
if (i18nBlock === null) {
|
|
21030
|
+
throw Error('Expected template with i18n placeholder to be in an i18n block.');
|
|
21031
|
+
}
|
|
21032
|
+
subTemplateIndex++;
|
|
21033
|
+
wrapTemplateWithI18n(view, i18nBlock);
|
|
21034
|
+
}
|
|
21035
|
+
// Continue traversing inside the template's view.
|
|
21036
|
+
return propagateI18nBlocksToTemplates(view, subTemplateIndex);
|
|
21037
|
+
}
|
|
20992
21038
|
/**
|
|
20993
21039
|
* Wraps a template view with i18n start and end ops.
|
|
20994
21040
|
*/
|
|
@@ -21154,17 +21200,13 @@ function disableBindings() {
|
|
|
21154
21200
|
function enableBindings() {
|
|
21155
21201
|
return call(Identifiers.enableBindings, [], null);
|
|
21156
21202
|
}
|
|
21157
|
-
function listener(name, handlerFn, sourceSpan) {
|
|
21158
|
-
|
|
21159
|
-
|
|
21160
|
-
|
|
21161
|
-
|
|
21162
|
-
}
|
|
21163
|
-
|
|
21164
|
-
return call(Identifiers.syntheticHostListener, [
|
|
21165
|
-
literal(name),
|
|
21166
|
-
handlerFn,
|
|
21167
|
-
], sourceSpan);
|
|
21203
|
+
function listener(name, handlerFn, eventTargetResolver, syntheticHost, sourceSpan) {
|
|
21204
|
+
const args = [literal(name), handlerFn];
|
|
21205
|
+
if (eventTargetResolver !== null) {
|
|
21206
|
+
args.push(literal(false)); // `useCapture` flag, defaults to `false`
|
|
21207
|
+
args.push(importExpr(eventTargetResolver));
|
|
21208
|
+
}
|
|
21209
|
+
return call(syntheticHost ? Identifiers.syntheticHostListener : Identifiers.listener, args, sourceSpan);
|
|
21168
21210
|
}
|
|
21169
21211
|
function pipe(slot, name) {
|
|
21170
21212
|
return call(Identifiers.pipe, [
|
|
@@ -21425,8 +21467,12 @@ function classMapInterpolate(strings, expressions, sourceSpan) {
|
|
|
21425
21467
|
const interpolationArgs = collateInterpolationArgs(strings, expressions);
|
|
21426
21468
|
return callVariadicInstruction(CLASS_MAP_INTERPOLATE_CONFIG, [], interpolationArgs, [], sourceSpan);
|
|
21427
21469
|
}
|
|
21428
|
-
function hostProperty(name, expression, sourceSpan) {
|
|
21429
|
-
|
|
21470
|
+
function hostProperty(name, expression, sanitizer, sourceSpan) {
|
|
21471
|
+
const args = [literal(name), expression];
|
|
21472
|
+
if (sanitizer !== null) {
|
|
21473
|
+
args.push(sanitizer);
|
|
21474
|
+
}
|
|
21475
|
+
return call(Identifiers.hostProperty, args, sourceSpan);
|
|
21430
21476
|
}
|
|
21431
21477
|
function syntheticHostProperty(name, expression, sourceSpan) {
|
|
21432
21478
|
return call(Identifiers.syntheticHostProperty, [literal(name), expression], sourceSpan);
|
|
@@ -21644,14 +21690,12 @@ function callVariadicInstruction(config, baseArgs, interpolationArgs, extraArgs,
|
|
|
21644
21690
|
}
|
|
21645
21691
|
|
|
21646
21692
|
/**
|
|
21647
|
-
* Map of
|
|
21693
|
+
* Map of target resolvers for event listeners.
|
|
21648
21694
|
*/
|
|
21649
|
-
const
|
|
21650
|
-
[
|
|
21651
|
-
[
|
|
21652
|
-
[
|
|
21653
|
-
[SanitizerFn.Script, Identifiers.sanitizeScript],
|
|
21654
|
-
[SanitizerFn.Style, Identifiers.sanitizeStyle], [SanitizerFn.Url, Identifiers.sanitizeUrl]
|
|
21695
|
+
const GLOBAL_TARGET_RESOLVERS$1 = new Map([
|
|
21696
|
+
['window', Identifiers.resolveWindow],
|
|
21697
|
+
['document', Identifiers.resolveDocument],
|
|
21698
|
+
['body', Identifiers.resolveBody],
|
|
21655
21699
|
]);
|
|
21656
21700
|
/**
|
|
21657
21701
|
* Compiles semantic operations across all views and generates output `o.Statement`s with actual
|
|
@@ -21728,10 +21772,11 @@ function reifyCreateOperations(unit, ops) {
|
|
|
21728
21772
|
break;
|
|
21729
21773
|
case OpKind.Listener:
|
|
21730
21774
|
const listenerFn = reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, op.consumesDollarEvent);
|
|
21731
|
-
const
|
|
21732
|
-
|
|
21733
|
-
|
|
21734
|
-
|
|
21775
|
+
const eventTargetResolver = op.eventTarget ? GLOBAL_TARGET_RESOLVERS$1.get(op.eventTarget) : null;
|
|
21776
|
+
if (eventTargetResolver === undefined) {
|
|
21777
|
+
throw new Error(`AssertionError: unknown event target ${op.eventTarget}`);
|
|
21778
|
+
}
|
|
21779
|
+
OpList.replace(op, listener(op.name, listenerFn, eventTargetResolver, op.hostListener && op.isAnimationListener, op.sourceSpan));
|
|
21735
21780
|
break;
|
|
21736
21781
|
case OpKind.Variable:
|
|
21737
21782
|
if (op.variable.name === null) {
|
|
@@ -21894,7 +21939,7 @@ function reifyUpdateOperations(_unit, ops) {
|
|
|
21894
21939
|
OpList.replace(op, syntheticHostProperty(op.name, op.expression, op.sourceSpan));
|
|
21895
21940
|
}
|
|
21896
21941
|
else {
|
|
21897
|
-
OpList.replace(op, hostProperty(op.name, op.expression, op.sourceSpan));
|
|
21942
|
+
OpList.replace(op, hostProperty(op.name, op.expression, op.sanitizer, op.sourceSpan));
|
|
21898
21943
|
}
|
|
21899
21944
|
}
|
|
21900
21945
|
break;
|
|
@@ -21973,8 +22018,6 @@ function reifyIrExpression(expr) {
|
|
|
21973
22018
|
return pipeBind(expr.targetSlot.slot, expr.varOffset, expr.args);
|
|
21974
22019
|
case ExpressionKind.PipeBindingVariadic:
|
|
21975
22020
|
return pipeBindV(expr.targetSlot.slot, expr.varOffset, expr.args);
|
|
21976
|
-
case ExpressionKind.SanitizerExpr:
|
|
21977
|
-
return importExpr(sanitizerIdentifierMap.get(expr.fn));
|
|
21978
22021
|
case ExpressionKind.SlotLiteralExpr:
|
|
21979
22022
|
return literal(expr.slot.slot);
|
|
21980
22023
|
default:
|
|
@@ -22267,10 +22310,11 @@ function resolvePlaceholdersForView(job, unit, i18nContexts, elements, pendingSt
|
|
|
22267
22310
|
}
|
|
22268
22311
|
break;
|
|
22269
22312
|
case OpKind.Template:
|
|
22313
|
+
const view = job.views.get(op.xref);
|
|
22270
22314
|
if (op.i18nPlaceholder === undefined) {
|
|
22271
22315
|
// If there is no i18n placeholder, just recurse into the view in case it contains i18n
|
|
22272
22316
|
// blocks.
|
|
22273
|
-
resolvePlaceholdersForView(job,
|
|
22317
|
+
resolvePlaceholdersForView(job, view, i18nContexts, elements);
|
|
22274
22318
|
}
|
|
22275
22319
|
else {
|
|
22276
22320
|
if (currentOps === null) {
|
|
@@ -22281,14 +22325,59 @@ function resolvePlaceholdersForView(job, unit, i18nContexts, elements, pendingSt
|
|
|
22281
22325
|
// the current template as a pending structural directive to be recorded when we find
|
|
22282
22326
|
// the element, content, or template it belongs to. This allows us to create combined
|
|
22283
22327
|
// values that represent, e.g. the start of a template and element at the same time.
|
|
22284
|
-
resolvePlaceholdersForView(job,
|
|
22328
|
+
resolvePlaceholdersForView(job, view, i18nContexts, elements, op);
|
|
22285
22329
|
}
|
|
22286
22330
|
else {
|
|
22287
22331
|
// If this is some other kind of template, we can record its start, recurse into its
|
|
22288
22332
|
// 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);
|
|
22333
|
+
recordTemplateStart(job, view, op.handle.slot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22334
|
+
resolvePlaceholdersForView(job, view, i18nContexts, elements);
|
|
22335
|
+
recordTemplateClose(job, view, op.handle.slot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22336
|
+
pendingStructuralDirective = undefined;
|
|
22337
|
+
}
|
|
22338
|
+
}
|
|
22339
|
+
break;
|
|
22340
|
+
case OpKind.RepeaterCreate:
|
|
22341
|
+
if (pendingStructuralDirective !== undefined) {
|
|
22342
|
+
throw Error('AssertionError: Unexpected structural directive associated with @for block');
|
|
22343
|
+
}
|
|
22344
|
+
// RepeaterCreate has 3 slots: the first is for the op itself, the second is for the @for
|
|
22345
|
+
// template and the (optional) third is for the @empty template.
|
|
22346
|
+
const forSlot = op.handle.slot + 1;
|
|
22347
|
+
const forView = job.views.get(op.xref);
|
|
22348
|
+
// First record all of the placeholders for the @for template.
|
|
22349
|
+
if (op.i18nPlaceholder === undefined) {
|
|
22350
|
+
// If there is no i18n placeholder, just recurse into the view in case it contains i18n
|
|
22351
|
+
// blocks.
|
|
22352
|
+
resolvePlaceholdersForView(job, forView, i18nContexts, elements);
|
|
22353
|
+
}
|
|
22354
|
+
else {
|
|
22355
|
+
if (currentOps === null) {
|
|
22356
|
+
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
22357
|
+
}
|
|
22358
|
+
recordTemplateStart(job, forView, forSlot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22359
|
+
resolvePlaceholdersForView(job, forView, i18nContexts, elements);
|
|
22360
|
+
recordTemplateClose(job, forView, forSlot, op.i18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22361
|
+
pendingStructuralDirective = undefined;
|
|
22362
|
+
}
|
|
22363
|
+
// Then if there's an @empty template, add its placeholders as well.
|
|
22364
|
+
if (op.emptyView !== null) {
|
|
22365
|
+
// RepeaterCreate has 3 slots: the first is for the op itself, the second is for the @for
|
|
22366
|
+
// template and the (optional) third is for the @empty template.
|
|
22367
|
+
const emptySlot = op.handle.slot + 2;
|
|
22368
|
+
const emptyView = job.views.get(op.emptyView);
|
|
22369
|
+
if (op.emptyI18nPlaceholder === undefined) {
|
|
22370
|
+
// If there is no i18n placeholder, just recurse into the view in case it contains i18n
|
|
22371
|
+
// blocks.
|
|
22372
|
+
resolvePlaceholdersForView(job, emptyView, i18nContexts, elements);
|
|
22373
|
+
}
|
|
22374
|
+
else {
|
|
22375
|
+
if (currentOps === null) {
|
|
22376
|
+
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
22377
|
+
}
|
|
22378
|
+
recordTemplateStart(job, emptyView, emptySlot, op.emptyI18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22379
|
+
resolvePlaceholdersForView(job, emptyView, i18nContexts, elements);
|
|
22380
|
+
recordTemplateClose(job, emptyView, emptySlot, op.emptyI18nPlaceholder, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22292
22381
|
pendingStructuralDirective = undefined;
|
|
22293
22382
|
}
|
|
22294
22383
|
}
|
|
@@ -22336,8 +22425,8 @@ function recordElementClose(op, i18nContext, i18nBlock, structuralDirective) {
|
|
|
22336
22425
|
/**
|
|
22337
22426
|
* Records an i18n param value for the start of a template.
|
|
22338
22427
|
*/
|
|
22339
|
-
function recordTemplateStart(job,
|
|
22340
|
-
let { startName, closeName } =
|
|
22428
|
+
function recordTemplateStart(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) {
|
|
22429
|
+
let { startName, closeName } = i18nPlaceholder;
|
|
22341
22430
|
let flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.OpenTag;
|
|
22342
22431
|
// For self-closing tags, there is no close tag placeholder. Instead, the start tag
|
|
22343
22432
|
// placeholder accounts for the start and close of the element.
|
|
@@ -22352,20 +22441,20 @@ function recordTemplateStart(job, op, i18nContext, i18nBlock, structuralDirectiv
|
|
|
22352
22441
|
}
|
|
22353
22442
|
// Record the start of the template. For the sub-template index, pass the index for the template's
|
|
22354
22443
|
// view, rather than the current i18n block's index.
|
|
22355
|
-
addParam(i18nContext.params, startName,
|
|
22444
|
+
addParam(i18nContext.params, startName, slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, view), flags);
|
|
22356
22445
|
}
|
|
22357
22446
|
/**
|
|
22358
22447
|
* Records an i18n param value for the closing of a template.
|
|
22359
22448
|
*/
|
|
22360
|
-
function recordTemplateClose(job,
|
|
22361
|
-
const { startName, closeName } =
|
|
22449
|
+
function recordTemplateClose(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) {
|
|
22450
|
+
const { startName, closeName } = i18nPlaceholder;
|
|
22362
22451
|
const flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag;
|
|
22363
22452
|
// Self-closing tags don't have a closing tag placeholder, instead the template's closing is
|
|
22364
22453
|
// recorded via an additional flag on the template start value.
|
|
22365
22454
|
if (closeName) {
|
|
22366
22455
|
// Record the closing of the template. For the sub-template index, pass the index for the
|
|
22367
22456
|
// template's view, rather than the current i18n block's index.
|
|
22368
|
-
addParam(i18nContext.params, closeName,
|
|
22457
|
+
addParam(i18nContext.params, closeName, slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, view), flags);
|
|
22369
22458
|
// If the template is associated with a structural directive, record the structural directive's
|
|
22370
22459
|
// closing after. Since this template must be in the structural directive's view, we can just
|
|
22371
22460
|
// directly use the current i18n block's sub-template index.
|
|
@@ -22378,8 +22467,8 @@ function recordTemplateClose(job, op, i18nContext, i18nBlock, structuralDirectiv
|
|
|
22378
22467
|
* Get the subTemplateIndex for the given template op. For template ops, use the subTemplateIndex of
|
|
22379
22468
|
* the child i18n block inside the template.
|
|
22380
22469
|
*/
|
|
22381
|
-
function getSubTemplateIndexForTemplateTag(job, i18nOp,
|
|
22382
|
-
for (const childOp of
|
|
22470
|
+
function getSubTemplateIndexForTemplateTag(job, i18nOp, view) {
|
|
22471
|
+
for (const childOp of view.create) {
|
|
22383
22472
|
if (childOp.kind === OpKind.I18nStart) {
|
|
22384
22473
|
return childOp.subTemplateIndex;
|
|
22385
22474
|
}
|
|
@@ -22596,12 +22685,20 @@ function processLexicalScope(unit, ops, savedView) {
|
|
|
22596
22685
|
}
|
|
22597
22686
|
|
|
22598
22687
|
/**
|
|
22599
|
-
*
|
|
22688
|
+
* Map of security contexts to their sanitizer function.
|
|
22600
22689
|
*/
|
|
22601
|
-
const
|
|
22602
|
-
[SecurityContext.HTML,
|
|
22603
|
-
[SecurityContext.
|
|
22604
|
-
[SecurityContext.
|
|
22690
|
+
const sanitizerFns = new Map([
|
|
22691
|
+
[SecurityContext.HTML, Identifiers.sanitizeHtml],
|
|
22692
|
+
[SecurityContext.RESOURCE_URL, Identifiers.sanitizeResourceUrl],
|
|
22693
|
+
[SecurityContext.SCRIPT, Identifiers.sanitizeScript],
|
|
22694
|
+
[SecurityContext.STYLE, Identifiers.sanitizeStyle], [SecurityContext.URL, Identifiers.sanitizeUrl]
|
|
22695
|
+
]);
|
|
22696
|
+
/**
|
|
22697
|
+
* Map of security contexts to their trusted value function.
|
|
22698
|
+
*/
|
|
22699
|
+
const trustedValueFns = new Map([
|
|
22700
|
+
[SecurityContext.HTML, Identifiers.trustConstantHtml],
|
|
22701
|
+
[SecurityContext.RESOURCE_URL, Identifiers.trustConstantResourceUrl],
|
|
22605
22702
|
]);
|
|
22606
22703
|
/**
|
|
22607
22704
|
* Resolves sanitization functions for ops that need them.
|
|
@@ -22609,24 +22706,61 @@ const sanitizers = new Map([
|
|
|
22609
22706
|
function resolveSanitizers(job) {
|
|
22610
22707
|
for (const unit of job.units) {
|
|
22611
22708
|
const elements = createOpXrefMap(unit);
|
|
22612
|
-
|
|
22709
|
+
// For normal element bindings we create trusted values for security sensitive constant
|
|
22710
|
+
// attributes. However, for host bindings we skip this step (this matches what
|
|
22711
|
+
// TemplateDefinitionBuilder does).
|
|
22712
|
+
// TODO: Is the TDB behavior correct here?
|
|
22713
|
+
if (job.kind !== CompilationJobKind.Host) {
|
|
22714
|
+
for (const op of unit.create) {
|
|
22715
|
+
if (op.kind === OpKind.ExtractedAttribute) {
|
|
22716
|
+
const trustedValueFn = trustedValueFns.get(getOnlySecurityContext(op.securityContext)) ?? null;
|
|
22717
|
+
op.trustedValueFn = trustedValueFn !== null ? importExpr(trustedValueFn) : null;
|
|
22718
|
+
}
|
|
22719
|
+
}
|
|
22720
|
+
}
|
|
22613
22721
|
for (const op of unit.update) {
|
|
22614
22722
|
switch (op.kind) {
|
|
22615
22723
|
case OpKind.Property:
|
|
22616
22724
|
case OpKind.Attribute:
|
|
22617
|
-
|
|
22618
|
-
|
|
22725
|
+
case OpKind.HostProperty:
|
|
22726
|
+
let sanitizerFn = null;
|
|
22727
|
+
if (Array.isArray(op.securityContext) && op.securityContext.length === 2 &&
|
|
22728
|
+
op.securityContext.indexOf(SecurityContext.URL) > -1 &&
|
|
22729
|
+
op.securityContext.indexOf(SecurityContext.RESOURCE_URL) > -1) {
|
|
22730
|
+
// When the host element isn't known, some URL attributes (such as "src" and "href") may
|
|
22731
|
+
// be part of multiple different security contexts. In this case we use special
|
|
22732
|
+
// sanitization function and select the actual sanitizer at runtime based on a tag name
|
|
22733
|
+
// that is provided while invoking sanitization function.
|
|
22734
|
+
sanitizerFn = Identifiers.sanitizeUrlOrResourceUrl;
|
|
22735
|
+
}
|
|
22736
|
+
else {
|
|
22737
|
+
sanitizerFn = sanitizerFns.get(getOnlySecurityContext(op.securityContext)) ?? null;
|
|
22738
|
+
}
|
|
22739
|
+
op.sanitizer = sanitizerFn !== null ? importExpr(sanitizerFn) : null;
|
|
22619
22740
|
// If there was no sanitization function found based on the security context of an
|
|
22620
22741
|
// attribute/property, check whether this attribute/property is one of the
|
|
22621
22742
|
// security-sensitive <iframe> attributes (and that the current element is actually an
|
|
22622
22743
|
// <iframe>).
|
|
22623
22744
|
if (op.sanitizer === null) {
|
|
22624
|
-
|
|
22625
|
-
if (
|
|
22626
|
-
|
|
22745
|
+
let isIframe = false;
|
|
22746
|
+
if (job.kind === CompilationJobKind.Host || op.kind === OpKind.HostProperty) {
|
|
22747
|
+
// Note: for host bindings defined on a directive, we do not try to find all
|
|
22748
|
+
// possible places where it can be matched, so we can not determine whether
|
|
22749
|
+
// the host element is an <iframe>. In this case, we just assume it is and append a
|
|
22750
|
+
// validation function, which is invoked at runtime and would have access to the
|
|
22751
|
+
// underlying DOM element to check if it's an <iframe> and if so - run extra checks.
|
|
22752
|
+
isIframe = true;
|
|
22753
|
+
}
|
|
22754
|
+
else {
|
|
22755
|
+
// For a normal binding we can just check if the element its on is an iframe.
|
|
22756
|
+
const ownerOp = elements.get(op.target);
|
|
22757
|
+
if (ownerOp === undefined || !isElementOrContainerOp(ownerOp)) {
|
|
22758
|
+
throw Error('Property should have an element-like owner');
|
|
22759
|
+
}
|
|
22760
|
+
isIframe = isIframeElement$1(ownerOp);
|
|
22627
22761
|
}
|
|
22628
|
-
if (
|
|
22629
|
-
op.sanitizer =
|
|
22762
|
+
if (isIframe && isIframeSecuritySensitiveAttr(op.name)) {
|
|
22763
|
+
op.sanitizer = importExpr(Identifiers.validateIframeAttribute);
|
|
22630
22764
|
}
|
|
22631
22765
|
}
|
|
22632
22766
|
break;
|
|
@@ -22640,6 +22774,22 @@ function resolveSanitizers(job) {
|
|
|
22640
22774
|
function isIframeElement$1(op) {
|
|
22641
22775
|
return op.kind === OpKind.ElementStart && op.tag?.toLowerCase() === 'iframe';
|
|
22642
22776
|
}
|
|
22777
|
+
/**
|
|
22778
|
+
* Asserts that there is only a single security context and returns it.
|
|
22779
|
+
*/
|
|
22780
|
+
function getOnlySecurityContext(securityContext) {
|
|
22781
|
+
if (Array.isArray(securityContext)) {
|
|
22782
|
+
if (securityContext.length > 1) {
|
|
22783
|
+
// TODO: What should we do here? TDB just took the first one, but this feels like something we
|
|
22784
|
+
// would want to know about and create a special case for like we did for Url/ResourceUrl. My
|
|
22785
|
+
// guess is that, outside of the Url/ResourceUrl case, this never actually happens. If there
|
|
22786
|
+
// do turn out to be other cases, throwing an error until we can address it feels safer.
|
|
22787
|
+
throw Error(`AssertionError: Ambiguous security context`);
|
|
22788
|
+
}
|
|
22789
|
+
return securityContext[0] || SecurityContext.NONE;
|
|
22790
|
+
}
|
|
22791
|
+
return securityContext;
|
|
22792
|
+
}
|
|
22643
22793
|
|
|
22644
22794
|
/**
|
|
22645
22795
|
* When inside of a listener, we may need access to one or more enclosing views. Therefore, each
|
|
@@ -22743,6 +22893,8 @@ function allocateSlots(job) {
|
|
|
22743
22893
|
// operation itself, so it can be emitted later.
|
|
22744
22894
|
const childView = job.views.get(op.xref);
|
|
22745
22895
|
op.decls = childView.decls;
|
|
22896
|
+
// TODO: currently we handle the decls for the RepeaterCreate empty template in the reify
|
|
22897
|
+
// phase. We should handle that here instead.
|
|
22746
22898
|
}
|
|
22747
22899
|
}
|
|
22748
22900
|
}
|
|
@@ -23066,6 +23218,8 @@ function countVariables(job) {
|
|
|
23066
23218
|
}
|
|
23067
23219
|
const childView = job.views.get(op.xref);
|
|
23068
23220
|
op.vars = childView.vars;
|
|
23221
|
+
// TODO: currently we handle the vars for the RepeaterCreate empty template in the reify
|
|
23222
|
+
// phase. We should handle that here instead.
|
|
23069
23223
|
}
|
|
23070
23224
|
}
|
|
23071
23225
|
}
|
|
@@ -23587,10 +23741,10 @@ const phases = [
|
|
|
23587
23741
|
{ kind: CompilationJobKind.Tmpl, fn: emitNamespaceChanges },
|
|
23588
23742
|
{ kind: CompilationJobKind.Tmpl, fn: propagateI18nBlocks },
|
|
23589
23743
|
{ kind: CompilationJobKind.Tmpl, fn: wrapI18nIcus },
|
|
23590
|
-
{ kind: CompilationJobKind.Tmpl, fn: createI18nContexts },
|
|
23591
23744
|
{ kind: CompilationJobKind.Both, fn: specializeStyleBindings },
|
|
23592
23745
|
{ kind: CompilationJobKind.Both, fn: specializeBindings },
|
|
23593
23746
|
{ kind: CompilationJobKind.Both, fn: extractAttributes },
|
|
23747
|
+
{ kind: CompilationJobKind.Tmpl, fn: createI18nContexts },
|
|
23594
23748
|
{ kind: CompilationJobKind.Both, fn: parseExtractedStyles },
|
|
23595
23749
|
{ kind: CompilationJobKind.Tmpl, fn: removeEmptyBindings },
|
|
23596
23750
|
{ kind: CompilationJobKind.Both, fn: collapseSingletonInterpolations },
|
|
@@ -23608,7 +23762,7 @@ const phases = [
|
|
|
23608
23762
|
{ kind: CompilationJobKind.Tmpl, fn: generateProjectionDefs },
|
|
23609
23763
|
{ kind: CompilationJobKind.Tmpl, fn: generateVariables },
|
|
23610
23764
|
{ kind: CompilationJobKind.Tmpl, fn: saveAndRestoreView },
|
|
23611
|
-
{ kind: CompilationJobKind.
|
|
23765
|
+
{ kind: CompilationJobKind.Both, fn: deleteAnyCasts },
|
|
23612
23766
|
{ kind: CompilationJobKind.Both, fn: resolveDollarEvent },
|
|
23613
23767
|
{ kind: CompilationJobKind.Tmpl, fn: generateRepeaterDerivedVars },
|
|
23614
23768
|
{ kind: CompilationJobKind.Tmpl, fn: generateTrackVariables },
|
|
@@ -23616,7 +23770,7 @@ const phases = [
|
|
|
23616
23770
|
{ kind: CompilationJobKind.Tmpl, fn: resolveDeferTargetNames },
|
|
23617
23771
|
{ kind: CompilationJobKind.Tmpl, fn: optimizeTrackFns },
|
|
23618
23772
|
{ kind: CompilationJobKind.Both, fn: resolveContexts },
|
|
23619
|
-
{ kind: CompilationJobKind.
|
|
23773
|
+
{ kind: CompilationJobKind.Both, fn: resolveSanitizers },
|
|
23620
23774
|
{ kind: CompilationJobKind.Tmpl, fn: liftLocalRefs },
|
|
23621
23775
|
{ kind: CompilationJobKind.Both, fn: generateNullishCoalesceExpressions },
|
|
23622
23776
|
{ kind: CompilationJobKind.Both, fn: expandSafeReads },
|
|
@@ -23752,6 +23906,10 @@ function emitHostBindingFunction(job) {
|
|
|
23752
23906
|
}
|
|
23753
23907
|
|
|
23754
23908
|
const compatibilityMode = CompatibilityMode.TemplateDefinitionBuilder;
|
|
23909
|
+
// Schema containing DOM elements and their properties.
|
|
23910
|
+
const domSchema = new DomElementSchemaRegistry();
|
|
23911
|
+
// Tag name of the `ng-template` element.
|
|
23912
|
+
const NG_TEMPLATE_TAG_NAME$1 = 'ng-template';
|
|
23755
23913
|
/**
|
|
23756
23914
|
* Process a template AST and convert it into a `ComponentCompilation` in the intermediate
|
|
23757
23915
|
* representation.
|
|
@@ -23769,10 +23927,24 @@ function ingestComponent(componentName, template, constantPool, relativeContextF
|
|
|
23769
23927
|
function ingestHostBinding(input, bindingParser, constantPool) {
|
|
23770
23928
|
const job = new HostBindingCompilationJob(input.componentName, constantPool, compatibilityMode);
|
|
23771
23929
|
for (const property of input.properties ?? []) {
|
|
23772
|
-
|
|
23930
|
+
let bindingKind = BindingKind.Property;
|
|
23931
|
+
// TODO: this should really be handled in the parser.
|
|
23932
|
+
if (property.name.startsWith('attr.')) {
|
|
23933
|
+
property.name = property.name.substring('attr.'.length);
|
|
23934
|
+
bindingKind = BindingKind.Attribute;
|
|
23935
|
+
}
|
|
23936
|
+
if (property.isAnimation) {
|
|
23937
|
+
bindingKind = BindingKind.Animation;
|
|
23938
|
+
}
|
|
23939
|
+
const securityContexts = bindingParser
|
|
23940
|
+
.calcPossibleSecurityContexts(input.componentSelector, property.name, bindingKind === BindingKind.Attribute)
|
|
23941
|
+
.filter(context => context !== SecurityContext.NONE);
|
|
23942
|
+
ingestHostProperty(job, property, bindingKind, false, securityContexts);
|
|
23773
23943
|
}
|
|
23774
23944
|
for (const [name, expr] of Object.entries(input.attributes) ?? []) {
|
|
23775
|
-
|
|
23945
|
+
const securityContexts = bindingParser.calcPossibleSecurityContexts(input.componentSelector, name, true)
|
|
23946
|
+
.filter(context => context !== SecurityContext.NONE);
|
|
23947
|
+
ingestHostAttribute(job, name, expr, securityContexts);
|
|
23776
23948
|
}
|
|
23777
23949
|
for (const event of input.events ?? []) {
|
|
23778
23950
|
ingestHostEvent(job, event);
|
|
@@ -23781,7 +23953,7 @@ function ingestHostBinding(input, bindingParser, constantPool) {
|
|
|
23781
23953
|
}
|
|
23782
23954
|
// TODO: We should refactor the parser to use the same types and structures for host bindings as
|
|
23783
23955
|
// with ordinary components. This would allow us to share a lot more ingestion code.
|
|
23784
|
-
function ingestHostProperty(job, property, isTextAttribute) {
|
|
23956
|
+
function ingestHostProperty(job, property, bindingKind, isTextAttribute, securityContexts) {
|
|
23785
23957
|
let expression;
|
|
23786
23958
|
const ast = property.expression.ast;
|
|
23787
23959
|
if (ast instanceof Interpolation$1) {
|
|
@@ -23790,26 +23962,18 @@ function ingestHostProperty(job, property, isTextAttribute) {
|
|
|
23790
23962
|
else {
|
|
23791
23963
|
expression = convertAst(ast, job, property.sourceSpan);
|
|
23792
23964
|
}
|
|
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));
|
|
23965
|
+
job.root.update.push(createBindingOp(job.root.xref, bindingKind, property.name, expression, null, securityContexts, isTextAttribute, false, null, /* TODO: How do Host bindings handle i18n attrs? */ null, property.sourceSpan));
|
|
23804
23966
|
}
|
|
23805
|
-
function ingestHostAttribute(job, name, value) {
|
|
23806
|
-
const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null,
|
|
23967
|
+
function ingestHostAttribute(job, name, value, securityContexts) {
|
|
23968
|
+
const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, securityContexts, true, false, null,
|
|
23807
23969
|
/* TODO */ null,
|
|
23808
23970
|
/* TODO: host attribute source spans */ null);
|
|
23809
23971
|
job.root.update.push(attrBinding);
|
|
23810
23972
|
}
|
|
23811
23973
|
function ingestHostEvent(job, event) {
|
|
23812
|
-
const
|
|
23974
|
+
const [phase, target] = event.type === 0 /* e.ParsedEventType.Regular */ ? [null, event.targetOrPhase] :
|
|
23975
|
+
[event.targetOrPhase, null];
|
|
23976
|
+
const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, [], phase, target, true, event.sourceSpan);
|
|
23813
23977
|
// TODO: Can this be a chain?
|
|
23814
23978
|
eventBinding.handlerOps.push(createStatementOp(new ReturnStatement(convertAst(event.handler.ast, job, event.sourceSpan), event.handlerSpan)));
|
|
23815
23979
|
job.root.create.push(eventBinding);
|
|
@@ -23866,8 +24030,14 @@ function ingestElement(unit, element) {
|
|
|
23866
24030
|
const [namespaceKey, elementName] = splitNsName(element.name);
|
|
23867
24031
|
const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan);
|
|
23868
24032
|
unit.create.push(startOp);
|
|
23869
|
-
|
|
24033
|
+
ingestElementBindings(unit, startOp, element);
|
|
23870
24034
|
ingestReferences(startOp, element);
|
|
24035
|
+
// Start i18n, if needed, goes after the element create and bindings, but before the nodes
|
|
24036
|
+
let i18nBlockId = null;
|
|
24037
|
+
if (element.i18n instanceof Message) {
|
|
24038
|
+
i18nBlockId = unit.job.allocateXrefId();
|
|
24039
|
+
unit.create.push(createI18nStartOp(i18nBlockId, element.i18n));
|
|
24040
|
+
}
|
|
23871
24041
|
ingestNodes(unit, element.children);
|
|
23872
24042
|
// The source span for the end op is typically the element closing tag. However, if no closing tag
|
|
23873
24043
|
// exists, such as in `<input>`, we use the start source span instead. Usually the start and end
|
|
@@ -23877,9 +24047,7 @@ function ingestElement(unit, element) {
|
|
|
23877
24047
|
const endOp = createElementEndOp(id, element.endSourceSpan ?? element.startSourceSpan);
|
|
23878
24048
|
unit.create.push(endOp);
|
|
23879
24049
|
// 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);
|
|
24050
|
+
if (i18nBlockId !== null) {
|
|
23883
24051
|
OpList.insertBefore(createI18nEndOp(i18nBlockId), endOp);
|
|
23884
24052
|
}
|
|
23885
24053
|
}
|
|
@@ -23905,7 +24073,7 @@ function ingestTemplate(unit, tmpl) {
|
|
|
23905
24073
|
const templateKind = isPlainTemplate(tmpl) ? TemplateKind.NgTemplate : TemplateKind.Structural;
|
|
23906
24074
|
const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan);
|
|
23907
24075
|
unit.create.push(templateOp);
|
|
23908
|
-
|
|
24076
|
+
ingestTemplateBindings(unit, templateOp, tmpl, templateKind);
|
|
23909
24077
|
ingestReferences(templateOp, tmpl);
|
|
23910
24078
|
ingestNodes(childView, tmpl.children);
|
|
23911
24079
|
for (const { name, value } of tmpl.variables) {
|
|
@@ -23921,7 +24089,7 @@ function ingestTemplate(unit, tmpl) {
|
|
|
23921
24089
|
}
|
|
23922
24090
|
}
|
|
23923
24091
|
/**
|
|
23924
|
-
* Ingest a
|
|
24092
|
+
* Ingest a content node from the AST into the given `ViewCompilation`.
|
|
23925
24093
|
*/
|
|
23926
24094
|
function ingestContent(unit, content) {
|
|
23927
24095
|
if (content.i18n !== undefined && !(content.i18n instanceof TagPlaceholder)) {
|
|
@@ -23930,7 +24098,8 @@ function ingestContent(unit, content) {
|
|
|
23930
24098
|
const attrs = content.attributes.flatMap(a => [a.name, a.value]);
|
|
23931
24099
|
const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.i18n, attrs, content.sourceSpan);
|
|
23932
24100
|
for (const attr of content.attributes) {
|
|
23933
|
-
|
|
24101
|
+
const securityContext = domSchema.securityContext(content.name, attr.name, true);
|
|
24102
|
+
unit.update.push(createBindingOp(op.xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
|
|
23934
24103
|
}
|
|
23935
24104
|
unit.create.push(op);
|
|
23936
24105
|
}
|
|
@@ -24045,13 +24214,16 @@ function ingestSwitchBlock(unit, switchBlock) {
|
|
|
24045
24214
|
const conditional = createConditionalOp(firstXref, firstSlotHandle, convertAst(switchBlock.expression, unit.job, null), conditions, switchBlock.sourceSpan);
|
|
24046
24215
|
unit.update.push(conditional);
|
|
24047
24216
|
}
|
|
24048
|
-
function ingestDeferView(unit, suffix, children, sourceSpan) {
|
|
24217
|
+
function ingestDeferView(unit, suffix, i18nMeta, children, sourceSpan) {
|
|
24218
|
+
if (i18nMeta !== undefined && !(i18nMeta instanceof BlockPlaceholder)) {
|
|
24219
|
+
throw Error('Unhandled i18n metadata type for defer block');
|
|
24220
|
+
}
|
|
24049
24221
|
if (children === undefined) {
|
|
24050
24222
|
return null;
|
|
24051
24223
|
}
|
|
24052
24224
|
const secondaryView = unit.job.allocateView(unit.xref);
|
|
24053
24225
|
ingestNodes(secondaryView, children);
|
|
24054
|
-
const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML,
|
|
24226
|
+
const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, i18nMeta, sourceSpan);
|
|
24055
24227
|
unit.create.push(templateOp);
|
|
24056
24228
|
return templateOp;
|
|
24057
24229
|
}
|
|
@@ -24061,10 +24233,10 @@ function ingestDeferBlock(unit, deferBlock) {
|
|
|
24061
24233
|
throw new Error(`AssertionError: unable to find metadata for deferred block`);
|
|
24062
24234
|
}
|
|
24063
24235
|
// 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);
|
|
24236
|
+
const main = ingestDeferView(unit, '', deferBlock.i18n, deferBlock.children, deferBlock.sourceSpan);
|
|
24237
|
+
const loading = ingestDeferView(unit, 'Loading', deferBlock.loading?.i18n, deferBlock.loading?.children, deferBlock.loading?.sourceSpan);
|
|
24238
|
+
const placeholder = ingestDeferView(unit, 'Placeholder', deferBlock.placeholder?.i18n, deferBlock.placeholder?.children, deferBlock.placeholder?.sourceSpan);
|
|
24239
|
+
const error = ingestDeferView(unit, 'Error', deferBlock.error?.i18n, deferBlock.error?.children, deferBlock.error?.sourceSpan);
|
|
24068
24240
|
// Create the main defer op, and ops for all secondary views.
|
|
24069
24241
|
const deferXref = unit.job.allocateXrefId();
|
|
24070
24242
|
const deferOp = createDeferOp(deferXref, main.xref, main.handle, blockMeta, deferBlock.sourceSpan);
|
|
@@ -24198,8 +24370,17 @@ function ingestForBlock(unit, forBlock) {
|
|
|
24198
24370
|
$odd: forBlock.contextVariables.$odd.name,
|
|
24199
24371
|
$implicit: forBlock.item.name,
|
|
24200
24372
|
};
|
|
24373
|
+
if (forBlock.i18n !== undefined && !(forBlock.i18n instanceof BlockPlaceholder)) {
|
|
24374
|
+
throw Error('AssertionError: Unhandled i18n metadata type or @for');
|
|
24375
|
+
}
|
|
24376
|
+
if (forBlock.empty?.i18n !== undefined &&
|
|
24377
|
+
!(forBlock.empty.i18n instanceof BlockPlaceholder)) {
|
|
24378
|
+
throw Error('AssertionError: Unhandled i18n metadata type or @empty');
|
|
24379
|
+
}
|
|
24380
|
+
const i18nPlaceholder = forBlock.i18n;
|
|
24381
|
+
const emptyI18nPlaceholder = forBlock.empty?.i18n;
|
|
24201
24382
|
const tagName = ingestControlFlowInsertionPoint(unit, repeaterView.xref, forBlock);
|
|
24202
|
-
const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, forBlock.sourceSpan);
|
|
24383
|
+
const repeaterCreate = createRepeaterCreateOp(repeaterView.xref, emptyView?.xref ?? null, tagName, track, varNames, i18nPlaceholder, emptyI18nPlaceholder, forBlock.sourceSpan);
|
|
24203
24384
|
unit.create.push(repeaterCreate);
|
|
24204
24385
|
const expression = convertAst(forBlock.expression, unit.job, convertSourceSpan(forBlock.expression.span, forBlock.sourceSpan));
|
|
24205
24386
|
const repeater = createRepeaterOp(repeaterCreate.xref, repeaterCreate.handle, expression, forBlock.sourceSpan);
|
|
@@ -24317,6 +24498,27 @@ function convertAst(ast, job, baseSourceSpan) {
|
|
|
24317
24498
|
throw new Error(`Unhandled expression type "${ast.constructor.name}" in file "${baseSourceSpan?.start.file.url}"`);
|
|
24318
24499
|
}
|
|
24319
24500
|
}
|
|
24501
|
+
function convertAstWithInterpolation(job, value, i18nMeta) {
|
|
24502
|
+
let expression;
|
|
24503
|
+
if (value instanceof Interpolation$1) {
|
|
24504
|
+
expression = new Interpolation(value.strings, value.expressions.map(e => convertAst(e, job, null)), Object.keys(asMessage(i18nMeta)?.placeholders ?? {}));
|
|
24505
|
+
}
|
|
24506
|
+
else if (value instanceof AST) {
|
|
24507
|
+
expression = convertAst(value, job, null);
|
|
24508
|
+
}
|
|
24509
|
+
else {
|
|
24510
|
+
expression = literal(value);
|
|
24511
|
+
}
|
|
24512
|
+
return expression;
|
|
24513
|
+
}
|
|
24514
|
+
// TODO: Can we populate Template binding kinds in ingest?
|
|
24515
|
+
const BINDING_KINDS = new Map([
|
|
24516
|
+
[0 /* e.BindingType.Property */, BindingKind.Property],
|
|
24517
|
+
[1 /* e.BindingType.Attribute */, BindingKind.Attribute],
|
|
24518
|
+
[2 /* e.BindingType.Class */, BindingKind.ClassName],
|
|
24519
|
+
[3 /* e.BindingType.Style */, BindingKind.StyleProperty],
|
|
24520
|
+
[4 /* e.BindingType.Animation */, BindingKind.Animation],
|
|
24521
|
+
]);
|
|
24320
24522
|
/**
|
|
24321
24523
|
* Checks whether the given template is a plain ng-template (as opposed to another kind of template
|
|
24322
24524
|
* such as a structural directive template or control flow template). This is checked based on the
|
|
@@ -24335,149 +24537,184 @@ function convertAst(ast, job, baseSourceSpan) {
|
|
|
24335
24537
|
* | `<ng-template *ngIf>` (structural) | null |
|
|
24336
24538
|
*/
|
|
24337
24539
|
function isPlainTemplate(tmpl) {
|
|
24338
|
-
return splitNsName(tmpl.tagName ?? '')[1] ===
|
|
24540
|
+
return splitNsName(tmpl.tagName ?? '')[1] === NG_TEMPLATE_TAG_NAME$1;
|
|
24339
24541
|
}
|
|
24340
24542
|
/**
|
|
24341
|
-
*
|
|
24342
|
-
* to their IR representation.
|
|
24543
|
+
* Ensures that the i18nMeta, if provided, is an i18n.Message.
|
|
24343
24544
|
*/
|
|
24344
|
-
function
|
|
24345
|
-
|
|
24346
|
-
|
|
24347
|
-
|
|
24348
|
-
|
|
24349
|
-
|
|
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
|
-
}
|
|
24545
|
+
function asMessage(i18nMeta) {
|
|
24546
|
+
if (i18nMeta == null) {
|
|
24547
|
+
return null;
|
|
24548
|
+
}
|
|
24549
|
+
if (!(i18nMeta instanceof Message)) {
|
|
24550
|
+
throw Error(`Expected i18n meta to be a Message, but got: ${i18nMeta.constructor.name}`);
|
|
24363
24551
|
}
|
|
24552
|
+
return i18nMeta;
|
|
24553
|
+
}
|
|
24554
|
+
/**
|
|
24555
|
+
* Process all of the bindings on an element in the template AST and convert them to their IR
|
|
24556
|
+
* representation.
|
|
24557
|
+
*/
|
|
24558
|
+
function ingestElementBindings(unit, op, element) {
|
|
24559
|
+
let bindings = new Array();
|
|
24364
24560
|
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;
|
|
24561
|
+
// Attribute literal bindings, such as `attr.foo="bar"`.
|
|
24562
|
+
const securityContext = domSchema.securityContext(element.name, attr.name, true);
|
|
24563
|
+
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
24564
|
}
|
|
24371
24565
|
for (const input of element.inputs) {
|
|
24372
|
-
|
|
24373
|
-
|
|
24566
|
+
// All dynamic bindings (both attribute and property bindings).
|
|
24567
|
+
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
24568
|
}
|
|
24569
|
+
unit.create.push(bindings.filter((b) => b?.kind === OpKind.ExtractedAttribute));
|
|
24570
|
+
unit.update.push(bindings.filter((b) => b?.kind === OpKind.Binding));
|
|
24375
24571
|
for (const output of element.outputs) {
|
|
24376
|
-
|
|
24377
|
-
|
|
24378
|
-
if (output.phase === null) {
|
|
24379
|
-
throw Error('Animation listener should have a phase');
|
|
24380
|
-
}
|
|
24572
|
+
if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) {
|
|
24573
|
+
throw Error('Animation listener should have a phase');
|
|
24381
24574
|
}
|
|
24382
|
-
|
|
24383
|
-
|
|
24384
|
-
|
|
24385
|
-
|
|
24386
|
-
|
|
24387
|
-
|
|
24388
|
-
|
|
24389
|
-
|
|
24390
|
-
|
|
24391
|
-
|
|
24392
|
-
|
|
24393
|
-
|
|
24394
|
-
|
|
24395
|
-
|
|
24575
|
+
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));
|
|
24576
|
+
}
|
|
24577
|
+
// If any of the bindings on this element have an i18n message, then an i18n attrs configuration
|
|
24578
|
+
// op is also required.
|
|
24579
|
+
if (bindings.some(b => b?.i18nMessage) !== null) {
|
|
24580
|
+
unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
|
|
24581
|
+
}
|
|
24582
|
+
}
|
|
24583
|
+
/**
|
|
24584
|
+
* Process all of the bindings on a template in the template AST and convert them to their IR
|
|
24585
|
+
* representation.
|
|
24586
|
+
*/
|
|
24587
|
+
function ingestTemplateBindings(unit, op, template, templateKind) {
|
|
24588
|
+
let bindings = new Array();
|
|
24589
|
+
for (const attr of template.templateAttrs) {
|
|
24590
|
+
if (attr instanceof TextAttribute) {
|
|
24591
|
+
const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
|
|
24592
|
+
bindings.push(createTemplateBinding(unit, op.xref, 1 /* e.BindingType.Attribute */, attr.name, attr.value, null, securityContext, true, templateKind, asMessage(attr.i18n), attr.sourceSpan));
|
|
24396
24593
|
}
|
|
24397
24594
|
else {
|
|
24398
|
-
|
|
24595
|
+
bindings.push(createTemplateBinding(unit, op.xref, attr.type, attr.name, astOf(attr.value), attr.unit, attr.securityContext, true, templateKind, asMessage(attr.i18n), attr.sourceSpan));
|
|
24596
|
+
}
|
|
24597
|
+
}
|
|
24598
|
+
for (const attr of template.attributes) {
|
|
24599
|
+
// Attribute literal bindings, such as `attr.foo="bar"`.
|
|
24600
|
+
const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
|
|
24601
|
+
bindings.push(createTemplateBinding(unit, op.xref, 1 /* e.BindingType.Attribute */, attr.name, attr.value, null, securityContext, false, templateKind, asMessage(attr.i18n), attr.sourceSpan));
|
|
24602
|
+
}
|
|
24603
|
+
for (const input of template.inputs) {
|
|
24604
|
+
// Dynamic bindings (both attribute and property bindings).
|
|
24605
|
+
bindings.push(createTemplateBinding(unit, op.xref, input.type, input.name, astOf(input.value), input.unit, input.securityContext, false, templateKind, asMessage(input.i18n), input.sourceSpan));
|
|
24606
|
+
}
|
|
24607
|
+
unit.create.push(bindings.filter((b) => b?.kind === OpKind.ExtractedAttribute));
|
|
24608
|
+
unit.update.push(bindings.filter((b) => b?.kind === OpKind.Binding));
|
|
24609
|
+
for (const output of template.outputs) {
|
|
24610
|
+
if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) {
|
|
24611
|
+
throw Error('Animation listener should have a phase');
|
|
24399
24612
|
}
|
|
24400
|
-
if (
|
|
24401
|
-
|
|
24613
|
+
if (templateKind === TemplateKind.NgTemplate) {
|
|
24614
|
+
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
24615
|
}
|
|
24403
|
-
|
|
24404
|
-
|
|
24405
|
-
|
|
24406
|
-
const
|
|
24407
|
-
|
|
24616
|
+
if (templateKind === TemplateKind.Structural &&
|
|
24617
|
+
output.type !== 1 /* e.ParsedEventType.Animation */) {
|
|
24618
|
+
// Animation bindings are excluded from the structural template's const array.
|
|
24619
|
+
const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, output.name, false);
|
|
24620
|
+
unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null, null, null, securityContext));
|
|
24408
24621
|
}
|
|
24409
|
-
listenerOp.handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
|
|
24410
|
-
unit.create.push(listenerOp);
|
|
24411
24622
|
}
|
|
24412
24623
|
// TODO: Perhaps we could do this in a phase? (It likely wouldn't change the slot indices.)
|
|
24413
|
-
if (
|
|
24624
|
+
if (bindings.some(b => b?.i18nMessage) !== null) {
|
|
24414
24625
|
unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
|
|
24415
24626
|
}
|
|
24416
24627
|
}
|
|
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
|
-
|
|
24628
|
+
/**
|
|
24629
|
+
* Helper to ingest an individual binding on a template, either an explicit `ng-template`, or an
|
|
24630
|
+
* implicit template created via structural directive.
|
|
24631
|
+
*
|
|
24632
|
+
* Bindings on templates are *extremely* tricky. I have tried to isolate all of the confusing edge
|
|
24633
|
+
* cases into this function, and to comment it well to document the behavior.
|
|
24634
|
+
*
|
|
24635
|
+
* Some of this behavior is intuitively incorrect, and we should consider changing it in the future.
|
|
24636
|
+
*
|
|
24637
|
+
* @param view The compilation unit for the view containing the template.
|
|
24638
|
+
* @param xref The xref of the template op.
|
|
24639
|
+
* @param type The binding type, according to the parser. This is fairly reasonable, e.g. both
|
|
24640
|
+
* dynamic and static attributes have e.BindingType.Attribute.
|
|
24641
|
+
* @param name The binding's name.
|
|
24642
|
+
* @param value The bindings's value, which will either be an input AST expression, or a string
|
|
24643
|
+
* literal. Note that the input AST expression may or may not be const -- it will only be a
|
|
24644
|
+
* string literal if the parser considered it a text binding.
|
|
24645
|
+
* @param unit If the binding has a unit (e.g. `px` for style bindings), then this is the unit.
|
|
24646
|
+
* @param securityContext The security context of the binding.
|
|
24647
|
+
* @param isStructuralTemplateAttribute Whether this binding actually applies to the structural
|
|
24648
|
+
* ng-template. For example, an `ngFor` would actually apply to the structural template. (Most
|
|
24649
|
+
* bindings on structural elements target the inner element, not the template.)
|
|
24650
|
+
* @param templateKind Whether this is an explicit `ng-template` or an implicit template created by
|
|
24651
|
+
* a structural directive. This should never be a block template.
|
|
24652
|
+
* @param i18nMessage The i18n metadata for the binding, if any.
|
|
24653
|
+
* @param sourceSpan The source span of the binding.
|
|
24654
|
+
* @returns An IR binding op, or null if the binding should be skipped.
|
|
24655
|
+
*/
|
|
24656
|
+
function createTemplateBinding(view, xref, type, name, value, unit, securityContext, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
|
|
24657
|
+
const isTextBinding = typeof value === 'string';
|
|
24658
|
+
// If this is a structural template, then several kinds of bindings should not result in an
|
|
24659
|
+
// update instruction.
|
|
24660
|
+
if (templateKind === TemplateKind.Structural) {
|
|
24661
|
+
if (!isStructuralTemplateAttribute &&
|
|
24662
|
+
(type === 0 /* e.BindingType.Property */ || type === 2 /* e.BindingType.Class */ ||
|
|
24663
|
+
type === 3 /* e.BindingType.Style */)) {
|
|
24664
|
+
// Because this binding doesn't really target the ng-template, it must be a binding on an
|
|
24665
|
+
// inner node of a structural template. We can't skip it entirely, because we still need it on
|
|
24666
|
+
// the ng-template's consts (e.g. for the purposes of directive matching). However, we should
|
|
24667
|
+
// not generate an update instruction for it.
|
|
24668
|
+
return createExtractedAttributeOp(xref, BindingKind.Property, name, null, null, i18nMessage, securityContext);
|
|
24669
|
+
}
|
|
24670
|
+
if (!isTextBinding && (type === 1 /* e.BindingType.Attribute */ || type === 4 /* e.BindingType.Animation */)) {
|
|
24671
|
+
// Again, this binding doesn't really target the ng-template; it actually targets the element
|
|
24672
|
+
// inside the structural template. In the case of non-text attribute or animation bindings,
|
|
24673
|
+
// the binding doesn't even show up on the ng-template const array, so we just skip it
|
|
24674
|
+
// entirely.
|
|
24675
|
+
return null;
|
|
24452
24676
|
}
|
|
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
24677
|
}
|
|
24463
|
-
let
|
|
24464
|
-
|
|
24465
|
-
|
|
24466
|
-
|
|
24467
|
-
|
|
24468
|
-
|
|
24469
|
-
|
|
24678
|
+
let bindingType = BINDING_KINDS.get(type);
|
|
24679
|
+
if (templateKind === TemplateKind.NgTemplate) {
|
|
24680
|
+
// We know we are dealing with bindings directly on an explicit ng-template.
|
|
24681
|
+
// Static attribute bindings should be collected into the const array as k/v pairs. Property
|
|
24682
|
+
// bindings should result in a `property` instruction, and `AttributeMarker.Bindings` const
|
|
24683
|
+
// entries.
|
|
24684
|
+
//
|
|
24685
|
+
// The difficulty is with dynamic attribute, style, and class bindings. These don't really make
|
|
24686
|
+
// sense on an `ng-template` and should probably be parser errors. However,
|
|
24687
|
+
// TemplateDefinitionBuilder generates `property` instructions for them, and so we do that as
|
|
24688
|
+
// well.
|
|
24689
|
+
//
|
|
24690
|
+
// Note that we do have a slight behavior difference with TemplateDefinitionBuilder: although
|
|
24691
|
+
// TDB emits `property` instructions for dynamic attributes, styles, and classes, only styles
|
|
24692
|
+
// and classes also get const collected into the `AttributeMarker.Bindings` field. Dynamic
|
|
24693
|
+
// attribute bindings are missing from the consts entirely. We choose to emit them into the
|
|
24694
|
+
// consts field anyway, to avoid creating special cases for something so arcane and nonsensical.
|
|
24695
|
+
if (type === 2 /* e.BindingType.Class */ || type === 3 /* e.BindingType.Style */ ||
|
|
24696
|
+
(type === 1 /* e.BindingType.Attribute */ && !isTextBinding)) {
|
|
24697
|
+
// TODO: These cases should be parse errors.
|
|
24698
|
+
bindingType = BindingKind.Property;
|
|
24470
24699
|
}
|
|
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
24700
|
}
|
|
24476
|
-
|
|
24477
|
-
|
|
24701
|
+
return createBindingOp(xref, bindingType, name, convertAstWithInterpolation(view.job, value, i18nMessage), unit, securityContext, isTextBinding, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan);
|
|
24702
|
+
}
|
|
24703
|
+
function makeListenerHandlerOps(unit, handler, handlerSpan) {
|
|
24704
|
+
handler = astOf(handler);
|
|
24705
|
+
const handlerOps = new Array();
|
|
24706
|
+
let handlerExprs = handler instanceof Chain ? handler.expressions : [handler];
|
|
24707
|
+
if (handlerExprs.length === 0) {
|
|
24708
|
+
throw new Error('Expected listener to have non-empty expression list.');
|
|
24478
24709
|
}
|
|
24479
|
-
const
|
|
24480
|
-
|
|
24710
|
+
const expressions = handlerExprs.map(expr => convertAst(expr, unit.job, handlerSpan));
|
|
24711
|
+
const returnExpr = expressions.pop();
|
|
24712
|
+
handlerOps.push(...expressions.map(e => createStatementOp(new ExpressionStatement(e, e.sourceSpan))));
|
|
24713
|
+
handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
|
|
24714
|
+
return handlerOps;
|
|
24715
|
+
}
|
|
24716
|
+
function astOf(ast) {
|
|
24717
|
+
return ast instanceof ASTWithSource ? ast.ast : ast;
|
|
24481
24718
|
}
|
|
24482
24719
|
/**
|
|
24483
24720
|
* Process all of the local references on an element-like structure in the template AST and
|
|
@@ -24565,11 +24802,12 @@ function ingestControlFlowInsertionPoint(unit, xref, node) {
|
|
|
24565
24802
|
// and they can be used in directive matching (in the case of `Template.templateAttrs`).
|
|
24566
24803
|
if (root !== null) {
|
|
24567
24804
|
for (const attr of root.attributes) {
|
|
24568
|
-
|
|
24805
|
+
const securityContext = domSchema.securityContext(NG_TEMPLATE_TAG_NAME$1, attr.name, true);
|
|
24806
|
+
unit.update.push(createBindingOp(xref, BindingKind.Attribute, attr.name, literal(attr.value), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
|
|
24569
24807
|
}
|
|
24570
24808
|
const tagName = root instanceof Element$1 ? root.name : root.tagName;
|
|
24571
24809
|
// Don't pass along `ng-template` tag name since it enables directive matching.
|
|
24572
|
-
return tagName ===
|
|
24810
|
+
return tagName === NG_TEMPLATE_TAG_NAME$1 ? null : tagName;
|
|
24573
24811
|
}
|
|
24574
24812
|
return null;
|
|
24575
24813
|
}
|
|
@@ -29996,6 +30234,7 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
|
|
|
29996
30234
|
}
|
|
29997
30235
|
const hostJob = ingestHostBinding({
|
|
29998
30236
|
componentName: name,
|
|
30237
|
+
componentSelector: selector,
|
|
29999
30238
|
properties: bindings,
|
|
30000
30239
|
events: eventBindings,
|
|
30001
30240
|
attributes: hostBindingsMetadata.attributes,
|
|
@@ -31755,7 +31994,7 @@ function publishFacade(global) {
|
|
|
31755
31994
|
* @description
|
|
31756
31995
|
* Entry point for all public APIs of the compiler package.
|
|
31757
31996
|
*/
|
|
31758
|
-
const VERSION = new Version('17.0.
|
|
31997
|
+
const VERSION = new Version('17.0.7');
|
|
31759
31998
|
|
|
31760
31999
|
class CompilerConfig {
|
|
31761
32000
|
constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
|
|
@@ -33321,7 +33560,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
|
|
|
33321
33560
|
function compileDeclareClassMetadata(metadata) {
|
|
33322
33561
|
const definitionMap = new DefinitionMap();
|
|
33323
33562
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
|
|
33324
|
-
definitionMap.set('version', literal('17.0.
|
|
33563
|
+
definitionMap.set('version', literal('17.0.7'));
|
|
33325
33564
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33326
33565
|
definitionMap.set('type', metadata.type);
|
|
33327
33566
|
definitionMap.set('decorators', metadata.decorators);
|
|
@@ -33429,7 +33668,7 @@ function createDirectiveDefinitionMap(meta) {
|
|
|
33429
33668
|
// in 16.1 is actually used.
|
|
33430
33669
|
const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
|
|
33431
33670
|
definitionMap.set('minVersion', literal(minVersion));
|
|
33432
|
-
definitionMap.set('version', literal('17.0.
|
|
33671
|
+
definitionMap.set('version', literal('17.0.7'));
|
|
33433
33672
|
// e.g. `type: MyDirective`
|
|
33434
33673
|
definitionMap.set('type', meta.type.value);
|
|
33435
33674
|
if (meta.isStandalone) {
|
|
@@ -33706,7 +33945,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
|
|
|
33706
33945
|
function compileDeclareFactoryFunction(meta) {
|
|
33707
33946
|
const definitionMap = new DefinitionMap();
|
|
33708
33947
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
|
|
33709
|
-
definitionMap.set('version', literal('17.0.
|
|
33948
|
+
definitionMap.set('version', literal('17.0.7'));
|
|
33710
33949
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33711
33950
|
definitionMap.set('type', meta.type.value);
|
|
33712
33951
|
definitionMap.set('deps', compileDependencies(meta.deps));
|
|
@@ -33741,7 +33980,7 @@ function compileDeclareInjectableFromMetadata(meta) {
|
|
|
33741
33980
|
function createInjectableDefinitionMap(meta) {
|
|
33742
33981
|
const definitionMap = new DefinitionMap();
|
|
33743
33982
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
|
|
33744
|
-
definitionMap.set('version', literal('17.0.
|
|
33983
|
+
definitionMap.set('version', literal('17.0.7'));
|
|
33745
33984
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33746
33985
|
definitionMap.set('type', meta.type.value);
|
|
33747
33986
|
// Only generate providedIn property if it has a non-null value
|
|
@@ -33792,7 +34031,7 @@ function compileDeclareInjectorFromMetadata(meta) {
|
|
|
33792
34031
|
function createInjectorDefinitionMap(meta) {
|
|
33793
34032
|
const definitionMap = new DefinitionMap();
|
|
33794
34033
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
|
|
33795
|
-
definitionMap.set('version', literal('17.0.
|
|
34034
|
+
definitionMap.set('version', literal('17.0.7'));
|
|
33796
34035
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33797
34036
|
definitionMap.set('type', meta.type.value);
|
|
33798
34037
|
definitionMap.set('providers', meta.providers);
|
|
@@ -33825,7 +34064,7 @@ function createNgModuleDefinitionMap(meta) {
|
|
|
33825
34064
|
throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
|
|
33826
34065
|
}
|
|
33827
34066
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
|
|
33828
|
-
definitionMap.set('version', literal('17.0.
|
|
34067
|
+
definitionMap.set('version', literal('17.0.7'));
|
|
33829
34068
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33830
34069
|
definitionMap.set('type', meta.type.value);
|
|
33831
34070
|
// We only generate the keys in the metadata if the arrays contain values.
|
|
@@ -33876,7 +34115,7 @@ function compileDeclarePipeFromMetadata(meta) {
|
|
|
33876
34115
|
function createPipeDefinitionMap(meta) {
|
|
33877
34116
|
const definitionMap = new DefinitionMap();
|
|
33878
34117
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
|
|
33879
|
-
definitionMap.set('version', literal('17.0.
|
|
34118
|
+
definitionMap.set('version', literal('17.0.7'));
|
|
33880
34119
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33881
34120
|
// e.g. `type: MyPipe`
|
|
33882
34121
|
definitionMap.set('type', meta.type.value);
|