@angular/compiler 17.0.5 → 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/compiler.mjs +2 -1
- 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/api.mjs +1 -1
- package/esm2022/src/render3/view/compiler.mjs +21 -8
- package/esm2022/src/render3/view/t2_api.mjs +1 -1
- package/esm2022/src/render3/view/t2_binder.mjs +6 -7
- package/esm2022/src/render3/view/template.mjs +1 -20
- package/esm2022/src/render3/view/util.mjs +24 -2
- package/esm2022/src/template/pipeline/ir/src/enums.mjs +34 -17
- package/esm2022/src/template/pipeline/ir/src/expression.mjs +19 -20
- package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +37 -11
- package/esm2022/src/template/pipeline/ir/src/ops/host.mjs +5 -2
- package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +29 -14
- package/esm2022/src/template/pipeline/src/emit.mjs +13 -9
- package/esm2022/src/template/pipeline/src/ingest.mjs +293 -158
- package/esm2022/src/template/pipeline/src/instruction.mjs +18 -14
- package/esm2022/src/template/pipeline/src/phases/apply_i18n_expressions.mjs +20 -4
- package/esm2022/src/template/pipeline/src/phases/assign_i18n_slot_dependencies.mjs +44 -22
- package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +26 -5
- package/esm2022/src/template/pipeline/src/phases/binding_specialization.mjs +4 -4
- package/esm2022/src/template/pipeline/src/phases/const_collection.mjs +15 -6
- package/esm2022/src/template/pipeline/src/phases/convert_i18n_bindings.mjs +52 -0
- package/esm2022/src/template/pipeline/src/phases/create_i18n_contexts.mjs +23 -1
- package/esm2022/src/template/pipeline/src/phases/extract_i18n_messages.mjs +65 -88
- package/esm2022/src/template/pipeline/src/phases/generate_advance.mjs +2 -2
- package/esm2022/src/template/pipeline/src/phases/generate_variables.mjs +7 -1
- package/esm2022/src/template/pipeline/src/phases/i18n_const_collection.mjs +147 -35
- package/esm2022/src/template/pipeline/src/phases/i18n_text_extraction.mjs +5 -3
- package/esm2022/src/template/pipeline/src/phases/parse_extracted_styles.mjs +4 -3
- package/esm2022/src/template/pipeline/src/phases/phase_remove_content_selectors.mjs +15 -3
- package/esm2022/src/template/pipeline/src/phases/propagate_i18n_blocks.mjs +31 -12
- package/esm2022/src/template/pipeline/src/phases/reify.mjs +18 -15
- package/esm2022/src/template/pipeline/src/phases/remove_unused_i18n_attrs.mjs +33 -0
- package/esm2022/src/template/pipeline/src/phases/resolve_i18n_element_placeholders.mjs +175 -31
- package/esm2022/src/template/pipeline/src/phases/resolve_i18n_expression_placeholders.mjs +10 -5
- 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 +1266 -549
- package/fesm2022/compiler.mjs.map +1 -1
- package/index.d.ts +21 -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
|
*/
|
|
@@ -3679,6 +3679,41 @@ function getInjectFn(target) {
|
|
|
3679
3679
|
}
|
|
3680
3680
|
}
|
|
3681
3681
|
|
|
3682
|
+
var TagContentType;
|
|
3683
|
+
(function (TagContentType) {
|
|
3684
|
+
TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT";
|
|
3685
|
+
TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
|
|
3686
|
+
TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
|
|
3687
|
+
})(TagContentType || (TagContentType = {}));
|
|
3688
|
+
function splitNsName(elementName) {
|
|
3689
|
+
if (elementName[0] != ':') {
|
|
3690
|
+
return [null, elementName];
|
|
3691
|
+
}
|
|
3692
|
+
const colonIndex = elementName.indexOf(':', 1);
|
|
3693
|
+
if (colonIndex === -1) {
|
|
3694
|
+
throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
|
|
3695
|
+
}
|
|
3696
|
+
return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
|
|
3697
|
+
}
|
|
3698
|
+
// `<ng-container>` tags work the same regardless the namespace
|
|
3699
|
+
function isNgContainer(tagName) {
|
|
3700
|
+
return splitNsName(tagName)[1] === 'ng-container';
|
|
3701
|
+
}
|
|
3702
|
+
// `<ng-content>` tags work the same regardless the namespace
|
|
3703
|
+
function isNgContent(tagName) {
|
|
3704
|
+
return splitNsName(tagName)[1] === 'ng-content';
|
|
3705
|
+
}
|
|
3706
|
+
// `<ng-template>` tags work the same regardless the namespace
|
|
3707
|
+
function isNgTemplate(tagName) {
|
|
3708
|
+
return splitNsName(tagName)[1] === 'ng-template';
|
|
3709
|
+
}
|
|
3710
|
+
function getNsPrefix(fullName) {
|
|
3711
|
+
return fullName === null ? null : splitNsName(fullName)[0];
|
|
3712
|
+
}
|
|
3713
|
+
function mergeNsAndName(prefix, localName) {
|
|
3714
|
+
return prefix ? `:${prefix}:${localName}` : localName;
|
|
3715
|
+
}
|
|
3716
|
+
|
|
3682
3717
|
/**
|
|
3683
3718
|
* This is an R3 `Node`-like wrapper for a raw `html.Comment` node. We do not currently
|
|
3684
3719
|
* require the implementation of a visitor for Comments as they are only collected at
|
|
@@ -4677,7 +4712,7 @@ const I18N_ATTR_PREFIX = 'i18n-';
|
|
|
4677
4712
|
/** Prefix of var expressions used in ICUs */
|
|
4678
4713
|
const I18N_ICU_VAR_PREFIX = 'VAR_';
|
|
4679
4714
|
/** Prefix of ICU expressions for post processing */
|
|
4680
|
-
const I18N_ICU_MAPPING_PREFIX = 'I18N_EXP_';
|
|
4715
|
+
const I18N_ICU_MAPPING_PREFIX$1 = 'I18N_EXP_';
|
|
4681
4716
|
/** Placeholder wrapper for i18n expressions **/
|
|
4682
4717
|
const I18N_PLACEHOLDER_SYMBOL = '�';
|
|
4683
4718
|
function isI18nAttribute(name) {
|
|
@@ -5019,6 +5054,26 @@ class DefinitionMap {
|
|
|
5019
5054
|
return literalMap(this.values);
|
|
5020
5055
|
}
|
|
5021
5056
|
}
|
|
5057
|
+
/**
|
|
5058
|
+
* Creates a `CssSelector` from an AST node.
|
|
5059
|
+
*/
|
|
5060
|
+
function createCssSelectorFromNode(node) {
|
|
5061
|
+
const elementName = node instanceof Element$1 ? node.name : 'ng-template';
|
|
5062
|
+
const attributes = getAttrsForDirectiveMatching(node);
|
|
5063
|
+
const cssSelector = new CssSelector();
|
|
5064
|
+
const elementNameNoNs = splitNsName(elementName)[1];
|
|
5065
|
+
cssSelector.setElement(elementNameNoNs);
|
|
5066
|
+
Object.getOwnPropertyNames(attributes).forEach((name) => {
|
|
5067
|
+
const nameNoNs = splitNsName(name)[1];
|
|
5068
|
+
const value = attributes[name];
|
|
5069
|
+
cssSelector.addAttribute(nameNoNs, value);
|
|
5070
|
+
if (name.toLowerCase() === 'class') {
|
|
5071
|
+
const classes = value.trim().split(/\s+/);
|
|
5072
|
+
classes.forEach(className => cssSelector.addClassName(className));
|
|
5073
|
+
}
|
|
5074
|
+
});
|
|
5075
|
+
return cssSelector;
|
|
5076
|
+
}
|
|
5022
5077
|
/**
|
|
5023
5078
|
* Extract a map of properties to values for a given element or template node, which can be used
|
|
5024
5079
|
* by the directive matching machinery.
|
|
@@ -8915,6 +8970,10 @@ var OpKind;
|
|
|
8915
8970
|
* An i18n context containing information needed to generate an i18n message.
|
|
8916
8971
|
*/
|
|
8917
8972
|
OpKind[OpKind["I18nContext"] = 43] = "I18nContext";
|
|
8973
|
+
/**
|
|
8974
|
+
* A creation op that corresponds to i18n attributes on an element.
|
|
8975
|
+
*/
|
|
8976
|
+
OpKind[OpKind["I18nAttributes"] = 44] = "I18nAttributes";
|
|
8918
8977
|
})(OpKind || (OpKind = {}));
|
|
8919
8978
|
/**
|
|
8920
8979
|
* Distinguishes different kinds of IR expressions.
|
|
@@ -9005,23 +9064,27 @@ var ExpressionKind;
|
|
|
9005
9064
|
* An expression representing a sanitizer function.
|
|
9006
9065
|
*/
|
|
9007
9066
|
ExpressionKind[ExpressionKind["SanitizerExpr"] = 20] = "SanitizerExpr";
|
|
9067
|
+
/**
|
|
9068
|
+
* An expression representing a function to create trusted values.
|
|
9069
|
+
*/
|
|
9070
|
+
ExpressionKind[ExpressionKind["TrustedValueFnExpr"] = 21] = "TrustedValueFnExpr";
|
|
9008
9071
|
/**
|
|
9009
9072
|
* An expression that will cause a literal slot index to be emitted.
|
|
9010
9073
|
*/
|
|
9011
|
-
ExpressionKind[ExpressionKind["SlotLiteralExpr"] =
|
|
9074
|
+
ExpressionKind[ExpressionKind["SlotLiteralExpr"] = 22] = "SlotLiteralExpr";
|
|
9012
9075
|
/**
|
|
9013
9076
|
* A test expression for a conditional op.
|
|
9014
9077
|
*/
|
|
9015
|
-
ExpressionKind[ExpressionKind["ConditionalCase"] =
|
|
9078
|
+
ExpressionKind[ExpressionKind["ConditionalCase"] = 23] = "ConditionalCase";
|
|
9016
9079
|
/**
|
|
9017
9080
|
* A variable for use inside a repeater, providing one of the ambiently-available context
|
|
9018
9081
|
* properties ($even, $first, etc.).
|
|
9019
9082
|
*/
|
|
9020
|
-
ExpressionKind[ExpressionKind["DerivedRepeaterVar"] =
|
|
9083
|
+
ExpressionKind[ExpressionKind["DerivedRepeaterVar"] = 24] = "DerivedRepeaterVar";
|
|
9021
9084
|
/**
|
|
9022
9085
|
* An expression that will be automatically extracted to the component const array.
|
|
9023
9086
|
*/
|
|
9024
|
-
ExpressionKind[ExpressionKind["ConstCollected"] =
|
|
9087
|
+
ExpressionKind[ExpressionKind["ConstCollected"] = 25] = "ConstCollected";
|
|
9025
9088
|
})(ExpressionKind || (ExpressionKind = {}));
|
|
9026
9089
|
var VariableFlags;
|
|
9027
9090
|
(function (VariableFlags) {
|
|
@@ -9065,18 +9128,6 @@ var CompatibilityMode;
|
|
|
9065
9128
|
CompatibilityMode[CompatibilityMode["Normal"] = 0] = "Normal";
|
|
9066
9129
|
CompatibilityMode[CompatibilityMode["TemplateDefinitionBuilder"] = 1] = "TemplateDefinitionBuilder";
|
|
9067
9130
|
})(CompatibilityMode || (CompatibilityMode = {}));
|
|
9068
|
-
/**
|
|
9069
|
-
* Represents functions used to sanitize different pieces of a template.
|
|
9070
|
-
*/
|
|
9071
|
-
var SanitizerFn;
|
|
9072
|
-
(function (SanitizerFn) {
|
|
9073
|
-
SanitizerFn[SanitizerFn["Html"] = 0] = "Html";
|
|
9074
|
-
SanitizerFn[SanitizerFn["Script"] = 1] = "Script";
|
|
9075
|
-
SanitizerFn[SanitizerFn["Style"] = 2] = "Style";
|
|
9076
|
-
SanitizerFn[SanitizerFn["Url"] = 3] = "Url";
|
|
9077
|
-
SanitizerFn[SanitizerFn["ResourceUrl"] = 4] = "ResourceUrl";
|
|
9078
|
-
SanitizerFn[SanitizerFn["IframeAttribute"] = 5] = "IframeAttribute";
|
|
9079
|
-
})(SanitizerFn || (SanitizerFn = {}));
|
|
9080
9131
|
/**
|
|
9081
9132
|
* Enumeration of the different kinds of `@defer` secondary blocks.
|
|
9082
9133
|
*/
|
|
@@ -9136,6 +9187,20 @@ var I18nParamResolutionTime;
|
|
|
9136
9187
|
*/
|
|
9137
9188
|
I18nParamResolutionTime[I18nParamResolutionTime["Postproccessing"] = 1] = "Postproccessing";
|
|
9138
9189
|
})(I18nParamResolutionTime || (I18nParamResolutionTime = {}));
|
|
9190
|
+
/**
|
|
9191
|
+
* The contexts in which an i18n expression can be used.
|
|
9192
|
+
*/
|
|
9193
|
+
var I18nExpressionFor;
|
|
9194
|
+
(function (I18nExpressionFor) {
|
|
9195
|
+
/**
|
|
9196
|
+
* This expression is used as a value (i.e. inside an i18n block).
|
|
9197
|
+
*/
|
|
9198
|
+
I18nExpressionFor[I18nExpressionFor["I18nText"] = 0] = "I18nText";
|
|
9199
|
+
/**
|
|
9200
|
+
* This expression is used in a binding.
|
|
9201
|
+
*/
|
|
9202
|
+
I18nExpressionFor[I18nExpressionFor["I18nAttribute"] = 1] = "I18nAttribute";
|
|
9203
|
+
})(I18nExpressionFor || (I18nExpressionFor = {}));
|
|
9139
9204
|
/**
|
|
9140
9205
|
* Flags that describe what an i18n param value. These determine how the value is serialized into
|
|
9141
9206
|
* the final map.
|
|
@@ -9202,7 +9267,14 @@ var I18nContextKind;
|
|
|
9202
9267
|
(function (I18nContextKind) {
|
|
9203
9268
|
I18nContextKind[I18nContextKind["RootI18n"] = 0] = "RootI18n";
|
|
9204
9269
|
I18nContextKind[I18nContextKind["Icu"] = 1] = "Icu";
|
|
9270
|
+
I18nContextKind[I18nContextKind["Attr"] = 2] = "Attr";
|
|
9205
9271
|
})(I18nContextKind || (I18nContextKind = {}));
|
|
9272
|
+
var TemplateKind;
|
|
9273
|
+
(function (TemplateKind) {
|
|
9274
|
+
TemplateKind[TemplateKind["NgTemplate"] = 0] = "NgTemplate";
|
|
9275
|
+
TemplateKind[TemplateKind["Structural"] = 1] = "Structural";
|
|
9276
|
+
TemplateKind[TemplateKind["Block"] = 2] = "Block";
|
|
9277
|
+
})(TemplateKind || (TemplateKind = {}));
|
|
9206
9278
|
|
|
9207
9279
|
/**
|
|
9208
9280
|
* Marker symbol for `ConsumesSlotOpTrait`.
|
|
@@ -9310,12 +9382,11 @@ const NEW_OP = {
|
|
|
9310
9382
|
/**
|
|
9311
9383
|
* Create an `InterpolationTextOp`.
|
|
9312
9384
|
*/
|
|
9313
|
-
function createInterpolateTextOp(xref, interpolation,
|
|
9385
|
+
function createInterpolateTextOp(xref, interpolation, sourceSpan) {
|
|
9314
9386
|
return {
|
|
9315
9387
|
kind: OpKind.InterpolateText,
|
|
9316
9388
|
target: xref,
|
|
9317
9389
|
interpolation,
|
|
9318
|
-
i18nPlaceholders,
|
|
9319
9390
|
sourceSpan,
|
|
9320
9391
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9321
9392
|
...TRAIT_CONSUMES_VARS,
|
|
@@ -9323,15 +9394,19 @@ function createInterpolateTextOp(xref, interpolation, i18nPlaceholders, sourceSp
|
|
|
9323
9394
|
};
|
|
9324
9395
|
}
|
|
9325
9396
|
class Interpolation {
|
|
9326
|
-
constructor(strings, expressions) {
|
|
9397
|
+
constructor(strings, expressions, i18nPlaceholders) {
|
|
9327
9398
|
this.strings = strings;
|
|
9328
9399
|
this.expressions = expressions;
|
|
9400
|
+
this.i18nPlaceholders = i18nPlaceholders;
|
|
9401
|
+
if (i18nPlaceholders.length !== 0 && i18nPlaceholders.length !== expressions.length) {
|
|
9402
|
+
throw new Error(`Expected ${expressions.length} placeholders to match interpolation expression count, but got ${i18nPlaceholders.length}`);
|
|
9403
|
+
}
|
|
9329
9404
|
}
|
|
9330
9405
|
}
|
|
9331
9406
|
/**
|
|
9332
9407
|
* Create a `BindingOp`, not yet transformed into a particular type of binding.
|
|
9333
9408
|
*/
|
|
9334
|
-
function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute,
|
|
9409
|
+
function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
|
|
9335
9410
|
return {
|
|
9336
9411
|
kind: OpKind.Binding,
|
|
9337
9412
|
bindingKind: kind,
|
|
@@ -9341,7 +9416,10 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
|
|
|
9341
9416
|
unit,
|
|
9342
9417
|
securityContext,
|
|
9343
9418
|
isTextAttribute,
|
|
9344
|
-
|
|
9419
|
+
isStructuralTemplateAttribute,
|
|
9420
|
+
templateKind,
|
|
9421
|
+
i18nContext: null,
|
|
9422
|
+
i18nMessage,
|
|
9345
9423
|
sourceSpan,
|
|
9346
9424
|
...NEW_OP,
|
|
9347
9425
|
};
|
|
@@ -9349,7 +9427,7 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
|
|
|
9349
9427
|
/**
|
|
9350
9428
|
* Create a `PropertyOp`.
|
|
9351
9429
|
*/
|
|
9352
|
-
function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext,
|
|
9430
|
+
function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext, isStructuralTemplateAttribute, templateKind, i18nContext, i18nMessage, sourceSpan) {
|
|
9353
9431
|
return {
|
|
9354
9432
|
kind: OpKind.Property,
|
|
9355
9433
|
target,
|
|
@@ -9358,7 +9436,10 @@ function createPropertyOp(target, name, expression, isAnimationTrigger, security
|
|
|
9358
9436
|
isAnimationTrigger,
|
|
9359
9437
|
securityContext,
|
|
9360
9438
|
sanitizer: null,
|
|
9361
|
-
|
|
9439
|
+
isStructuralTemplateAttribute,
|
|
9440
|
+
templateKind,
|
|
9441
|
+
i18nContext,
|
|
9442
|
+
i18nMessage,
|
|
9362
9443
|
sourceSpan,
|
|
9363
9444
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9364
9445
|
...TRAIT_CONSUMES_VARS,
|
|
@@ -9423,7 +9504,7 @@ function createClassMapOp(xref, expression, sourceSpan) {
|
|
|
9423
9504
|
/**
|
|
9424
9505
|
* Create an `AttributeOp`.
|
|
9425
9506
|
*/
|
|
9426
|
-
function createAttributeOp(target, name, expression, securityContext, isTextAttribute,
|
|
9507
|
+
function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isStructuralTemplateAttribute, templateKind, i18nMessage, sourceSpan) {
|
|
9427
9508
|
return {
|
|
9428
9509
|
kind: OpKind.Attribute,
|
|
9429
9510
|
target,
|
|
@@ -9432,7 +9513,10 @@ function createAttributeOp(target, name, expression, securityContext, isTextAttr
|
|
|
9432
9513
|
securityContext,
|
|
9433
9514
|
sanitizer: null,
|
|
9434
9515
|
isTextAttribute,
|
|
9435
|
-
|
|
9516
|
+
isStructuralTemplateAttribute,
|
|
9517
|
+
templateKind,
|
|
9518
|
+
i18nContext: null,
|
|
9519
|
+
i18nMessage,
|
|
9436
9520
|
sourceSpan,
|
|
9437
9521
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9438
9522
|
...TRAIT_CONSUMES_VARS,
|
|
@@ -9493,15 +9577,18 @@ function createDeferWhenOp(target, expr, prefetch, sourceSpan) {
|
|
|
9493
9577
|
/**
|
|
9494
9578
|
* Create an i18n expression op.
|
|
9495
9579
|
*/
|
|
9496
|
-
function createI18nExpressionOp(context, target, handle, expression, i18nPlaceholder, resolutionTime, sourceSpan) {
|
|
9580
|
+
function createI18nExpressionOp(context, target, i18nOwner, handle, expression, i18nPlaceholder, resolutionTime, usage, name, sourceSpan) {
|
|
9497
9581
|
return {
|
|
9498
9582
|
kind: OpKind.I18nExpression,
|
|
9499
9583
|
context,
|
|
9500
9584
|
target,
|
|
9585
|
+
i18nOwner,
|
|
9501
9586
|
handle,
|
|
9502
9587
|
expression,
|
|
9503
9588
|
i18nPlaceholder,
|
|
9504
9589
|
resolutionTime,
|
|
9590
|
+
usage,
|
|
9591
|
+
name,
|
|
9505
9592
|
sourceSpan,
|
|
9506
9593
|
...NEW_OP,
|
|
9507
9594
|
...TRAIT_CONSUMES_VARS,
|
|
@@ -9509,12 +9596,12 @@ function createI18nExpressionOp(context, target, handle, expression, i18nPlaceho
|
|
|
9509
9596
|
};
|
|
9510
9597
|
}
|
|
9511
9598
|
/**
|
|
9512
|
-
*Creates an op to apply i18n expression ops
|
|
9599
|
+
* Creates an op to apply i18n expression ops.
|
|
9513
9600
|
*/
|
|
9514
|
-
function createI18nApplyOp(
|
|
9601
|
+
function createI18nApplyOp(owner, handle, sourceSpan) {
|
|
9515
9602
|
return {
|
|
9516
9603
|
kind: OpKind.I18nApply,
|
|
9517
|
-
|
|
9604
|
+
owner,
|
|
9518
9605
|
handle,
|
|
9519
9606
|
sourceSpan,
|
|
9520
9607
|
...NEW_OP,
|
|
@@ -10063,24 +10150,6 @@ class ReadTemporaryExpr extends ExpressionBase {
|
|
|
10063
10150
|
return r;
|
|
10064
10151
|
}
|
|
10065
10152
|
}
|
|
10066
|
-
class SanitizerExpr extends ExpressionBase {
|
|
10067
|
-
constructor(fn) {
|
|
10068
|
-
super();
|
|
10069
|
-
this.fn = fn;
|
|
10070
|
-
this.kind = ExpressionKind.SanitizerExpr;
|
|
10071
|
-
}
|
|
10072
|
-
visitExpression(visitor, context) { }
|
|
10073
|
-
isEquivalent(e) {
|
|
10074
|
-
return e instanceof SanitizerExpr && e.fn === this.fn;
|
|
10075
|
-
}
|
|
10076
|
-
isConstant() {
|
|
10077
|
-
return true;
|
|
10078
|
-
}
|
|
10079
|
-
clone() {
|
|
10080
|
-
return new SanitizerExpr(this.fn);
|
|
10081
|
-
}
|
|
10082
|
-
transformInternalExpressions() { }
|
|
10083
|
-
}
|
|
10084
10153
|
class SlotLiteralExpr extends ExpressionBase {
|
|
10085
10154
|
constructor(slot) {
|
|
10086
10155
|
super();
|
|
@@ -10211,7 +10280,6 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10211
10280
|
case OpKind.ClassProp:
|
|
10212
10281
|
case OpKind.ClassMap:
|
|
10213
10282
|
case OpKind.Binding:
|
|
10214
|
-
case OpKind.HostProperty:
|
|
10215
10283
|
if (op.expression instanceof Interpolation) {
|
|
10216
10284
|
transformExpressionsInInterpolation(op.expression, transform, flags);
|
|
10217
10285
|
}
|
|
@@ -10220,6 +10288,7 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10220
10288
|
}
|
|
10221
10289
|
break;
|
|
10222
10290
|
case OpKind.Property:
|
|
10291
|
+
case OpKind.HostProperty:
|
|
10223
10292
|
case OpKind.Attribute:
|
|
10224
10293
|
if (op.expression instanceof Interpolation) {
|
|
10225
10294
|
transformExpressionsInInterpolation(op.expression, transform, flags);
|
|
@@ -10265,6 +10334,8 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10265
10334
|
case OpKind.ExtractedAttribute:
|
|
10266
10335
|
op.expression =
|
|
10267
10336
|
op.expression && transformExpressionsInExpression(op.expression, transform, flags);
|
|
10337
|
+
op.trustedValueFn = op.trustedValueFn &&
|
|
10338
|
+
transformExpressionsInExpression(op.trustedValueFn, transform, flags);
|
|
10268
10339
|
break;
|
|
10269
10340
|
case OpKind.RepeaterCreate:
|
|
10270
10341
|
op.track = transformExpressionsInExpression(op.track, transform, flags);
|
|
@@ -10318,6 +10389,7 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10318
10389
|
case OpKind.ProjectionDef:
|
|
10319
10390
|
case OpKind.Template:
|
|
10320
10391
|
case OpKind.Text:
|
|
10392
|
+
case OpKind.I18nAttributes:
|
|
10321
10393
|
// These operations contain no expressions.
|
|
10322
10394
|
break;
|
|
10323
10395
|
default:
|
|
@@ -10338,6 +10410,9 @@ function transformExpressionsInExpression(expr, transform, flags) {
|
|
|
10338
10410
|
expr.lhs = transformExpressionsInExpression(expr.lhs, transform, flags);
|
|
10339
10411
|
expr.rhs = transformExpressionsInExpression(expr.rhs, transform, flags);
|
|
10340
10412
|
}
|
|
10413
|
+
else if (expr instanceof UnaryOperatorExpr) {
|
|
10414
|
+
expr.expr = transformExpressionsInExpression(expr.expr, transform, flags);
|
|
10415
|
+
}
|
|
10341
10416
|
else if (expr instanceof ReadPropExpr) {
|
|
10342
10417
|
expr.receiver = transformExpressionsInExpression(expr.receiver, transform, flags);
|
|
10343
10418
|
}
|
|
@@ -10389,6 +10464,17 @@ function transformExpressionsInExpression(expr, transform, flags) {
|
|
|
10389
10464
|
expr.expressions[i] = transformExpressionsInExpression(expr.expressions[i], transform, flags);
|
|
10390
10465
|
}
|
|
10391
10466
|
}
|
|
10467
|
+
else if (expr instanceof NotExpr) {
|
|
10468
|
+
expr.condition = transformExpressionsInExpression(expr.condition, transform, flags);
|
|
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
|
+
}
|
|
10392
10478
|
else if (expr instanceof ReadVarExpr || expr instanceof ExternalExpr ||
|
|
10393
10479
|
expr instanceof LiteralExpr) {
|
|
10394
10480
|
// No action for these types.
|
|
@@ -10730,10 +10816,11 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
|
|
|
10730
10816
|
/**
|
|
10731
10817
|
* Create a `TemplateOp`.
|
|
10732
10818
|
*/
|
|
10733
|
-
function createTemplateOp(xref, tag, functionNameSuffix, namespace, i18nPlaceholder, sourceSpan) {
|
|
10819
|
+
function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, sourceSpan) {
|
|
10734
10820
|
return {
|
|
10735
10821
|
kind: OpKind.Template,
|
|
10736
10822
|
xref,
|
|
10823
|
+
templateKind,
|
|
10737
10824
|
attributes: null,
|
|
10738
10825
|
tag,
|
|
10739
10826
|
handle: new SlotHandle(),
|
|
@@ -10749,7 +10836,7 @@ function createTemplateOp(xref, tag, functionNameSuffix, namespace, i18nPlacehol
|
|
|
10749
10836
|
...NEW_OP,
|
|
10750
10837
|
};
|
|
10751
10838
|
}
|
|
10752
|
-
function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, sourceSpan) {
|
|
10839
|
+
function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, i18nPlaceholder, emptyI18nPlaceholder, sourceSpan) {
|
|
10753
10840
|
return {
|
|
10754
10841
|
kind: OpKind.RepeaterCreate,
|
|
10755
10842
|
attributes: null,
|
|
@@ -10767,6 +10854,8 @@ function createRepeaterCreateOp(primaryView, emptyView, tag, track, varNames, so
|
|
|
10767
10854
|
vars: null,
|
|
10768
10855
|
varNames,
|
|
10769
10856
|
usesComponentInstance: false,
|
|
10857
|
+
i18nPlaceholder,
|
|
10858
|
+
emptyI18nPlaceholder,
|
|
10770
10859
|
sourceSpan,
|
|
10771
10860
|
...TRAIT_CONSUMES_SLOT,
|
|
10772
10861
|
...NEW_OP,
|
|
@@ -10815,7 +10904,9 @@ function createTextOp(xref, initialValue, sourceSpan) {
|
|
|
10815
10904
|
/**
|
|
10816
10905
|
* Create a `ListenerOp`. Host bindings reuse all the listener logic.
|
|
10817
10906
|
*/
|
|
10818
|
-
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);
|
|
10819
10910
|
return {
|
|
10820
10911
|
kind: OpKind.Listener,
|
|
10821
10912
|
target,
|
|
@@ -10823,11 +10914,12 @@ function createListenerOp(target, targetSlot, name, tag, animationPhase, hostLis
|
|
|
10823
10914
|
tag,
|
|
10824
10915
|
hostListener,
|
|
10825
10916
|
name,
|
|
10826
|
-
handlerOps:
|
|
10917
|
+
handlerOps: handlerList,
|
|
10827
10918
|
handlerFnName: null,
|
|
10828
10919
|
consumesDollarEvent: false,
|
|
10829
10920
|
isAnimationListener: animationPhase !== null,
|
|
10830
|
-
animationPhase
|
|
10921
|
+
animationPhase,
|
|
10922
|
+
eventTarget,
|
|
10831
10923
|
sourceSpan,
|
|
10832
10924
|
...NEW_OP,
|
|
10833
10925
|
};
|
|
@@ -10856,14 +10948,15 @@ function createProjectionDefOp(def) {
|
|
|
10856
10948
|
...NEW_OP,
|
|
10857
10949
|
};
|
|
10858
10950
|
}
|
|
10859
|
-
function createProjectionOp(xref, selector, sourceSpan) {
|
|
10951
|
+
function createProjectionOp(xref, selector, i18nPlaceholder, attributes, sourceSpan) {
|
|
10860
10952
|
return {
|
|
10861
10953
|
kind: OpKind.Projection,
|
|
10862
10954
|
xref,
|
|
10863
10955
|
handle: new SlotHandle(),
|
|
10864
10956
|
selector,
|
|
10957
|
+
i18nPlaceholder,
|
|
10865
10958
|
projectionSlotIndex: 0,
|
|
10866
|
-
attributes
|
|
10959
|
+
attributes,
|
|
10867
10960
|
localRefs: [],
|
|
10868
10961
|
sourceSpan,
|
|
10869
10962
|
...NEW_OP,
|
|
@@ -10873,13 +10966,17 @@ function createProjectionOp(xref, selector, sourceSpan) {
|
|
|
10873
10966
|
/**
|
|
10874
10967
|
* Create an `ExtractedAttributeOp`.
|
|
10875
10968
|
*/
|
|
10876
|
-
function createExtractedAttributeOp(target, bindingKind, name, expression) {
|
|
10969
|
+
function createExtractedAttributeOp(target, bindingKind, name, expression, i18nContext, i18nMessage, securityContext) {
|
|
10877
10970
|
return {
|
|
10878
10971
|
kind: OpKind.ExtractedAttribute,
|
|
10879
10972
|
target,
|
|
10880
10973
|
bindingKind,
|
|
10881
10974
|
name,
|
|
10882
10975
|
expression,
|
|
10976
|
+
i18nContext,
|
|
10977
|
+
i18nMessage,
|
|
10978
|
+
securityContext,
|
|
10979
|
+
trustedValueFn: null,
|
|
10883
10980
|
...NEW_OP,
|
|
10884
10981
|
};
|
|
10885
10982
|
}
|
|
@@ -10922,10 +11019,11 @@ function createDeferOnOp(defer, trigger, prefetch, sourceSpan) {
|
|
|
10922
11019
|
/**
|
|
10923
11020
|
* Create an `ExtractedMessageOp`.
|
|
10924
11021
|
*/
|
|
10925
|
-
function createI18nMessageOp(xref, i18nBlock, message, messagePlaceholder, params, postprocessingParams, needsPostprocessing) {
|
|
11022
|
+
function createI18nMessageOp(xref, i18nContext, i18nBlock, message, messagePlaceholder, params, postprocessingParams, needsPostprocessing) {
|
|
10926
11023
|
return {
|
|
10927
11024
|
kind: OpKind.I18nMessage,
|
|
10928
11025
|
xref,
|
|
11026
|
+
i18nContext,
|
|
10929
11027
|
i18nBlock,
|
|
10930
11028
|
message,
|
|
10931
11029
|
messagePlaceholder,
|
|
@@ -10988,6 +11086,9 @@ function createIcuEndOp(xref) {
|
|
|
10988
11086
|
};
|
|
10989
11087
|
}
|
|
10990
11088
|
function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan) {
|
|
11089
|
+
if (i18nBlock === null && contextKind !== I18nContextKind.Attr) {
|
|
11090
|
+
throw new Error('AssertionError: i18nBlock must be provided for non-attribute contexts.');
|
|
11091
|
+
}
|
|
10991
11092
|
return {
|
|
10992
11093
|
kind: OpKind.I18nContext,
|
|
10993
11094
|
contextKind,
|
|
@@ -11000,6 +11101,17 @@ function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan)
|
|
|
11000
11101
|
...NEW_OP,
|
|
11001
11102
|
};
|
|
11002
11103
|
}
|
|
11104
|
+
function createI18nAttributesOp(xref, handle, target) {
|
|
11105
|
+
return {
|
|
11106
|
+
kind: OpKind.I18nAttributes,
|
|
11107
|
+
xref,
|
|
11108
|
+
handle,
|
|
11109
|
+
target,
|
|
11110
|
+
i18nAttributesConfig: null,
|
|
11111
|
+
...NEW_OP,
|
|
11112
|
+
...TRAIT_CONSUMES_SLOT,
|
|
11113
|
+
};
|
|
11114
|
+
}
|
|
11003
11115
|
function literalOrArrayLiteral$1(value) {
|
|
11004
11116
|
if (Array.isArray(value)) {
|
|
11005
11117
|
return literalArr(value.map(literalOrArrayLiteral$1));
|
|
@@ -11007,12 +11119,15 @@ function literalOrArrayLiteral$1(value) {
|
|
|
11007
11119
|
return literal(value, INFERRED_TYPE);
|
|
11008
11120
|
}
|
|
11009
11121
|
|
|
11010
|
-
function createHostPropertyOp(name, expression, isAnimationTrigger, sourceSpan) {
|
|
11122
|
+
function createHostPropertyOp(name, expression, isAnimationTrigger, i18nContext, securityContext, sourceSpan) {
|
|
11011
11123
|
return {
|
|
11012
11124
|
kind: OpKind.HostProperty,
|
|
11013
11125
|
name,
|
|
11014
11126
|
expression,
|
|
11015
11127
|
isAnimationTrigger,
|
|
11128
|
+
i18nContext,
|
|
11129
|
+
securityContext,
|
|
11130
|
+
sanitizer: null,
|
|
11016
11131
|
sourceSpan,
|
|
11017
11132
|
...TRAIT_CONSUMES_VARS,
|
|
11018
11133
|
...NEW_OP,
|
|
@@ -11249,7 +11364,7 @@ function applyI18nExpressions(job) {
|
|
|
11249
11364
|
// Only add apply after expressions that are not followed by more expressions.
|
|
11250
11365
|
if (op.kind === OpKind.I18nExpression && needsApplication(i18nContexts, op)) {
|
|
11251
11366
|
// TODO: what should be the source span for the apply op?
|
|
11252
|
-
OpList.insertAfter(createI18nApplyOp(op.
|
|
11367
|
+
OpList.insertAfter(createI18nApplyOp(op.i18nOwner, op.handle, null), op);
|
|
11253
11368
|
}
|
|
11254
11369
|
}
|
|
11255
11370
|
}
|
|
@@ -11262,42 +11377,80 @@ function needsApplication(i18nContexts, op) {
|
|
|
11262
11377
|
if (op.next?.kind !== OpKind.I18nExpression) {
|
|
11263
11378
|
return true;
|
|
11264
11379
|
}
|
|
11265
|
-
// If the next op is an expression targeting a different i18n block, we need to apply.
|
|
11266
11380
|
const context = i18nContexts.get(op.context);
|
|
11267
11381
|
const nextContext = i18nContexts.get(op.next.context);
|
|
11268
|
-
if (context
|
|
11382
|
+
if (context === undefined) {
|
|
11383
|
+
throw new Error('AssertionError: expected an I18nContextOp to exist for the I18nExpressionOp\'s context');
|
|
11384
|
+
}
|
|
11385
|
+
if (nextContext === undefined) {
|
|
11386
|
+
throw new Error('AssertionError: expected an I18nContextOp to exist for the next I18nExpressionOp\'s context');
|
|
11387
|
+
}
|
|
11388
|
+
// If the next op is an expression targeting a different i18n block (or different element, in the
|
|
11389
|
+
// case of i18n attributes), we need to apply.
|
|
11390
|
+
// First, handle the case of i18n blocks.
|
|
11391
|
+
if (context.i18nBlock !== null) {
|
|
11392
|
+
// This is a block context. Compare the blocks.
|
|
11393
|
+
if (context.i18nBlock !== nextContext.i18nBlock) {
|
|
11394
|
+
return true;
|
|
11395
|
+
}
|
|
11396
|
+
return false;
|
|
11397
|
+
}
|
|
11398
|
+
// Second, handle the case of i18n attributes.
|
|
11399
|
+
if (op.i18nOwner !== op.next.i18nOwner) {
|
|
11269
11400
|
return true;
|
|
11270
11401
|
}
|
|
11271
11402
|
return false;
|
|
11272
11403
|
}
|
|
11273
11404
|
|
|
11274
11405
|
/**
|
|
11275
|
-
* Updates i18n expression ops to
|
|
11406
|
+
* Updates i18n expression ops to target the last slot in their owning i18n block, and moves them
|
|
11407
|
+
* after the last update instruction that depends on that slot.
|
|
11276
11408
|
*/
|
|
11277
11409
|
function assignI18nSlotDependencies(job) {
|
|
11278
|
-
const i18nLastSlotConsumers = new Map();
|
|
11279
|
-
let lastSlotConsumer = null;
|
|
11280
|
-
let currentI18nOp = null;
|
|
11281
11410
|
for (const unit of job.units) {
|
|
11282
|
-
//
|
|
11283
|
-
|
|
11284
|
-
|
|
11285
|
-
|
|
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
|
+
};
|
|
11286
11423
|
}
|
|
11287
|
-
|
|
11288
|
-
|
|
11289
|
-
|
|
11290
|
-
|
|
11291
|
-
|
|
11292
|
-
|
|
11293
|
-
|
|
11294
|
-
break;
|
|
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;
|
|
11295
11431
|
}
|
|
11296
|
-
|
|
11297
|
-
|
|
11298
|
-
|
|
11299
|
-
|
|
11300
|
-
|
|
11432
|
+
if (hasConsumesSlotTrait(createOp)) {
|
|
11433
|
+
if (state !== null) {
|
|
11434
|
+
state.lastSlotConsumer = createOp.xref;
|
|
11435
|
+
}
|
|
11436
|
+
while (true) {
|
|
11437
|
+
if (updateOp.next === null) {
|
|
11438
|
+
break;
|
|
11439
|
+
}
|
|
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;
|
|
11448
|
+
}
|
|
11449
|
+
if (hasDependsOnSlotContextTrait(updateOp) && updateOp.target !== createOp.xref) {
|
|
11450
|
+
break;
|
|
11451
|
+
}
|
|
11452
|
+
updateOp = updateOp.next;
|
|
11453
|
+
}
|
|
11301
11454
|
}
|
|
11302
11455
|
}
|
|
11303
11456
|
}
|
|
@@ -11331,22 +11484,42 @@ function extractAttributes(job) {
|
|
|
11331
11484
|
break;
|
|
11332
11485
|
case OpKind.Property:
|
|
11333
11486
|
if (!op.isAnimationTrigger) {
|
|
11334
|
-
|
|
11487
|
+
let bindingKind;
|
|
11488
|
+
if (op.i18nMessage !== null && op.templateKind === null) {
|
|
11489
|
+
// If the binding has an i18n context, it is an i18n attribute, and should have that
|
|
11490
|
+
// kind in the consts array.
|
|
11491
|
+
bindingKind = BindingKind.I18n;
|
|
11492
|
+
}
|
|
11493
|
+
else if (op.isStructuralTemplateAttribute) {
|
|
11494
|
+
bindingKind = BindingKind.Template;
|
|
11495
|
+
}
|
|
11496
|
+
else {
|
|
11497
|
+
bindingKind = BindingKind.Property;
|
|
11498
|
+
}
|
|
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));
|
|
11335
11503
|
}
|
|
11336
11504
|
break;
|
|
11337
11505
|
case OpKind.StyleProp:
|
|
11338
11506
|
case OpKind.ClassProp:
|
|
11507
|
+
// TODO: Can style or class bindings be i18n attributes?
|
|
11339
11508
|
// The old compiler treated empty style bindings as regular bindings for the purpose of
|
|
11340
11509
|
// directive matching. That behavior is incorrect, but we emulate it in compatibility
|
|
11341
11510
|
// mode.
|
|
11342
11511
|
if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
|
|
11343
11512
|
op.expression instanceof EmptyExpr) {
|
|
11344
|
-
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null
|
|
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));
|
|
11345
11516
|
}
|
|
11346
11517
|
break;
|
|
11347
11518
|
case OpKind.Listener:
|
|
11348
11519
|
if (!op.isAnimationListener) {
|
|
11349
|
-
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);
|
|
11350
11523
|
if (job.kind === CompilationJobKind.Host) {
|
|
11351
11524
|
// This attribute will apply to the enclosing host binding compilation unit, so order
|
|
11352
11525
|
// doesn't matter.
|
|
@@ -11395,7 +11568,7 @@ function extractAttributeOp(unit, op, elements) {
|
|
|
11395
11568
|
}
|
|
11396
11569
|
}
|
|
11397
11570
|
if (extractable) {
|
|
11398
|
-
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);
|
|
11399
11572
|
if (unit.job.kind === CompilationJobKind.Host) {
|
|
11400
11573
|
// This attribute will apply to the enclosing host binding compilation unit, so order doesn't
|
|
11401
11574
|
// matter.
|
|
@@ -11442,16 +11615,16 @@ function specializeBindings(job) {
|
|
|
11442
11615
|
target.nonBindable = true;
|
|
11443
11616
|
}
|
|
11444
11617
|
else {
|
|
11445
|
-
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));
|
|
11446
11619
|
}
|
|
11447
11620
|
break;
|
|
11448
11621
|
case BindingKind.Property:
|
|
11449
11622
|
case BindingKind.Animation:
|
|
11450
11623
|
if (job.kind === CompilationJobKind.Host) {
|
|
11451
|
-
OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.sourceSpan));
|
|
11624
|
+
OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.i18nContext, op.securityContext, op.sourceSpan));
|
|
11452
11625
|
}
|
|
11453
11626
|
else {
|
|
11454
|
-
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));
|
|
11455
11628
|
}
|
|
11456
11629
|
break;
|
|
11457
11630
|
case BindingKind.I18n:
|
|
@@ -11628,41 +11801,6 @@ function generateConditionalExpressions(job) {
|
|
|
11628
11801
|
}
|
|
11629
11802
|
}
|
|
11630
11803
|
|
|
11631
|
-
var TagContentType;
|
|
11632
|
-
(function (TagContentType) {
|
|
11633
|
-
TagContentType[TagContentType["RAW_TEXT"] = 0] = "RAW_TEXT";
|
|
11634
|
-
TagContentType[TagContentType["ESCAPABLE_RAW_TEXT"] = 1] = "ESCAPABLE_RAW_TEXT";
|
|
11635
|
-
TagContentType[TagContentType["PARSABLE_DATA"] = 2] = "PARSABLE_DATA";
|
|
11636
|
-
})(TagContentType || (TagContentType = {}));
|
|
11637
|
-
function splitNsName(elementName) {
|
|
11638
|
-
if (elementName[0] != ':') {
|
|
11639
|
-
return [null, elementName];
|
|
11640
|
-
}
|
|
11641
|
-
const colonIndex = elementName.indexOf(':', 1);
|
|
11642
|
-
if (colonIndex === -1) {
|
|
11643
|
-
throw new Error(`Unsupported format "${elementName}" expecting ":namespace:name"`);
|
|
11644
|
-
}
|
|
11645
|
-
return [elementName.slice(1, colonIndex), elementName.slice(colonIndex + 1)];
|
|
11646
|
-
}
|
|
11647
|
-
// `<ng-container>` tags work the same regardless the namespace
|
|
11648
|
-
function isNgContainer(tagName) {
|
|
11649
|
-
return splitNsName(tagName)[1] === 'ng-container';
|
|
11650
|
-
}
|
|
11651
|
-
// `<ng-content>` tags work the same regardless the namespace
|
|
11652
|
-
function isNgContent(tagName) {
|
|
11653
|
-
return splitNsName(tagName)[1] === 'ng-content';
|
|
11654
|
-
}
|
|
11655
|
-
// `<ng-template>` tags work the same regardless the namespace
|
|
11656
|
-
function isNgTemplate(tagName) {
|
|
11657
|
-
return splitNsName(tagName)[1] === 'ng-template';
|
|
11658
|
-
}
|
|
11659
|
-
function getNsPrefix(fullName) {
|
|
11660
|
-
return fullName === null ? null : splitNsName(fullName)[0];
|
|
11661
|
-
}
|
|
11662
|
-
function mergeNsAndName(prefix, localName) {
|
|
11663
|
-
return prefix ? `:${prefix}:${localName}` : localName;
|
|
11664
|
-
}
|
|
11665
|
-
|
|
11666
11804
|
const BINARY_OPERATORS = new Map([
|
|
11667
11805
|
['&&', BinaryOperator.And],
|
|
11668
11806
|
['>', BinaryOperator.Bigger],
|
|
@@ -11723,7 +11861,7 @@ function collectElementConsts(job) {
|
|
|
11723
11861
|
if (op.kind === OpKind.ExtractedAttribute) {
|
|
11724
11862
|
const attributes = allElementAttributes.get(op.target) || new ElementAttributes();
|
|
11725
11863
|
allElementAttributes.set(op.target, attributes);
|
|
11726
|
-
attributes.add(op.bindingKind, op.name, op.expression);
|
|
11864
|
+
attributes.add(op.bindingKind, op.name, op.expression, op.trustedValueFn);
|
|
11727
11865
|
OpList.remove(op);
|
|
11728
11866
|
}
|
|
11729
11867
|
}
|
|
@@ -11789,11 +11927,12 @@ class ElementAttributes {
|
|
|
11789
11927
|
get i18n() {
|
|
11790
11928
|
return this.byKind.get(BindingKind.I18n) ?? FLYWEIGHT_ARRAY;
|
|
11791
11929
|
}
|
|
11792
|
-
add(kind, name, value) {
|
|
11930
|
+
add(kind, name, value, trustedValueFn) {
|
|
11793
11931
|
if (this.known.has(name)) {
|
|
11794
11932
|
return;
|
|
11795
11933
|
}
|
|
11796
11934
|
this.known.add(name);
|
|
11935
|
+
// TODO: Can this be its own phase
|
|
11797
11936
|
if (name === 'ngProjectAs') {
|
|
11798
11937
|
if (value === null || !(value instanceof LiteralExpr) || (value.value == null) ||
|
|
11799
11938
|
(typeof value.value?.toString() !== 'string')) {
|
|
@@ -11807,9 +11946,17 @@ class ElementAttributes {
|
|
|
11807
11946
|
array.push(...getAttributeNameLiterals$1(name));
|
|
11808
11947
|
if (kind === BindingKind.Attribute || kind === BindingKind.StyleProperty) {
|
|
11809
11948
|
if (value === null) {
|
|
11810
|
-
throw Error('Attribute & style element attributes must have a value');
|
|
11949
|
+
throw Error('Attribute, i18n attribute, & style element attributes must have a value');
|
|
11950
|
+
}
|
|
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);
|
|
11811
11959
|
}
|
|
11812
|
-
array.push(value);
|
|
11813
11960
|
}
|
|
11814
11961
|
}
|
|
11815
11962
|
arrayFor(kind) {
|
|
@@ -11861,6 +12008,50 @@ function serializeAttributes({ attributes, bindings, classes, i18n, projectAs, s
|
|
|
11861
12008
|
return literalArr(attrArray);
|
|
11862
12009
|
}
|
|
11863
12010
|
|
|
12011
|
+
/**
|
|
12012
|
+
* Some binding instructions in the update block may actually correspond to i18n bindings. In that
|
|
12013
|
+
* case, they should be replaced with i18nExp instructions for the dynamic portions.
|
|
12014
|
+
*/
|
|
12015
|
+
function convertI18nBindings(job) {
|
|
12016
|
+
const i18nAttributesByElem = new Map();
|
|
12017
|
+
for (const unit of job.units) {
|
|
12018
|
+
for (const op of unit.create) {
|
|
12019
|
+
if (op.kind === OpKind.I18nAttributes) {
|
|
12020
|
+
i18nAttributesByElem.set(op.target, op);
|
|
12021
|
+
}
|
|
12022
|
+
}
|
|
12023
|
+
for (const op of unit.update) {
|
|
12024
|
+
switch (op.kind) {
|
|
12025
|
+
case OpKind.Property:
|
|
12026
|
+
case OpKind.Attribute:
|
|
12027
|
+
if (op.i18nContext === null) {
|
|
12028
|
+
continue;
|
|
12029
|
+
}
|
|
12030
|
+
if (!(op.expression instanceof Interpolation)) {
|
|
12031
|
+
continue;
|
|
12032
|
+
}
|
|
12033
|
+
const i18nAttributesForElem = i18nAttributesByElem.get(op.target);
|
|
12034
|
+
if (i18nAttributesForElem === undefined) {
|
|
12035
|
+
throw new Error('AssertionError: An i18n attribute binding instruction requires the owning element to have an I18nAttributes create instruction');
|
|
12036
|
+
}
|
|
12037
|
+
if (i18nAttributesForElem.target !== op.target) {
|
|
12038
|
+
throw new Error('AssertionError: Expected i18nAttributes target element to match binding target element');
|
|
12039
|
+
}
|
|
12040
|
+
const ops = [];
|
|
12041
|
+
for (let i = 0; i < op.expression.expressions.length; i++) {
|
|
12042
|
+
const expr = op.expression.expressions[i];
|
|
12043
|
+
if (op.expression.i18nPlaceholders.length !== op.expression.expressions.length) {
|
|
12044
|
+
throw new Error(`AssertionError: An i18n attribute binding instruction requires the same number of expressions and placeholders, but found ${op.expression.i18nPlaceholders.length} placeholders and ${op.expression.expressions.length} expressions`);
|
|
12045
|
+
}
|
|
12046
|
+
ops.push(createI18nExpressionOp(op.i18nContext, i18nAttributesForElem.target, i18nAttributesForElem.xref, i18nAttributesForElem.handle, expr, op.expression.i18nPlaceholders[i], I18nParamResolutionTime.Creation, I18nExpressionFor.I18nAttribute, op.name, op.sourceSpan));
|
|
12047
|
+
}
|
|
12048
|
+
OpList.replaceWithMany(op, ops);
|
|
12049
|
+
break;
|
|
12050
|
+
}
|
|
12051
|
+
}
|
|
12052
|
+
}
|
|
12053
|
+
}
|
|
12054
|
+
|
|
11864
12055
|
/**
|
|
11865
12056
|
* Create extracted deps functions for defer ops.
|
|
11866
12057
|
*/
|
|
@@ -11909,6 +12100,9 @@ function createI18nContexts(job) {
|
|
|
11909
12100
|
const rootContexts = new Map();
|
|
11910
12101
|
let currentI18nOp = null;
|
|
11911
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();
|
|
11912
12106
|
for (const unit of job.units) {
|
|
11913
12107
|
for (const op of unit.create) {
|
|
11914
12108
|
switch (op.kind) {
|
|
@@ -11946,6 +12140,25 @@ function createI18nContexts(job) {
|
|
|
11946
12140
|
break;
|
|
11947
12141
|
}
|
|
11948
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
|
+
}
|
|
11949
12162
|
}
|
|
11950
12163
|
// Assign contexts to child i18n blocks, now that all root i18n blocks have their context
|
|
11951
12164
|
// assigned.
|
|
@@ -12304,7 +12517,7 @@ function ternaryTransform(e) {
|
|
|
12304
12517
|
/**
|
|
12305
12518
|
* The escape sequence used indicate message param values.
|
|
12306
12519
|
*/
|
|
12307
|
-
const ESCAPE = '\uFFFD';
|
|
12520
|
+
const ESCAPE$1 = '\uFFFD';
|
|
12308
12521
|
/**
|
|
12309
12522
|
* Marker used to indicate an element tag.
|
|
12310
12523
|
*/
|
|
@@ -12353,6 +12566,18 @@ function extractI18nMessages(job) {
|
|
|
12353
12566
|
}
|
|
12354
12567
|
}
|
|
12355
12568
|
}
|
|
12569
|
+
// TODO: Miles and I think that contexts have a 1-to-1 correspondence with extracted messages, so
|
|
12570
|
+
// this phase can probably be simplified.
|
|
12571
|
+
// Extract messages from contexts of i18n attributes.
|
|
12572
|
+
for (const unit of job.units) {
|
|
12573
|
+
for (const op of unit.create) {
|
|
12574
|
+
if (op.kind !== OpKind.I18nContext || op.contextKind !== I18nContextKind.Attr) {
|
|
12575
|
+
continue;
|
|
12576
|
+
}
|
|
12577
|
+
const i18nMessageOp = createI18nMessage(job, op);
|
|
12578
|
+
unit.create.push(i18nMessageOp);
|
|
12579
|
+
}
|
|
12580
|
+
}
|
|
12356
12581
|
// Extract messages from root i18n blocks.
|
|
12357
12582
|
const i18nBlockMessages = new Map();
|
|
12358
12583
|
for (const unit of job.units) {
|
|
@@ -12377,6 +12602,9 @@ function extractI18nMessages(job) {
|
|
|
12377
12602
|
}
|
|
12378
12603
|
const i18nContext = i18nContexts.get(op.context);
|
|
12379
12604
|
if (i18nContext.contextKind === I18nContextKind.Icu) {
|
|
12605
|
+
if (i18nContext.i18nBlock === null) {
|
|
12606
|
+
throw Error('ICU context should have its i18n block set.');
|
|
12607
|
+
}
|
|
12380
12608
|
const subMessage = createI18nMessage(job, i18nContext, op.messagePlaceholder);
|
|
12381
12609
|
unit.create.push(subMessage);
|
|
12382
12610
|
const rootI18nId = i18nBlocks.get(i18nContext.i18nBlock).root;
|
|
@@ -12396,119 +12624,86 @@ function extractI18nMessages(job) {
|
|
|
12396
12624
|
* Create an i18n message op from an i18n context op.
|
|
12397
12625
|
*/
|
|
12398
12626
|
function createI18nMessage(job, context, messagePlaceholder) {
|
|
12399
|
-
let
|
|
12400
|
-
const
|
|
12401
|
-
needsPostprocessing
|
|
12402
|
-
|
|
12627
|
+
let formattedParams = formatParams(context.params);
|
|
12628
|
+
const formattedPostprocessingParams = formatParams(context.postprocessingParams);
|
|
12629
|
+
let needsPostprocessing = formattedPostprocessingParams.size > 0;
|
|
12630
|
+
for (const values of context.params.values()) {
|
|
12631
|
+
if (values.length > 1) {
|
|
12632
|
+
needsPostprocessing = true;
|
|
12633
|
+
}
|
|
12634
|
+
}
|
|
12635
|
+
return createI18nMessageOp(job.allocateXrefId(), context.xref, context.i18nBlock, context.message, messagePlaceholder ?? null, formattedParams, formattedPostprocessingParams, needsPostprocessing);
|
|
12403
12636
|
}
|
|
12404
12637
|
/**
|
|
12405
12638
|
* Formats a map of `I18nParamValue[]` values into a map of `Expression` values.
|
|
12406
|
-
* @return A tuple of the formatted params and a boolean indicating whether postprocessing is needed
|
|
12407
|
-
* for any of the params
|
|
12408
12639
|
*/
|
|
12409
12640
|
function formatParams(params) {
|
|
12410
12641
|
const formattedParams = new Map();
|
|
12411
|
-
let needsPostprocessing = false;
|
|
12412
12642
|
for (const [placeholder, placeholderValues] of params) {
|
|
12413
|
-
const
|
|
12414
|
-
needsPostprocessing ||= paramNeedsPostprocessing;
|
|
12643
|
+
const serializedValues = formatParamValues(placeholderValues);
|
|
12415
12644
|
if (serializedValues !== null) {
|
|
12416
12645
|
formattedParams.set(placeholder, literal(serializedValues));
|
|
12417
12646
|
}
|
|
12418
12647
|
}
|
|
12419
|
-
return
|
|
12648
|
+
return formattedParams;
|
|
12420
12649
|
}
|
|
12421
12650
|
/**
|
|
12422
12651
|
* Formats an `I18nParamValue[]` into a string (or null for empty array).
|
|
12423
|
-
* @return A tuple of the formatted value and a boolean indicating whether postprocessing is needed
|
|
12424
|
-
* for the value
|
|
12425
12652
|
*/
|
|
12426
12653
|
function formatParamValues(values) {
|
|
12427
12654
|
if (values.length === 0) {
|
|
12428
|
-
return
|
|
12655
|
+
return null;
|
|
12429
12656
|
}
|
|
12430
|
-
collapseElementTemplatePairs(values);
|
|
12431
12657
|
const serializedValues = values.map(value => formatValue(value));
|
|
12432
12658
|
return serializedValues.length === 1 ?
|
|
12433
|
-
|
|
12434
|
-
|
|
12435
|
-
}
|
|
12436
|
-
/**
|
|
12437
|
-
* Collapses element/template pairs that refer to the same subTemplateIndex, i.e. elements and
|
|
12438
|
-
* templates that refer to the same element instance.
|
|
12439
|
-
*
|
|
12440
|
-
* This accounts for the case of a structural directive inside an i18n block, e.g.:
|
|
12441
|
-
* ```
|
|
12442
|
-
* <div i18n>
|
|
12443
|
-
* <div *ngIf="condition">
|
|
12444
|
-
* </div>
|
|
12445
|
-
* ```
|
|
12446
|
-
*
|
|
12447
|
-
* In this case, both the element start and template start placeholders are the same,
|
|
12448
|
-
* and we collapse them down into a single compound placeholder value. Rather than produce
|
|
12449
|
-
* `[\uFFFD#1:1\uFFFD|\uFFFD*2:1\uFFFD]`, we want to produce `\uFFFD#1:1\uFFFD\uFFFD*2:1\uFFFD`,
|
|
12450
|
-
* likewise for the closing of the element/template.
|
|
12451
|
-
*/
|
|
12452
|
-
function collapseElementTemplatePairs(values) {
|
|
12453
|
-
// Record the indicies of element and template values in the values array by subTemplateIndex.
|
|
12454
|
-
const valueIndiciesBySubTemplateIndex = new Map();
|
|
12455
|
-
for (let i = 0; i < values.length; i++) {
|
|
12456
|
-
const value = values[i];
|
|
12457
|
-
if (value.subTemplateIndex !== null &&
|
|
12458
|
-
(value.flags & (I18nParamValueFlags.ElementTag | I18nParamValueFlags.TemplateTag))) {
|
|
12459
|
-
const valueIndicies = valueIndiciesBySubTemplateIndex.get(value.subTemplateIndex) ?? [];
|
|
12460
|
-
valueIndicies.push(i);
|
|
12461
|
-
valueIndiciesBySubTemplateIndex.set(value.subTemplateIndex, valueIndicies);
|
|
12462
|
-
}
|
|
12463
|
-
}
|
|
12464
|
-
// For each subTemplateIndex, check if any values can be collapsed.
|
|
12465
|
-
for (const [subTemplateIndex, valueIndicies] of valueIndiciesBySubTemplateIndex) {
|
|
12466
|
-
if (valueIndicies.length > 1) {
|
|
12467
|
-
const elementIndex = valueIndicies.find(index => values[index].flags & I18nParamValueFlags.ElementTag);
|
|
12468
|
-
const templateIndex = valueIndicies.find(index => values[index].flags & I18nParamValueFlags.TemplateTag);
|
|
12469
|
-
// If the values list contains both an element and template value, we can collapse.
|
|
12470
|
-
if (elementIndex !== undefined && templateIndex !== undefined) {
|
|
12471
|
-
const elementValue = values[elementIndex];
|
|
12472
|
-
const templateValue = values[templateIndex];
|
|
12473
|
-
// To match the TemplateDefinitionBuilder output, flip the order depending on whether the
|
|
12474
|
-
// values represent a closing or opening tag (or both).
|
|
12475
|
-
// TODO(mmalerba): Figure out if this makes a difference in terms of either functionality,
|
|
12476
|
-
// or the resulting message ID. If not, we can remove the special-casing in the future.
|
|
12477
|
-
let compundValue;
|
|
12478
|
-
if ((elementValue.flags & I18nParamValueFlags.OpenTag) &&
|
|
12479
|
-
(elementValue.flags & I18nParamValueFlags.CloseTag)) {
|
|
12480
|
-
// TODO(mmalerba): Is this a TDB bug? I don't understand why it would put the template
|
|
12481
|
-
// value twice.
|
|
12482
|
-
compundValue = `${formatValue(templateValue)}${formatValue(elementValue)}${formatValue(templateValue)}`;
|
|
12483
|
-
}
|
|
12484
|
-
else if (elementValue.flags & I18nParamValueFlags.OpenTag) {
|
|
12485
|
-
compundValue = `${formatValue(templateValue)}${formatValue(elementValue)}`;
|
|
12486
|
-
}
|
|
12487
|
-
else {
|
|
12488
|
-
compundValue = `${formatValue(elementValue)}${formatValue(templateValue)}`;
|
|
12489
|
-
}
|
|
12490
|
-
// Replace the element value with the combined value.
|
|
12491
|
-
values.splice(elementIndex, 1, { value: compundValue, subTemplateIndex, flags: I18nParamValueFlags.None });
|
|
12492
|
-
// Replace the template value with null to preserve the indicies we calculated earlier.
|
|
12493
|
-
values.splice(templateIndex, 1, null);
|
|
12494
|
-
}
|
|
12495
|
-
}
|
|
12496
|
-
}
|
|
12497
|
-
// Strip out any nulled out values we introduced above.
|
|
12498
|
-
for (let i = values.length - 1; i >= 0; i--) {
|
|
12499
|
-
if (values[i] === null) {
|
|
12500
|
-
values.splice(i, 1);
|
|
12501
|
-
}
|
|
12502
|
-
}
|
|
12659
|
+
serializedValues[0] :
|
|
12660
|
+
`${LIST_START_MARKER}${serializedValues.join(LIST_DELIMITER)}${LIST_END_MARKER}`;
|
|
12503
12661
|
}
|
|
12504
12662
|
/**
|
|
12505
12663
|
* Formats a single `I18nParamValue` into a string
|
|
12506
12664
|
*/
|
|
12507
12665
|
function formatValue(value) {
|
|
12666
|
+
// Element tags with a structural directive use a special form that concatenates the element and
|
|
12667
|
+
// template values.
|
|
12668
|
+
if ((value.flags & I18nParamValueFlags.ElementTag) &&
|
|
12669
|
+
(value.flags & I18nParamValueFlags.TemplateTag)) {
|
|
12670
|
+
if (typeof value.value !== 'object') {
|
|
12671
|
+
throw Error('AssertionError: Expected i18n param value to have an element and template slot');
|
|
12672
|
+
}
|
|
12673
|
+
const elementValue = formatValue({
|
|
12674
|
+
...value,
|
|
12675
|
+
value: value.value.element,
|
|
12676
|
+
flags: value.flags & ~I18nParamValueFlags.TemplateTag
|
|
12677
|
+
});
|
|
12678
|
+
const templateValue = formatValue({
|
|
12679
|
+
...value,
|
|
12680
|
+
value: value.value.template,
|
|
12681
|
+
flags: value.flags & ~I18nParamValueFlags.ElementTag
|
|
12682
|
+
});
|
|
12683
|
+
// TODO(mmalerba): This is likely a bug in TemplateDefinitionBuilder, we should not need to
|
|
12684
|
+
// record the template value twice. For now I'm re-implementing the behavior here to keep the
|
|
12685
|
+
// output consistent with TemplateDefinitionBuilder.
|
|
12686
|
+
if ((value.flags & I18nParamValueFlags.OpenTag) &&
|
|
12687
|
+
(value.flags & I18nParamValueFlags.CloseTag)) {
|
|
12688
|
+
return `${templateValue}${elementValue}${templateValue}`;
|
|
12689
|
+
}
|
|
12690
|
+
// To match the TemplateDefinitionBuilder output, flip the order depending on whether the
|
|
12691
|
+
// values represent a closing or opening tag (or both).
|
|
12692
|
+
// TODO(mmalerba): Figure out if this makes a difference in terms of either functionality,
|
|
12693
|
+
// or the resulting message ID. If not, we can remove the special-casing in the future.
|
|
12694
|
+
return value.flags & I18nParamValueFlags.CloseTag ? `${elementValue}${templateValue}` :
|
|
12695
|
+
`${templateValue}${elementValue}`;
|
|
12696
|
+
}
|
|
12697
|
+
// Self-closing tags use a special form that concatenates the start and close tag values.
|
|
12698
|
+
if ((value.flags & I18nParamValueFlags.OpenTag) &&
|
|
12699
|
+
(value.flags & I18nParamValueFlags.CloseTag)) {
|
|
12700
|
+
return `${formatValue({ ...value, flags: value.flags & ~I18nParamValueFlags.CloseTag })}${formatValue({ ...value, flags: value.flags & ~I18nParamValueFlags.OpenTag })}`;
|
|
12701
|
+
}
|
|
12508
12702
|
// If there are no special flags, just return the raw value.
|
|
12509
12703
|
if (value.flags === I18nParamValueFlags.None) {
|
|
12510
12704
|
return `${value.value}`;
|
|
12511
12705
|
}
|
|
12706
|
+
// Encode the remaining flags as part of the value.
|
|
12512
12707
|
let tagMarker = '';
|
|
12513
12708
|
let closeMarker = '';
|
|
12514
12709
|
if (value.flags & I18nParamValueFlags.ElementTag) {
|
|
@@ -12521,12 +12716,7 @@ function formatValue(value) {
|
|
|
12521
12716
|
closeMarker = value.flags & I18nParamValueFlags.CloseTag ? TAG_CLOSE_MARKER : '';
|
|
12522
12717
|
}
|
|
12523
12718
|
const context = value.subTemplateIndex === null ? '' : `${CONTEXT_MARKER}${value.subTemplateIndex}`;
|
|
12524
|
-
|
|
12525
|
-
if ((value.flags & I18nParamValueFlags.OpenTag) &&
|
|
12526
|
-
(value.flags & I18nParamValueFlags.CloseTag)) {
|
|
12527
|
-
return `${ESCAPE}${tagMarker}${value.value}${context}${ESCAPE}${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
|
|
12528
|
-
}
|
|
12529
|
-
return `${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
|
|
12719
|
+
return `${ESCAPE$1}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE$1}`;
|
|
12530
12720
|
}
|
|
12531
12721
|
|
|
12532
12722
|
/**
|
|
@@ -12560,7 +12750,7 @@ function generateAdvance(job) {
|
|
|
12560
12750
|
else if (!slotMap.has(op.target)) {
|
|
12561
12751
|
// We expect ops that _do_ depend on the slot counter to point at declarations that exist in
|
|
12562
12752
|
// the `slotMap`.
|
|
12563
|
-
throw new Error(`AssertionError: reference to unknown slot for
|
|
12753
|
+
throw new Error(`AssertionError: reference to unknown slot for target ${op.target}`);
|
|
12564
12754
|
}
|
|
12565
12755
|
const slot = slotMap.get(op.target);
|
|
12566
12756
|
// Does the slot counter need to be adjusted?
|
|
@@ -12643,9 +12833,15 @@ function recursivelyProcessView(view, parentScope) {
|
|
|
12643
12833
|
for (const op of view.create) {
|
|
12644
12834
|
switch (op.kind) {
|
|
12645
12835
|
case OpKind.Template:
|
|
12836
|
+
// Descend into child embedded views.
|
|
12837
|
+
recursivelyProcessView(view.job.views.get(op.xref), scope);
|
|
12838
|
+
break;
|
|
12646
12839
|
case OpKind.RepeaterCreate:
|
|
12647
12840
|
// Descend into child embedded views.
|
|
12648
12841
|
recursivelyProcessView(view.job.views.get(op.xref), scope);
|
|
12842
|
+
if (op.emptyView) {
|
|
12843
|
+
recursivelyProcessView(view.job.views.get(op.emptyView), scope);
|
|
12844
|
+
}
|
|
12649
12845
|
break;
|
|
12650
12846
|
case OpKind.Listener:
|
|
12651
12847
|
// Prepend variables to listener handler functions.
|
|
@@ -19770,35 +19966,136 @@ const NG_I18N_CLOSURE_MODE$1 = 'ngI18nClosureMode';
|
|
|
19770
19966
|
* considers variables like `I18N_0` as constants and throws an error when their value changes.
|
|
19771
19967
|
*/
|
|
19772
19968
|
const TRANSLATION_VAR_PREFIX = 'i18n_';
|
|
19969
|
+
/** Prefix of ICU expressions for post processing */
|
|
19970
|
+
const I18N_ICU_MAPPING_PREFIX = 'I18N_EXP_';
|
|
19971
|
+
/**
|
|
19972
|
+
* The escape sequence used for message param values.
|
|
19973
|
+
*/
|
|
19974
|
+
const ESCAPE = '\uFFFD';
|
|
19773
19975
|
/**
|
|
19774
19976
|
* Lifts i18n properties into the consts array.
|
|
19775
19977
|
* TODO: Can we use `ConstCollectedExpr`?
|
|
19978
|
+
* TODO: The way the various attributes are linked together is very complex. Perhaps we could
|
|
19979
|
+
* simplify the process, maybe by combining the context and message ops?
|
|
19776
19980
|
*/
|
|
19777
19981
|
function collectI18nConsts(job) {
|
|
19778
19982
|
const fileBasedI18nSuffix = job.relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_').toUpperCase() + '_';
|
|
19779
|
-
|
|
19780
|
-
//
|
|
19983
|
+
// Step One: Build up various lookup maps we need to collect all the consts.
|
|
19984
|
+
// Context Xref -> Extracted Attribute Ops
|
|
19985
|
+
const extractedAttributesByI18nContext = new Map();
|
|
19986
|
+
// Element/ElementStart Xref -> I18n Attributes config op
|
|
19987
|
+
const i18nAttributesByElement = new Map();
|
|
19988
|
+
// Element/ElementStart Xref -> All I18n Expression ops for attrs on that target
|
|
19989
|
+
const i18nExpressionsByElement = new Map();
|
|
19990
|
+
// I18n Message Xref -> I18n Message Op (TODO: use a central op map)
|
|
19781
19991
|
const messages = new Map();
|
|
19992
|
+
for (const unit of job.units) {
|
|
19993
|
+
for (const op of unit.ops()) {
|
|
19994
|
+
if (op.kind === OpKind.ExtractedAttribute && op.i18nContext !== null) {
|
|
19995
|
+
const attributes = extractedAttributesByI18nContext.get(op.i18nContext) ?? [];
|
|
19996
|
+
attributes.push(op);
|
|
19997
|
+
extractedAttributesByI18nContext.set(op.i18nContext, attributes);
|
|
19998
|
+
}
|
|
19999
|
+
else if (op.kind === OpKind.I18nAttributes) {
|
|
20000
|
+
i18nAttributesByElement.set(op.target, op);
|
|
20001
|
+
}
|
|
20002
|
+
else if (op.kind === OpKind.I18nExpression && op.usage === I18nExpressionFor.I18nAttribute) {
|
|
20003
|
+
const expressions = i18nExpressionsByElement.get(op.target) ?? [];
|
|
20004
|
+
expressions.push(op);
|
|
20005
|
+
i18nExpressionsByElement.set(op.target, expressions);
|
|
20006
|
+
}
|
|
20007
|
+
else if (op.kind === OpKind.I18nMessage) {
|
|
20008
|
+
messages.set(op.xref, op);
|
|
20009
|
+
}
|
|
20010
|
+
}
|
|
20011
|
+
}
|
|
20012
|
+
// Step Two: Serialize the extracted i18n messages for root i18n blocks and i18n attributes into
|
|
20013
|
+
// the const array.
|
|
20014
|
+
//
|
|
20015
|
+
// Also, each i18n message will have a variable expression that can refer to its
|
|
20016
|
+
// value. Store these expressions in the appropriate place:
|
|
20017
|
+
// 1. For normal i18n content, it also goes in the const array. We save the const index to use
|
|
20018
|
+
// later.
|
|
20019
|
+
// 2. For extracted attributes, it becomes the value of the extracted attribute instruction.
|
|
20020
|
+
// 3. For i18n bindings, it will go in a separate const array instruction below; for now, we just
|
|
20021
|
+
// save it.
|
|
20022
|
+
const i18nValuesByContext = new Map();
|
|
20023
|
+
const messageConstIndices = new Map();
|
|
19782
20024
|
for (const unit of job.units) {
|
|
19783
20025
|
for (const op of unit.create) {
|
|
19784
20026
|
if (op.kind === OpKind.I18nMessage) {
|
|
19785
|
-
|
|
20027
|
+
if (op.messagePlaceholder === null) {
|
|
20028
|
+
const { mainVar, statements } = collectMessage(job, fileBasedI18nSuffix, messages, op);
|
|
20029
|
+
if (op.i18nBlock !== null) {
|
|
20030
|
+
// This is a regular i18n message with a corresponding i18n block. Collect it into the
|
|
20031
|
+
// const array.
|
|
20032
|
+
const i18nConst = job.addConst(mainVar, statements);
|
|
20033
|
+
messageConstIndices.set(op.i18nBlock, i18nConst);
|
|
20034
|
+
}
|
|
20035
|
+
else {
|
|
20036
|
+
// This is an i18n attribute. Extract the initializers into the const pool.
|
|
20037
|
+
job.constsInitializers.push(...statements);
|
|
20038
|
+
// Save the i18n variable value for later.
|
|
20039
|
+
i18nValuesByContext.set(op.i18nContext, mainVar);
|
|
20040
|
+
// This i18n message may correspond to an individual extracted attribute. If so, The
|
|
20041
|
+
// value of that attribute is updated to read the extracted i18n variable.
|
|
20042
|
+
const attributesForMessage = extractedAttributesByI18nContext.get(op.i18nContext);
|
|
20043
|
+
if (attributesForMessage !== undefined) {
|
|
20044
|
+
for (const attr of attributesForMessage) {
|
|
20045
|
+
attr.expression = mainVar.clone();
|
|
20046
|
+
}
|
|
20047
|
+
}
|
|
20048
|
+
}
|
|
20049
|
+
}
|
|
19786
20050
|
OpList.remove(op);
|
|
19787
20051
|
}
|
|
19788
20052
|
}
|
|
19789
20053
|
}
|
|
19790
|
-
//
|
|
19791
|
-
|
|
19792
|
-
|
|
19793
|
-
|
|
19794
|
-
|
|
20054
|
+
// Step Three: Serialize I18nAttributes configurations into the const array. Each I18nAttributes
|
|
20055
|
+
// instruction has a config array, which contains k-v pairs describing each binding name, and the
|
|
20056
|
+
// i18n variable that provides the value.
|
|
20057
|
+
for (const unit of job.units) {
|
|
20058
|
+
for (const elem of unit.create) {
|
|
20059
|
+
if (isElementOrContainerOp(elem)) {
|
|
20060
|
+
const i18nAttributes = i18nAttributesByElement.get(elem.xref);
|
|
20061
|
+
if (i18nAttributes === undefined) {
|
|
20062
|
+
// This element is not associated with an i18n attributes configuration instruction.
|
|
20063
|
+
continue;
|
|
20064
|
+
}
|
|
20065
|
+
let i18nExpressions = i18nExpressionsByElement.get(elem.xref);
|
|
20066
|
+
if (i18nExpressions === undefined) {
|
|
20067
|
+
// Unused i18nAttributes should have already been removed.
|
|
20068
|
+
// TODO: Should the removal of those dead instructions be merged with this phase?
|
|
20069
|
+
throw new Error('AssertionError: Could not find any i18n expressions associated with an I18nAttributes instruction');
|
|
20070
|
+
}
|
|
20071
|
+
// Find expressions for all the unique property names, removing duplicates.
|
|
20072
|
+
const seenPropertyNames = new Set();
|
|
20073
|
+
i18nExpressions = i18nExpressions.filter(i18nExpr => {
|
|
20074
|
+
const seen = (seenPropertyNames.has(i18nExpr.name));
|
|
20075
|
+
seenPropertyNames.add(i18nExpr.name);
|
|
20076
|
+
return !seen;
|
|
20077
|
+
});
|
|
20078
|
+
const i18nAttributeConfig = i18nExpressions.flatMap(i18nExpr => {
|
|
20079
|
+
const i18nExprValue = i18nValuesByContext.get(i18nExpr.context);
|
|
20080
|
+
if (i18nExprValue === undefined) {
|
|
20081
|
+
throw new Error('AssertionError: Could not find i18n expression\'s value');
|
|
20082
|
+
}
|
|
20083
|
+
return [literal(i18nExpr.name), i18nExprValue];
|
|
20084
|
+
});
|
|
20085
|
+
i18nAttributes.i18nAttributesConfig =
|
|
20086
|
+
job.addConst(new LiteralArrayExpr(i18nAttributeConfig));
|
|
20087
|
+
}
|
|
19795
20088
|
}
|
|
19796
20089
|
}
|
|
19797
|
-
//
|
|
20090
|
+
// Step Four: Propagate the extracted const index into i18n ops that messages were extracted from.
|
|
19798
20091
|
for (const unit of job.units) {
|
|
19799
20092
|
for (const op of unit.create) {
|
|
19800
20093
|
if (op.kind === OpKind.I18nStart) {
|
|
19801
|
-
|
|
20094
|
+
const msgIndex = messageConstIndices.get(op.root);
|
|
20095
|
+
if (msgIndex === undefined) {
|
|
20096
|
+
throw new Error('AssertionError: Could not find corresponding i18n block index for an i18n message op; was an i18n message incorrectly assumed to correspond to an attribute?');
|
|
20097
|
+
}
|
|
20098
|
+
op.messageIndex = msgIndex;
|
|
19802
20099
|
}
|
|
19803
20100
|
}
|
|
19804
20101
|
}
|
|
@@ -19808,18 +20105,23 @@ function collectI18nConsts(job) {
|
|
|
19808
20105
|
* This will recursively collect any sub-messages referenced from the parent message as well.
|
|
19809
20106
|
*/
|
|
19810
20107
|
function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
|
|
19811
|
-
// Recursively collect any sub-messages,
|
|
20108
|
+
// Recursively collect any sub-messages, record each sub-message's main variable under its
|
|
20109
|
+
// placeholder so that we can add them to the params for the parent message. It is possible
|
|
20110
|
+
// that multiple sub-messages will share the same placeholder, so we need to track an array of
|
|
20111
|
+
// variables for each placeholder.
|
|
19812
20112
|
const statements = [];
|
|
20113
|
+
const subMessagePlaceholders = new Map();
|
|
19813
20114
|
for (const subMessageId of messageOp.subMessages) {
|
|
19814
20115
|
const subMessage = messages.get(subMessageId);
|
|
19815
20116
|
const { mainVar: subMessageVar, statements: subMessageStatements } = collectMessage(job, fileBasedI18nSuffix, messages, subMessage);
|
|
19816
20117
|
statements.push(...subMessageStatements);
|
|
19817
|
-
|
|
20118
|
+
const subMessages = subMessagePlaceholders.get(subMessage.messagePlaceholder) ?? [];
|
|
20119
|
+
subMessages.push(subMessageVar);
|
|
20120
|
+
subMessagePlaceholders.set(subMessage.messagePlaceholder, subMessages);
|
|
19818
20121
|
}
|
|
20122
|
+
addSubMessageParams(messageOp, subMessagePlaceholders);
|
|
19819
20123
|
// Sort the params for consistency with TemaplateDefinitionBuilder output.
|
|
19820
20124
|
messageOp.params = new Map([...messageOp.params.entries()].sort());
|
|
19821
|
-
// Check that the message has all of its parameters filled out.
|
|
19822
|
-
assertAllParamsResolved(messageOp);
|
|
19823
20125
|
const mainVar = variable(job.pool.uniqueName(TRANSLATION_VAR_PREFIX));
|
|
19824
20126
|
// Closure Compiler requires const names to start with `MSG_` but disallows any other
|
|
19825
20127
|
// const to start with `MSG_`. We define a variable starting with `MSG_` just for the
|
|
@@ -19830,10 +20132,11 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
|
|
|
19830
20132
|
// set in post-processing.
|
|
19831
20133
|
if (messageOp.needsPostprocessing) {
|
|
19832
20134
|
// Sort the post-processing params for consistency with TemaplateDefinitionBuilder output.
|
|
19833
|
-
|
|
20135
|
+
const postprocessingParams = Object.fromEntries([...messageOp.postprocessingParams.entries()].sort());
|
|
20136
|
+
const formattedPostprocessingParams = formatI18nPlaceholderNamesInMap(postprocessingParams, /* useCamelCase */ false);
|
|
19834
20137
|
const extraTransformFnParams = [];
|
|
19835
20138
|
if (messageOp.postprocessingParams.size > 0) {
|
|
19836
|
-
extraTransformFnParams.push(
|
|
20139
|
+
extraTransformFnParams.push(mapLiteral(formattedPostprocessingParams, /* quoted */ true));
|
|
19837
20140
|
}
|
|
19838
20141
|
transformFn = (expr) => importExpr(Identifiers.i18nPostprocess).callFn([expr, ...extraTransformFnParams]);
|
|
19839
20142
|
}
|
|
@@ -19841,6 +20144,26 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
|
|
|
19841
20144
|
statements.push(...getTranslationDeclStmts$1(messageOp.message, mainVar, closureVar, messageOp.params, transformFn));
|
|
19842
20145
|
return { mainVar, statements };
|
|
19843
20146
|
}
|
|
20147
|
+
/**
|
|
20148
|
+
* Adds the given subMessage placeholders to the given message op.
|
|
20149
|
+
*
|
|
20150
|
+
* If a placeholder only corresponds to a single sub-message variable, we just set that variable
|
|
20151
|
+
* as the param value. However, if the placeholder corresponds to multiple sub-message
|
|
20152
|
+
* variables, we need to add a special placeholder value that is handled by the post-processing
|
|
20153
|
+
* step. We then add the array of variables as a post-processing param.
|
|
20154
|
+
*/
|
|
20155
|
+
function addSubMessageParams(messageOp, subMessagePlaceholders) {
|
|
20156
|
+
for (const [placeholder, subMessages] of subMessagePlaceholders) {
|
|
20157
|
+
if (subMessages.length === 1) {
|
|
20158
|
+
messageOp.params.set(placeholder, subMessages[0]);
|
|
20159
|
+
}
|
|
20160
|
+
else {
|
|
20161
|
+
messageOp.params.set(placeholder, literal(`${ESCAPE}${I18N_ICU_MAPPING_PREFIX}${placeholder}${ESCAPE}`));
|
|
20162
|
+
messageOp.postprocessingParams.set(placeholder, literalArr(subMessages));
|
|
20163
|
+
messageOp.needsPostprocessing = true;
|
|
20164
|
+
}
|
|
20165
|
+
}
|
|
20166
|
+
}
|
|
19844
20167
|
/**
|
|
19845
20168
|
* Generate statements that define a given translation message.
|
|
19846
20169
|
*
|
|
@@ -19863,7 +20186,8 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
|
|
|
19863
20186
|
* @param closureVar The variable for Closure `goog.getMsg` calls, e.g. `MSG_EXTERNAL_XXX`.
|
|
19864
20187
|
* @param params Object mapping placeholder names to their values (e.g.
|
|
19865
20188
|
* `{ "interpolation": "\uFFFD0\uFFFD" }`).
|
|
19866
|
-
* @param transformFn Optional transformation function that will be applied to the translation
|
|
20189
|
+
* @param transformFn Optional transformation function that will be applied to the translation
|
|
20190
|
+
* (e.g.
|
|
19867
20191
|
* post-processing).
|
|
19868
20192
|
* @returns An array of statements that defined a given translation.
|
|
19869
20193
|
*/
|
|
@@ -19908,28 +20232,13 @@ function i18nGenerateClosureVar(pool, messageId, fileBasedI18nSuffix, useExterna
|
|
|
19908
20232
|
}
|
|
19909
20233
|
return variable(name);
|
|
19910
20234
|
}
|
|
19911
|
-
/**
|
|
19912
|
-
* Asserts that all of the message's placeholders have values.
|
|
19913
|
-
*/
|
|
19914
|
-
function assertAllParamsResolved(op) {
|
|
19915
|
-
for (let placeholder in op.message.placeholders) {
|
|
19916
|
-
placeholder = placeholder.trimEnd();
|
|
19917
|
-
if (!op.params.has(placeholder) && !op.postprocessingParams.has(placeholder)) {
|
|
19918
|
-
throw Error(`Failed to resolve i18n placeholder: ${placeholder}`);
|
|
19919
|
-
}
|
|
19920
|
-
}
|
|
19921
|
-
for (let placeholder in op.message.placeholderToMessage) {
|
|
19922
|
-
placeholder = placeholder.trimEnd();
|
|
19923
|
-
if (!op.params.has(placeholder) && !op.postprocessingParams.has(placeholder)) {
|
|
19924
|
-
throw Error(`Failed to resolve i18n message placeholder: ${placeholder}`);
|
|
19925
|
-
}
|
|
19926
|
-
}
|
|
19927
|
-
}
|
|
19928
20235
|
|
|
19929
20236
|
/**
|
|
19930
20237
|
* Removes text nodes within i18n blocks since they are already hardcoded into the i18n message.
|
|
20238
|
+
* Also, replaces interpolations on these text nodes with i18n expressions of the non-text portions,
|
|
20239
|
+
* which will be applied later.
|
|
19931
20240
|
*/
|
|
19932
|
-
function
|
|
20241
|
+
function convertI18nText(job) {
|
|
19933
20242
|
for (const unit of job.units) {
|
|
19934
20243
|
// Remove all text nodes within i18n blocks, their content is already captured in the i18n
|
|
19935
20244
|
// message.
|
|
@@ -19984,7 +20293,7 @@ function extractI18nText(job) {
|
|
|
19984
20293
|
const expr = op.interpolation.expressions[i];
|
|
19985
20294
|
// For now, this i18nExpression depends on the slot context of the enclosing i18n block.
|
|
19986
20295
|
// Later, we will modify this, and advance to a different point.
|
|
19987
|
-
ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.handle, expr, op.i18nPlaceholders[i], resolutionTime, expr.sourceSpan ?? op.sourceSpan));
|
|
20296
|
+
ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.xref, i18nOp.handle, expr, op.interpolation.i18nPlaceholders[i], resolutionTime, I18nExpressionFor.I18nText, '', expr.sourceSpan ?? op.sourceSpan));
|
|
19988
20297
|
}
|
|
19989
20298
|
OpList.replaceWithMany(op, ops);
|
|
19990
20299
|
break;
|
|
@@ -20522,14 +20831,14 @@ function parseExtractedStyles(job) {
|
|
|
20522
20831
|
if (op.name === 'style') {
|
|
20523
20832
|
const parsedStyles = parse(op.expression.value);
|
|
20524
20833
|
for (let i = 0; i < parsedStyles.length - 1; i += 2) {
|
|
20525
|
-
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1])), op);
|
|
20834
|
+
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1]), null, null, SecurityContext.STYLE), op);
|
|
20526
20835
|
}
|
|
20527
20836
|
OpList.remove(op);
|
|
20528
20837
|
}
|
|
20529
20838
|
else if (op.name === 'class') {
|
|
20530
20839
|
const parsedClasses = op.expression.value.trim().split(/\s+/g);
|
|
20531
20840
|
for (const parsedClass of parsedClasses) {
|
|
20532
|
-
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null), op);
|
|
20841
|
+
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null, null, null, SecurityContext.NONE), op);
|
|
20533
20842
|
}
|
|
20534
20843
|
OpList.remove(op);
|
|
20535
20844
|
}
|
|
@@ -20545,18 +20854,30 @@ function parseExtractedStyles(job) {
|
|
|
20545
20854
|
function removeContentSelectors(job) {
|
|
20546
20855
|
for (const unit of job.units) {
|
|
20547
20856
|
const elements = createOpXrefMap(unit);
|
|
20548
|
-
for (const op of unit.
|
|
20857
|
+
for (const op of unit.ops()) {
|
|
20549
20858
|
switch (op.kind) {
|
|
20550
20859
|
case OpKind.Binding:
|
|
20551
20860
|
const target = lookupInXrefMap(elements, op.target);
|
|
20552
|
-
if (op.name
|
|
20861
|
+
if (isSelectAttribute(op.name) && target.kind === OpKind.Projection) {
|
|
20553
20862
|
OpList.remove(op);
|
|
20554
20863
|
}
|
|
20555
20864
|
break;
|
|
20865
|
+
case OpKind.Projection:
|
|
20866
|
+
// op.attributes is an array of [attr1-name, attr1-value, attr2-name, attr2-value, ...],
|
|
20867
|
+
// find the "select" attribute and remove its name and corresponding value.
|
|
20868
|
+
for (let i = op.attributes.length - 2; i >= 0; i -= 2) {
|
|
20869
|
+
if (isSelectAttribute(op.attributes[i])) {
|
|
20870
|
+
op.attributes.splice(i, 2);
|
|
20871
|
+
}
|
|
20872
|
+
}
|
|
20873
|
+
break;
|
|
20556
20874
|
}
|
|
20557
20875
|
}
|
|
20558
20876
|
}
|
|
20559
20877
|
}
|
|
20878
|
+
function isSelectAttribute(name) {
|
|
20879
|
+
return name.toLowerCase() === 'select';
|
|
20880
|
+
}
|
|
20560
20881
|
/**
|
|
20561
20882
|
* Looks up an element in the given map by xref ID.
|
|
20562
20883
|
*/
|
|
@@ -20676,25 +20997,44 @@ function propagateI18nBlocksToTemplates(unit, subTemplateIndex) {
|
|
|
20676
20997
|
i18nBlock = op;
|
|
20677
20998
|
break;
|
|
20678
20999
|
case OpKind.I18nEnd:
|
|
21000
|
+
// When we exit a root-level i18n block, reset the sub-template index counter.
|
|
21001
|
+
if (i18nBlock.subTemplateIndex === null) {
|
|
21002
|
+
subTemplateIndex = 0;
|
|
21003
|
+
}
|
|
20679
21004
|
i18nBlock = null;
|
|
20680
21005
|
break;
|
|
20681
21006
|
case OpKind.Template:
|
|
20682
|
-
|
|
20683
|
-
|
|
20684
|
-
|
|
20685
|
-
|
|
20686
|
-
|
|
20687
|
-
|
|
20688
|
-
|
|
20689
|
-
|
|
20690
|
-
|
|
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);
|
|
20691
21016
|
}
|
|
20692
|
-
|
|
20693
|
-
subTemplateIndex = propagateI18nBlocksToTemplates(templateView, subTemplateIndex);
|
|
21017
|
+
break;
|
|
20694
21018
|
}
|
|
20695
21019
|
}
|
|
20696
21020
|
return subTemplateIndex;
|
|
20697
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
|
+
}
|
|
20698
21038
|
/**
|
|
20699
21039
|
* Wraps a template view with i18n start and end ops.
|
|
20700
21040
|
*/
|
|
@@ -20860,17 +21200,13 @@ function disableBindings() {
|
|
|
20860
21200
|
function enableBindings() {
|
|
20861
21201
|
return call(Identifiers.enableBindings, [], null);
|
|
20862
21202
|
}
|
|
20863
|
-
function listener(name, handlerFn, sourceSpan) {
|
|
20864
|
-
|
|
20865
|
-
|
|
20866
|
-
|
|
20867
|
-
|
|
20868
|
-
}
|
|
20869
|
-
|
|
20870
|
-
return call(Identifiers.syntheticHostListener, [
|
|
20871
|
-
literal(name),
|
|
20872
|
-
handlerFn,
|
|
20873
|
-
], 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);
|
|
20874
21210
|
}
|
|
20875
21211
|
function pipe(slot, name) {
|
|
20876
21212
|
return call(Identifiers.pipe, [
|
|
@@ -21017,6 +21353,10 @@ function i18n(slot, constIndex, subTemplateIndex) {
|
|
|
21017
21353
|
function i18nEnd() {
|
|
21018
21354
|
return call(Identifiers.i18nEnd, [], null);
|
|
21019
21355
|
}
|
|
21356
|
+
function i18nAttributes(slot, i18nAttributesConfig) {
|
|
21357
|
+
const args = [literal(slot), literal(i18nAttributesConfig)];
|
|
21358
|
+
return call(Identifiers.i18nAttributes, args, null);
|
|
21359
|
+
}
|
|
21020
21360
|
function property(name, expression, sanitizer, sourceSpan) {
|
|
21021
21361
|
const args = [literal(name), expression];
|
|
21022
21362
|
if (sanitizer !== null) {
|
|
@@ -21127,8 +21467,12 @@ function classMapInterpolate(strings, expressions, sourceSpan) {
|
|
|
21127
21467
|
const interpolationArgs = collateInterpolationArgs(strings, expressions);
|
|
21128
21468
|
return callVariadicInstruction(CLASS_MAP_INTERPOLATE_CONFIG, [], interpolationArgs, [], sourceSpan);
|
|
21129
21469
|
}
|
|
21130
|
-
function hostProperty(name, expression, sourceSpan) {
|
|
21131
|
-
|
|
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);
|
|
21132
21476
|
}
|
|
21133
21477
|
function syntheticHostProperty(name, expression, sourceSpan) {
|
|
21134
21478
|
return call(Identifiers.syntheticHostProperty, [literal(name), expression], sourceSpan);
|
|
@@ -21346,14 +21690,12 @@ function callVariadicInstruction(config, baseArgs, interpolationArgs, extraArgs,
|
|
|
21346
21690
|
}
|
|
21347
21691
|
|
|
21348
21692
|
/**
|
|
21349
|
-
* Map of
|
|
21693
|
+
* Map of target resolvers for event listeners.
|
|
21350
21694
|
*/
|
|
21351
|
-
const
|
|
21352
|
-
[
|
|
21353
|
-
[
|
|
21354
|
-
[
|
|
21355
|
-
[SanitizerFn.Script, Identifiers.sanitizeScript],
|
|
21356
|
-
[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],
|
|
21357
21699
|
]);
|
|
21358
21700
|
/**
|
|
21359
21701
|
* Compiles semantic operations across all views and generates output `o.Statement`s with actual
|
|
@@ -21403,6 +21745,12 @@ function reifyCreateOperations(unit, ops) {
|
|
|
21403
21745
|
case OpKind.I18n:
|
|
21404
21746
|
OpList.replace(op, i18n(op.handle.slot, op.messageIndex, op.subTemplateIndex));
|
|
21405
21747
|
break;
|
|
21748
|
+
case OpKind.I18nAttributes:
|
|
21749
|
+
if (op.i18nAttributesConfig === null) {
|
|
21750
|
+
throw new Error(`AssertionError: i18nAttributesConfig was not set`);
|
|
21751
|
+
}
|
|
21752
|
+
OpList.replace(op, i18nAttributes(op.handle.slot, op.i18nAttributesConfig));
|
|
21753
|
+
break;
|
|
21406
21754
|
case OpKind.Template:
|
|
21407
21755
|
if (!(unit instanceof ViewCompilationUnit)) {
|
|
21408
21756
|
throw new Error(`AssertionError: must be compiling a component`);
|
|
@@ -21424,10 +21772,11 @@ function reifyCreateOperations(unit, ops) {
|
|
|
21424
21772
|
break;
|
|
21425
21773
|
case OpKind.Listener:
|
|
21426
21774
|
const listenerFn = reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, op.consumesDollarEvent);
|
|
21427
|
-
const
|
|
21428
|
-
|
|
21429
|
-
|
|
21430
|
-
|
|
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));
|
|
21431
21780
|
break;
|
|
21432
21781
|
case OpKind.Variable:
|
|
21433
21782
|
if (op.variable.name === null) {
|
|
@@ -21590,7 +21939,7 @@ function reifyUpdateOperations(_unit, ops) {
|
|
|
21590
21939
|
OpList.replace(op, syntheticHostProperty(op.name, op.expression, op.sourceSpan));
|
|
21591
21940
|
}
|
|
21592
21941
|
else {
|
|
21593
|
-
OpList.replace(op, hostProperty(op.name, op.expression, op.sourceSpan));
|
|
21942
|
+
OpList.replace(op, hostProperty(op.name, op.expression, op.sanitizer, op.sourceSpan));
|
|
21594
21943
|
}
|
|
21595
21944
|
}
|
|
21596
21945
|
break;
|
|
@@ -21669,8 +22018,6 @@ function reifyIrExpression(expr) {
|
|
|
21669
22018
|
return pipeBind(expr.targetSlot.slot, expr.varOffset, expr.args);
|
|
21670
22019
|
case ExpressionKind.PipeBindingVariadic:
|
|
21671
22020
|
return pipeBindV(expr.targetSlot.slot, expr.varOffset, expr.args);
|
|
21672
|
-
case ExpressionKind.SanitizerExpr:
|
|
21673
|
-
return importExpr(sanitizerIdentifierMap.get(expr.fn));
|
|
21674
22021
|
case ExpressionKind.SlotLiteralExpr:
|
|
21675
22022
|
return literal(expr.slot.slot);
|
|
21676
22023
|
default:
|
|
@@ -21744,6 +22091,31 @@ function removeI18nContexts(job) {
|
|
|
21744
22091
|
}
|
|
21745
22092
|
}
|
|
21746
22093
|
|
|
22094
|
+
/**
|
|
22095
|
+
* i18nAttributes ops will be generated for each i18n attribute. However, not all i18n attribues
|
|
22096
|
+
* will contain dynamic content, and so some of these i18nAttributes ops may be unnecessary.
|
|
22097
|
+
*/
|
|
22098
|
+
function removeUnusedI18nAttributesOps(job) {
|
|
22099
|
+
for (const unit of job.units) {
|
|
22100
|
+
const ownersWithI18nExpressions = new Set();
|
|
22101
|
+
for (const op of unit.update) {
|
|
22102
|
+
switch (op.kind) {
|
|
22103
|
+
case OpKind.I18nExpression:
|
|
22104
|
+
ownersWithI18nExpressions.add(op.i18nOwner);
|
|
22105
|
+
}
|
|
22106
|
+
}
|
|
22107
|
+
for (const op of unit.create) {
|
|
22108
|
+
switch (op.kind) {
|
|
22109
|
+
case OpKind.I18nAttributes:
|
|
22110
|
+
if (ownersWithI18nExpressions.has(op.xref)) {
|
|
22111
|
+
continue;
|
|
22112
|
+
}
|
|
22113
|
+
OpList.remove(op);
|
|
22114
|
+
}
|
|
22115
|
+
}
|
|
22116
|
+
}
|
|
22117
|
+
}
|
|
22118
|
+
|
|
21747
22119
|
/**
|
|
21748
22120
|
* Inside the body of a repeater, certain context variables (such as `$first`) are ambiently
|
|
21749
22121
|
* available. This phase finds those variable usages, and replaces them with the appropriate
|
|
@@ -21875,10 +22247,14 @@ function resolveI18nElementPlaceholders(job) {
|
|
|
21875
22247
|
}
|
|
21876
22248
|
resolvePlaceholdersForView(job, job.root, i18nContexts, elements);
|
|
21877
22249
|
}
|
|
21878
|
-
|
|
22250
|
+
/**
|
|
22251
|
+
* Recursively resolves element and template tag placeholders in the given view.
|
|
22252
|
+
*/
|
|
22253
|
+
function resolvePlaceholdersForView(job, unit, i18nContexts, elements, pendingStructuralDirective) {
|
|
21879
22254
|
// Track the current i18n op and corresponding i18n context op as we step through the creation
|
|
21880
22255
|
// IR.
|
|
21881
22256
|
let currentOps = null;
|
|
22257
|
+
let pendingStructuralDirectiveCloses = new Map();
|
|
21882
22258
|
for (const op of unit.create) {
|
|
21883
22259
|
switch (op.kind) {
|
|
21884
22260
|
case OpKind.I18nStart:
|
|
@@ -21897,14 +22273,14 @@ function resolvePlaceholdersForView(job, unit, i18nContexts, elements) {
|
|
|
21897
22273
|
if (currentOps === null) {
|
|
21898
22274
|
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
21899
22275
|
}
|
|
21900
|
-
|
|
21901
|
-
|
|
21902
|
-
//
|
|
21903
|
-
|
|
21904
|
-
|
|
21905
|
-
flags |= I18nParamValueFlags.CloseTag;
|
|
22276
|
+
recordElementStart(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22277
|
+
// If there is a separate close tag placeholder for this element, save the pending
|
|
22278
|
+
// structural directive so we can pass it to the closing tag as well.
|
|
22279
|
+
if (pendingStructuralDirective && op.i18nPlaceholder.closeName) {
|
|
22280
|
+
pendingStructuralDirectiveCloses.set(op.xref, pendingStructuralDirective);
|
|
21906
22281
|
}
|
|
21907
|
-
|
|
22282
|
+
// Clear out the pending structural directive now that its been accounted for.
|
|
22283
|
+
pendingStructuralDirective = undefined;
|
|
21908
22284
|
}
|
|
21909
22285
|
break;
|
|
21910
22286
|
case OpKind.ElementEnd:
|
|
@@ -21913,55 +22289,195 @@ function resolvePlaceholdersForView(job, unit, i18nContexts, elements) {
|
|
|
21913
22289
|
const startOp = elements.get(op.xref);
|
|
21914
22290
|
if (startOp && startOp.i18nPlaceholder !== undefined) {
|
|
21915
22291
|
if (currentOps === null) {
|
|
21916
|
-
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
22292
|
+
throw Error('AssertionError: i18n tag placeholder should only occur inside an i18n block');
|
|
21917
22293
|
}
|
|
21918
|
-
|
|
21919
|
-
//
|
|
21920
|
-
|
|
21921
|
-
|
|
22294
|
+
recordElementClose(startOp, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirectiveCloses.get(op.xref));
|
|
22295
|
+
// Clear out the pending structural directive close that was accounted for.
|
|
22296
|
+
pendingStructuralDirectiveCloses.delete(op.xref);
|
|
22297
|
+
}
|
|
22298
|
+
break;
|
|
22299
|
+
case OpKind.Projection:
|
|
22300
|
+
// For content projections with i18n placeholders, record its slot value in the params map
|
|
22301
|
+
// under the corresponding tag start and close placeholders.
|
|
22302
|
+
if (op.i18nPlaceholder !== undefined) {
|
|
22303
|
+
if (currentOps === null) {
|
|
22304
|
+
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
21922
22305
|
}
|
|
22306
|
+
recordElementStart(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22307
|
+
recordElementClose(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22308
|
+
// Clear out the pending structural directive now that its been accounted for.
|
|
22309
|
+
pendingStructuralDirective = undefined;
|
|
21923
22310
|
}
|
|
21924
22311
|
break;
|
|
21925
22312
|
case OpKind.Template:
|
|
21926
|
-
|
|
21927
|
-
|
|
21928
|
-
|
|
22313
|
+
const view = job.views.get(op.xref);
|
|
22314
|
+
if (op.i18nPlaceholder === undefined) {
|
|
22315
|
+
// If there is no i18n placeholder, just recurse into the view in case it contains i18n
|
|
22316
|
+
// blocks.
|
|
22317
|
+
resolvePlaceholdersForView(job, view, i18nContexts, elements);
|
|
22318
|
+
}
|
|
22319
|
+
else {
|
|
21929
22320
|
if (currentOps === null) {
|
|
21930
22321
|
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
21931
22322
|
}
|
|
21932
|
-
|
|
21933
|
-
|
|
21934
|
-
|
|
21935
|
-
|
|
21936
|
-
|
|
21937
|
-
|
|
22323
|
+
if (op.templateKind === TemplateKind.Structural) {
|
|
22324
|
+
// If this is a structural directive template, don't record anything yet. Instead pass
|
|
22325
|
+
// the current template as a pending structural directive to be recorded when we find
|
|
22326
|
+
// the element, content, or template it belongs to. This allows us to create combined
|
|
22327
|
+
// values that represent, e.g. the start of a template and element at the same time.
|
|
22328
|
+
resolvePlaceholdersForView(job, view, i18nContexts, elements, op);
|
|
21938
22329
|
}
|
|
21939
|
-
|
|
21940
|
-
|
|
21941
|
-
|
|
21942
|
-
|
|
22330
|
+
else {
|
|
22331
|
+
// If this is some other kind of template, we can record its start, recurse into its
|
|
22332
|
+
// view, and then record its end.
|
|
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;
|
|
21943
22337
|
}
|
|
21944
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
|
+
}
|
|
21945
22354
|
else {
|
|
21946
|
-
|
|
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);
|
|
22381
|
+
pendingStructuralDirective = undefined;
|
|
22382
|
+
}
|
|
21947
22383
|
}
|
|
21948
22384
|
break;
|
|
21949
22385
|
}
|
|
21950
22386
|
}
|
|
21951
22387
|
}
|
|
22388
|
+
/**
|
|
22389
|
+
* Records an i18n param value for the start of an element.
|
|
22390
|
+
*/
|
|
22391
|
+
function recordElementStart(op, i18nContext, i18nBlock, structuralDirective) {
|
|
22392
|
+
const { startName, closeName } = op.i18nPlaceholder;
|
|
22393
|
+
let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.OpenTag;
|
|
22394
|
+
let value = op.handle.slot;
|
|
22395
|
+
// If the element is associated with a structural directive, start it as well.
|
|
22396
|
+
if (structuralDirective !== undefined) {
|
|
22397
|
+
flags |= I18nParamValueFlags.TemplateTag;
|
|
22398
|
+
value = { element: value, template: structuralDirective.handle.slot };
|
|
22399
|
+
}
|
|
22400
|
+
// For self-closing tags, there is no close tag placeholder. Instead, the start tag
|
|
22401
|
+
// placeholder accounts for the start and close of the element.
|
|
22402
|
+
if (!closeName) {
|
|
22403
|
+
flags |= I18nParamValueFlags.CloseTag;
|
|
22404
|
+
}
|
|
22405
|
+
addParam(i18nContext.params, startName, value, i18nBlock.subTemplateIndex, flags);
|
|
22406
|
+
}
|
|
22407
|
+
/**
|
|
22408
|
+
* Records an i18n param value for the closing of an element.
|
|
22409
|
+
*/
|
|
22410
|
+
function recordElementClose(op, i18nContext, i18nBlock, structuralDirective) {
|
|
22411
|
+
const { closeName } = op.i18nPlaceholder;
|
|
22412
|
+
// Self-closing tags don't have a closing tag placeholder, instead the element closing is
|
|
22413
|
+
// recorded via an additional flag on the element start value.
|
|
22414
|
+
if (closeName) {
|
|
22415
|
+
let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.CloseTag;
|
|
22416
|
+
let value = op.handle.slot;
|
|
22417
|
+
// If the element is associated with a structural directive, close it as well.
|
|
22418
|
+
if (structuralDirective !== undefined) {
|
|
22419
|
+
flags |= I18nParamValueFlags.TemplateTag;
|
|
22420
|
+
value = { element: value, template: structuralDirective.handle.slot };
|
|
22421
|
+
}
|
|
22422
|
+
addParam(i18nContext.params, closeName, value, i18nBlock.subTemplateIndex, flags);
|
|
22423
|
+
}
|
|
22424
|
+
}
|
|
22425
|
+
/**
|
|
22426
|
+
* Records an i18n param value for the start of a template.
|
|
22427
|
+
*/
|
|
22428
|
+
function recordTemplateStart(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) {
|
|
22429
|
+
let { startName, closeName } = i18nPlaceholder;
|
|
22430
|
+
let flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.OpenTag;
|
|
22431
|
+
// For self-closing tags, there is no close tag placeholder. Instead, the start tag
|
|
22432
|
+
// placeholder accounts for the start and close of the element.
|
|
22433
|
+
if (!closeName) {
|
|
22434
|
+
flags |= I18nParamValueFlags.CloseTag;
|
|
22435
|
+
}
|
|
22436
|
+
// If the template is associated with a structural directive, record the structural directive's
|
|
22437
|
+
// start first. Since this template must be in the structural directive's view, we can just
|
|
22438
|
+
// directly use the current i18n block's sub-template index.
|
|
22439
|
+
if (structuralDirective !== undefined) {
|
|
22440
|
+
addParam(i18nContext.params, startName, structuralDirective.handle.slot, i18nBlock.subTemplateIndex, flags);
|
|
22441
|
+
}
|
|
22442
|
+
// Record the start of the template. For the sub-template index, pass the index for the template's
|
|
22443
|
+
// view, rather than the current i18n block's index.
|
|
22444
|
+
addParam(i18nContext.params, startName, slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, view), flags);
|
|
22445
|
+
}
|
|
22446
|
+
/**
|
|
22447
|
+
* Records an i18n param value for the closing of a template.
|
|
22448
|
+
*/
|
|
22449
|
+
function recordTemplateClose(job, view, slot, i18nPlaceholder, i18nContext, i18nBlock, structuralDirective) {
|
|
22450
|
+
const { startName, closeName } = i18nPlaceholder;
|
|
22451
|
+
const flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag;
|
|
22452
|
+
// Self-closing tags don't have a closing tag placeholder, instead the template's closing is
|
|
22453
|
+
// recorded via an additional flag on the template start value.
|
|
22454
|
+
if (closeName) {
|
|
22455
|
+
// Record the closing of the template. For the sub-template index, pass the index for the
|
|
22456
|
+
// template's view, rather than the current i18n block's index.
|
|
22457
|
+
addParam(i18nContext.params, closeName, slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, view), flags);
|
|
22458
|
+
// If the template is associated with a structural directive, record the structural directive's
|
|
22459
|
+
// closing after. Since this template must be in the structural directive's view, we can just
|
|
22460
|
+
// directly use the current i18n block's sub-template index.
|
|
22461
|
+
if (structuralDirective !== undefined) {
|
|
22462
|
+
addParam(i18nContext.params, closeName, structuralDirective.handle.slot, i18nBlock.subTemplateIndex, flags);
|
|
22463
|
+
}
|
|
22464
|
+
}
|
|
22465
|
+
}
|
|
21952
22466
|
/**
|
|
21953
22467
|
* Get the subTemplateIndex for the given template op. For template ops, use the subTemplateIndex of
|
|
21954
22468
|
* the child i18n block inside the template.
|
|
21955
22469
|
*/
|
|
21956
|
-
function getSubTemplateIndexForTemplateTag(job, i18nOp,
|
|
21957
|
-
for (const childOp of
|
|
22470
|
+
function getSubTemplateIndexForTemplateTag(job, i18nOp, view) {
|
|
22471
|
+
for (const childOp of view.create) {
|
|
21958
22472
|
if (childOp.kind === OpKind.I18nStart) {
|
|
21959
22473
|
return childOp.subTemplateIndex;
|
|
21960
22474
|
}
|
|
21961
22475
|
}
|
|
21962
22476
|
return i18nOp.subTemplateIndex;
|
|
21963
22477
|
}
|
|
21964
|
-
/**
|
|
22478
|
+
/**
|
|
22479
|
+
* Add a param value to the given params map.
|
|
22480
|
+
*/
|
|
21965
22481
|
function addParam(params, placeholder, value, subTemplateIndex, flags) {
|
|
21966
22482
|
const values = params.get(placeholder) ?? [];
|
|
21967
22483
|
values.push({ value, subTemplateIndex, flags });
|
|
@@ -21987,14 +22503,19 @@ function resolveI18nExpressionPlaceholders(job) {
|
|
|
21987
22503
|
}
|
|
21988
22504
|
}
|
|
21989
22505
|
}
|
|
21990
|
-
// Keep track of the next available expression index
|
|
22506
|
+
// Keep track of the next available expression index for each i18n message.
|
|
21991
22507
|
const expressionIndices = new Map();
|
|
22508
|
+
// Keep track of a reference index for each expression.
|
|
22509
|
+
// We use different references for normal i18n expressio and attribute i18n expressions. This is
|
|
22510
|
+
// because child i18n blocks in templates don't get their own context, since they're rolled into
|
|
22511
|
+
// the translated message of the parent, but they may target a different slot.
|
|
22512
|
+
const referenceIndex = (op) => op.usage === I18nExpressionFor.I18nText ? op.i18nOwner : op.context;
|
|
21992
22513
|
for (const unit of job.units) {
|
|
21993
22514
|
for (const op of unit.update) {
|
|
21994
22515
|
if (op.kind === OpKind.I18nExpression) {
|
|
21995
22516
|
const i18nContext = i18nContexts.get(op.context);
|
|
21996
|
-
const index = expressionIndices.get(op
|
|
21997
|
-
const subTemplateIndex = subTemplateIndicies.get(op.
|
|
22517
|
+
const index = expressionIndices.get(referenceIndex(op)) || 0;
|
|
22518
|
+
const subTemplateIndex = subTemplateIndicies.get(op.i18nOwner) ?? null;
|
|
21998
22519
|
// Add the expression index in the appropriate params map.
|
|
21999
22520
|
const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
|
|
22000
22521
|
i18nContext.params :
|
|
@@ -22006,7 +22527,7 @@ function resolveI18nExpressionPlaceholders(job) {
|
|
|
22006
22527
|
flags: I18nParamValueFlags.ExpressionIndex
|
|
22007
22528
|
});
|
|
22008
22529
|
params.set(op.i18nPlaceholder, values);
|
|
22009
|
-
expressionIndices.set(op
|
|
22530
|
+
expressionIndices.set(referenceIndex(op), index + 1);
|
|
22010
22531
|
}
|
|
22011
22532
|
}
|
|
22012
22533
|
}
|
|
@@ -22164,12 +22685,20 @@ function processLexicalScope(unit, ops, savedView) {
|
|
|
22164
22685
|
}
|
|
22165
22686
|
|
|
22166
22687
|
/**
|
|
22167
|
-
*
|
|
22688
|
+
* Map of security contexts to their sanitizer function.
|
|
22689
|
+
*/
|
|
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.
|
|
22168
22698
|
*/
|
|
22169
|
-
const
|
|
22170
|
-
[SecurityContext.HTML,
|
|
22171
|
-
[SecurityContext.
|
|
22172
|
-
[SecurityContext.RESOURCE_URL, SanitizerFn.ResourceUrl]
|
|
22699
|
+
const trustedValueFns = new Map([
|
|
22700
|
+
[SecurityContext.HTML, Identifiers.trustConstantHtml],
|
|
22701
|
+
[SecurityContext.RESOURCE_URL, Identifiers.trustConstantResourceUrl],
|
|
22173
22702
|
]);
|
|
22174
22703
|
/**
|
|
22175
22704
|
* Resolves sanitization functions for ops that need them.
|
|
@@ -22177,24 +22706,61 @@ const sanitizers = new Map([
|
|
|
22177
22706
|
function resolveSanitizers(job) {
|
|
22178
22707
|
for (const unit of job.units) {
|
|
22179
22708
|
const elements = createOpXrefMap(unit);
|
|
22180
|
-
|
|
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
|
+
}
|
|
22181
22721
|
for (const op of unit.update) {
|
|
22182
22722
|
switch (op.kind) {
|
|
22183
22723
|
case OpKind.Property:
|
|
22184
22724
|
case OpKind.Attribute:
|
|
22185
|
-
|
|
22186
|
-
|
|
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;
|
|
22187
22740
|
// If there was no sanitization function found based on the security context of an
|
|
22188
22741
|
// attribute/property, check whether this attribute/property is one of the
|
|
22189
22742
|
// security-sensitive <iframe> attributes (and that the current element is actually an
|
|
22190
22743
|
// <iframe>).
|
|
22191
22744
|
if (op.sanitizer === null) {
|
|
22192
|
-
|
|
22193
|
-
if (
|
|
22194
|
-
|
|
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;
|
|
22195
22753
|
}
|
|
22196
|
-
|
|
22197
|
-
|
|
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);
|
|
22761
|
+
}
|
|
22762
|
+
if (isIframe && isIframeSecuritySensitiveAttr(op.name)) {
|
|
22763
|
+
op.sanitizer = importExpr(Identifiers.validateIframeAttribute);
|
|
22198
22764
|
}
|
|
22199
22765
|
}
|
|
22200
22766
|
break;
|
|
@@ -22208,6 +22774,22 @@ function resolveSanitizers(job) {
|
|
|
22208
22774
|
function isIframeElement$1(op) {
|
|
22209
22775
|
return op.kind === OpKind.ElementStart && op.tag?.toLowerCase() === 'iframe';
|
|
22210
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
|
+
}
|
|
22211
22793
|
|
|
22212
22794
|
/**
|
|
22213
22795
|
* When inside of a listener, we may need access to one or more enclosing views. Therefore, each
|
|
@@ -22311,6 +22893,8 @@ function allocateSlots(job) {
|
|
|
22311
22893
|
// operation itself, so it can be emitted later.
|
|
22312
22894
|
const childView = job.views.get(op.xref);
|
|
22313
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.
|
|
22314
22898
|
}
|
|
22315
22899
|
}
|
|
22316
22900
|
}
|
|
@@ -22634,6 +23218,8 @@ function countVariables(job) {
|
|
|
22634
23218
|
}
|
|
22635
23219
|
const childView = job.views.get(op.xref);
|
|
22636
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.
|
|
22637
23223
|
}
|
|
22638
23224
|
}
|
|
22639
23225
|
}
|
|
@@ -23153,12 +23739,12 @@ const phases = [
|
|
|
23153
23739
|
{ kind: CompilationJobKind.Tmpl, fn: removeContentSelectors },
|
|
23154
23740
|
{ kind: CompilationJobKind.Host, fn: parseHostStyleProperties },
|
|
23155
23741
|
{ kind: CompilationJobKind.Tmpl, fn: emitNamespaceChanges },
|
|
23156
|
-
{ kind: CompilationJobKind.Both, fn: specializeStyleBindings },
|
|
23157
|
-
{ kind: CompilationJobKind.Both, fn: specializeBindings },
|
|
23158
23742
|
{ kind: CompilationJobKind.Tmpl, fn: propagateI18nBlocks },
|
|
23159
23743
|
{ kind: CompilationJobKind.Tmpl, fn: wrapI18nIcus },
|
|
23160
|
-
{ kind: CompilationJobKind.
|
|
23744
|
+
{ kind: CompilationJobKind.Both, fn: specializeStyleBindings },
|
|
23745
|
+
{ kind: CompilationJobKind.Both, fn: specializeBindings },
|
|
23161
23746
|
{ kind: CompilationJobKind.Both, fn: extractAttributes },
|
|
23747
|
+
{ kind: CompilationJobKind.Tmpl, fn: createI18nContexts },
|
|
23162
23748
|
{ kind: CompilationJobKind.Both, fn: parseExtractedStyles },
|
|
23163
23749
|
{ kind: CompilationJobKind.Tmpl, fn: removeEmptyBindings },
|
|
23164
23750
|
{ kind: CompilationJobKind.Both, fn: collapseSingletonInterpolations },
|
|
@@ -23166,14 +23752,17 @@ const phases = [
|
|
|
23166
23752
|
{ kind: CompilationJobKind.Tmpl, fn: generateConditionalExpressions },
|
|
23167
23753
|
{ kind: CompilationJobKind.Tmpl, fn: createPipes },
|
|
23168
23754
|
{ kind: CompilationJobKind.Tmpl, fn: configureDeferInstructions },
|
|
23169
|
-
{ kind: CompilationJobKind.Tmpl, fn:
|
|
23755
|
+
{ kind: CompilationJobKind.Tmpl, fn: convertI18nText },
|
|
23756
|
+
{ kind: CompilationJobKind.Tmpl, fn: convertI18nBindings },
|
|
23757
|
+
{ kind: CompilationJobKind.Tmpl, fn: removeUnusedI18nAttributesOps },
|
|
23758
|
+
{ kind: CompilationJobKind.Tmpl, fn: assignI18nSlotDependencies },
|
|
23170
23759
|
{ kind: CompilationJobKind.Tmpl, fn: applyI18nExpressions },
|
|
23171
23760
|
{ kind: CompilationJobKind.Tmpl, fn: createVariadicPipes },
|
|
23172
23761
|
{ kind: CompilationJobKind.Both, fn: generatePureLiteralStructures },
|
|
23173
23762
|
{ kind: CompilationJobKind.Tmpl, fn: generateProjectionDefs },
|
|
23174
23763
|
{ kind: CompilationJobKind.Tmpl, fn: generateVariables },
|
|
23175
23764
|
{ kind: CompilationJobKind.Tmpl, fn: saveAndRestoreView },
|
|
23176
|
-
{ kind: CompilationJobKind.
|
|
23765
|
+
{ kind: CompilationJobKind.Both, fn: deleteAnyCasts },
|
|
23177
23766
|
{ kind: CompilationJobKind.Both, fn: resolveDollarEvent },
|
|
23178
23767
|
{ kind: CompilationJobKind.Tmpl, fn: generateRepeaterDerivedVars },
|
|
23179
23768
|
{ kind: CompilationJobKind.Tmpl, fn: generateTrackVariables },
|
|
@@ -23181,7 +23770,7 @@ const phases = [
|
|
|
23181
23770
|
{ kind: CompilationJobKind.Tmpl, fn: resolveDeferTargetNames },
|
|
23182
23771
|
{ kind: CompilationJobKind.Tmpl, fn: optimizeTrackFns },
|
|
23183
23772
|
{ kind: CompilationJobKind.Both, fn: resolveContexts },
|
|
23184
|
-
{ kind: CompilationJobKind.
|
|
23773
|
+
{ kind: CompilationJobKind.Both, fn: resolveSanitizers },
|
|
23185
23774
|
{ kind: CompilationJobKind.Tmpl, fn: liftLocalRefs },
|
|
23186
23775
|
{ kind: CompilationJobKind.Both, fn: generateNullishCoalesceExpressions },
|
|
23187
23776
|
{ kind: CompilationJobKind.Both, fn: expandSafeReads },
|
|
@@ -23196,7 +23785,6 @@ const phases = [
|
|
|
23196
23785
|
{ kind: CompilationJobKind.Tmpl, fn: collectI18nConsts },
|
|
23197
23786
|
{ kind: CompilationJobKind.Tmpl, fn: collectConstExpressions },
|
|
23198
23787
|
{ kind: CompilationJobKind.Both, fn: collectElementConsts },
|
|
23199
|
-
{ kind: CompilationJobKind.Tmpl, fn: assignI18nSlotDependencies },
|
|
23200
23788
|
{ kind: CompilationJobKind.Tmpl, fn: removeI18nContexts },
|
|
23201
23789
|
{ kind: CompilationJobKind.Both, fn: countVariables },
|
|
23202
23790
|
{ kind: CompilationJobKind.Tmpl, fn: generateAdvance },
|
|
@@ -23318,6 +23906,10 @@ function emitHostBindingFunction(job) {
|
|
|
23318
23906
|
}
|
|
23319
23907
|
|
|
23320
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';
|
|
23321
23913
|
/**
|
|
23322
23914
|
* Process a template AST and convert it into a `ComponentCompilation` in the intermediate
|
|
23323
23915
|
* representation.
|
|
@@ -23335,10 +23927,24 @@ function ingestComponent(componentName, template, constantPool, relativeContextF
|
|
|
23335
23927
|
function ingestHostBinding(input, bindingParser, constantPool) {
|
|
23336
23928
|
const job = new HostBindingCompilationJob(input.componentName, constantPool, compatibilityMode);
|
|
23337
23929
|
for (const property of input.properties ?? []) {
|
|
23338
|
-
|
|
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);
|
|
23339
23943
|
}
|
|
23340
23944
|
for (const [name, expr] of Object.entries(input.attributes) ?? []) {
|
|
23341
|
-
|
|
23945
|
+
const securityContexts = bindingParser.calcPossibleSecurityContexts(input.componentSelector, name, true)
|
|
23946
|
+
.filter(context => context !== SecurityContext.NONE);
|
|
23947
|
+
ingestHostAttribute(job, name, expr, securityContexts);
|
|
23342
23948
|
}
|
|
23343
23949
|
for (const event of input.events ?? []) {
|
|
23344
23950
|
ingestHostEvent(job, event);
|
|
@@ -23347,34 +23953,27 @@ function ingestHostBinding(input, bindingParser, constantPool) {
|
|
|
23347
23953
|
}
|
|
23348
23954
|
// TODO: We should refactor the parser to use the same types and structures for host bindings as
|
|
23349
23955
|
// with ordinary components. This would allow us to share a lot more ingestion code.
|
|
23350
|
-
function ingestHostProperty(job, property, isTextAttribute) {
|
|
23956
|
+
function ingestHostProperty(job, property, bindingKind, isTextAttribute, securityContexts) {
|
|
23351
23957
|
let expression;
|
|
23352
23958
|
const ast = property.expression.ast;
|
|
23353
23959
|
if (ast instanceof Interpolation$1) {
|
|
23354
|
-
expression = new Interpolation(ast.strings, ast.expressions.map(expr => convertAst(expr, job, property.sourceSpan)));
|
|
23960
|
+
expression = new Interpolation(ast.strings, ast.expressions.map(expr => convertAst(expr, job, property.sourceSpan)), []);
|
|
23355
23961
|
}
|
|
23356
23962
|
else {
|
|
23357
23963
|
expression = convertAst(ast, job, property.sourceSpan);
|
|
23358
23964
|
}
|
|
23359
|
-
|
|
23360
|
-
// TODO: this should really be handled in the parser.
|
|
23361
|
-
if (property.name.startsWith('attr.')) {
|
|
23362
|
-
property.name = property.name.substring('attr.'.length);
|
|
23363
|
-
bindingKind = BindingKind.Attribute;
|
|
23364
|
-
}
|
|
23365
|
-
if (property.isAnimation) {
|
|
23366
|
-
bindingKind = BindingKind.Animation;
|
|
23367
|
-
}
|
|
23368
|
-
job.root.update.push(createBindingOp(job.root.xref, bindingKind, property.name, expression, null, SecurityContext
|
|
23369
|
-
.NONE /* TODO: what should we pass as security context? Passing NONE for now. */, isTextAttribute, false, 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));
|
|
23370
23966
|
}
|
|
23371
|
-
function ingestHostAttribute(job, name, value) {
|
|
23372
|
-
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,
|
|
23969
|
+
/* TODO */ null,
|
|
23373
23970
|
/* TODO: host attribute source spans */ null);
|
|
23374
23971
|
job.root.update.push(attrBinding);
|
|
23375
23972
|
}
|
|
23376
23973
|
function ingestHostEvent(job, event) {
|
|
23377
|
-
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);
|
|
23378
23977
|
// TODO: Can this be a chain?
|
|
23379
23978
|
eventBinding.handlerOps.push(createStatementOp(new ReturnStatement(convertAst(event.handler.ast, job, event.sourceSpan), event.handlerSpan)));
|
|
23380
23979
|
job.root.create.push(eventBinding);
|
|
@@ -23431,8 +24030,14 @@ function ingestElement(unit, element) {
|
|
|
23431
24030
|
const [namespaceKey, elementName] = splitNsName(element.name);
|
|
23432
24031
|
const startOp = createElementStartOp(elementName, id, namespaceForKey(namespaceKey), element.i18n instanceof TagPlaceholder ? element.i18n : undefined, element.startSourceSpan);
|
|
23433
24032
|
unit.create.push(startOp);
|
|
23434
|
-
|
|
24033
|
+
ingestElementBindings(unit, startOp, element);
|
|
23435
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
|
+
}
|
|
23436
24041
|
ingestNodes(unit, element.children);
|
|
23437
24042
|
// The source span for the end op is typically the element closing tag. However, if no closing tag
|
|
23438
24043
|
// exists, such as in `<input>`, we use the start source span instead. Usually the start and end
|
|
@@ -23442,9 +24047,7 @@ function ingestElement(unit, element) {
|
|
|
23442
24047
|
const endOp = createElementEndOp(id, element.endSourceSpan ?? element.startSourceSpan);
|
|
23443
24048
|
unit.create.push(endOp);
|
|
23444
24049
|
// If there is an i18n message associated with this element, insert i18n start and end ops.
|
|
23445
|
-
if (
|
|
23446
|
-
const i18nBlockId = unit.job.allocateXrefId();
|
|
23447
|
-
OpList.insertAfter(createI18nStartOp(i18nBlockId, element.i18n), startOp);
|
|
24050
|
+
if (i18nBlockId !== null) {
|
|
23448
24051
|
OpList.insertBefore(createI18nEndOp(i18nBlockId), endOp);
|
|
23449
24052
|
}
|
|
23450
24053
|
}
|
|
@@ -23467,10 +24070,11 @@ function ingestTemplate(unit, tmpl) {
|
|
|
23467
24070
|
const functionNameSuffix = tagNameWithoutNamespace === null ?
|
|
23468
24071
|
'' :
|
|
23469
24072
|
prefixWithNamespace(tagNameWithoutNamespace, namespace);
|
|
23470
|
-
const
|
|
23471
|
-
|
|
23472
|
-
|
|
23473
|
-
|
|
24073
|
+
const templateKind = isPlainTemplate(tmpl) ? TemplateKind.NgTemplate : TemplateKind.Structural;
|
|
24074
|
+
const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan);
|
|
24075
|
+
unit.create.push(templateOp);
|
|
24076
|
+
ingestTemplateBindings(unit, templateOp, tmpl, templateKind);
|
|
24077
|
+
ingestReferences(templateOp, tmpl);
|
|
23474
24078
|
ingestNodes(childView, tmpl.children);
|
|
23475
24079
|
for (const { name, value } of tmpl.variables) {
|
|
23476
24080
|
childView.contextVariables.set(name, value !== '' ? value : '$implicit');
|
|
@@ -23478,19 +24082,24 @@ function ingestTemplate(unit, tmpl) {
|
|
|
23478
24082
|
// If this is a plain template and there is an i18n message associated with it, insert i18n start
|
|
23479
24083
|
// and end ops. For structural directive templates, the i18n ops will be added when ingesting the
|
|
23480
24084
|
// element/template the directive is placed on.
|
|
23481
|
-
if (
|
|
24085
|
+
if (templateKind === TemplateKind.NgTemplate && tmpl.i18n instanceof Message) {
|
|
23482
24086
|
const id = unit.job.allocateXrefId();
|
|
23483
24087
|
OpList.insertAfter(createI18nStartOp(id, tmpl.i18n), childView.create.head);
|
|
23484
24088
|
OpList.insertBefore(createI18nEndOp(id), childView.create.tail);
|
|
23485
24089
|
}
|
|
23486
24090
|
}
|
|
23487
24091
|
/**
|
|
23488
|
-
* Ingest a
|
|
24092
|
+
* Ingest a content node from the AST into the given `ViewCompilation`.
|
|
23489
24093
|
*/
|
|
23490
24094
|
function ingestContent(unit, content) {
|
|
23491
|
-
|
|
24095
|
+
if (content.i18n !== undefined && !(content.i18n instanceof TagPlaceholder)) {
|
|
24096
|
+
throw Error(`Unhandled i18n metadata type for element: ${content.i18n.constructor.name}`);
|
|
24097
|
+
}
|
|
24098
|
+
const attrs = content.attributes.flatMap(a => [a.name, a.value]);
|
|
24099
|
+
const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.i18n, attrs, content.sourceSpan);
|
|
23492
24100
|
for (const attr of content.attributes) {
|
|
23493
|
-
|
|
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));
|
|
23494
24103
|
}
|
|
23495
24104
|
unit.create.push(op);
|
|
23496
24105
|
}
|
|
@@ -23515,6 +24124,7 @@ function ingestBoundText(unit, text, i18nPlaceholders) {
|
|
|
23515
24124
|
throw Error(`Unhandled i18n metadata type for text interpolation: ${text.i18n?.constructor.name}`);
|
|
23516
24125
|
}
|
|
23517
24126
|
if (i18nPlaceholders === undefined) {
|
|
24127
|
+
// TODO: We probably can just use the placeholders field, instead of walking the AST.
|
|
23518
24128
|
i18nPlaceholders = text.i18n instanceof Container ?
|
|
23519
24129
|
text.i18n.children
|
|
23520
24130
|
.filter((node) => node instanceof Placeholder)
|
|
@@ -23530,7 +24140,7 @@ function ingestBoundText(unit, text, i18nPlaceholders) {
|
|
|
23530
24140
|
// interpolation. We copy that behavior in compatibility mode.
|
|
23531
24141
|
// TODO: is it actually correct to generate these extra maps in modern mode?
|
|
23532
24142
|
const baseSourceSpan = unit.job.compatibility ? null : text.sourceSpan;
|
|
23533
|
-
unit.update.push(createInterpolateTextOp(textXref, new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, unit.job, baseSourceSpan))
|
|
24143
|
+
unit.update.push(createInterpolateTextOp(textXref, new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, unit.job, baseSourceSpan)), i18nPlaceholders), text.sourceSpan));
|
|
23534
24144
|
}
|
|
23535
24145
|
/**
|
|
23536
24146
|
* Ingest an `@if` block into the given `ViewCompilation`.
|
|
@@ -23551,14 +24161,21 @@ function ingestIfBlock(unit, ifBlock) {
|
|
|
23551
24161
|
if (ifCase.expressionAlias !== null) {
|
|
23552
24162
|
cView.contextVariables.set(ifCase.expressionAlias.name, CTX_REF);
|
|
23553
24163
|
}
|
|
23554
|
-
|
|
23555
|
-
|
|
24164
|
+
let ifCaseI18nMeta = undefined;
|
|
24165
|
+
if (ifCase.i18n !== undefined) {
|
|
24166
|
+
if (!(ifCase.i18n instanceof BlockPlaceholder)) {
|
|
24167
|
+
throw Error(`Unhandled i18n metadata type for if block: ${ifCase.i18n?.constructor.name}`);
|
|
24168
|
+
}
|
|
24169
|
+
ifCaseI18nMeta = ifCase.i18n;
|
|
24170
|
+
}
|
|
24171
|
+
const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, tagName, 'Conditional', Namespace.HTML, ifCaseI18nMeta, ifCase.sourceSpan);
|
|
24172
|
+
unit.create.push(templateOp);
|
|
23556
24173
|
if (firstXref === null) {
|
|
23557
24174
|
firstXref = cView.xref;
|
|
23558
|
-
firstSlotHandle =
|
|
24175
|
+
firstSlotHandle = templateOp.handle;
|
|
23559
24176
|
}
|
|
23560
24177
|
const caseExpr = ifCase.expression ? convertAst(ifCase.expression, unit.job, null) : null;
|
|
23561
|
-
const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr,
|
|
24178
|
+
const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, templateOp.xref, templateOp.handle, ifCase.expressionAlias);
|
|
23562
24179
|
conditions.push(conditionalCaseExpr);
|
|
23563
24180
|
ingestNodes(cView, ifCase.children);
|
|
23564
24181
|
}
|
|
@@ -23574,29 +24191,39 @@ function ingestSwitchBlock(unit, switchBlock) {
|
|
|
23574
24191
|
let conditions = [];
|
|
23575
24192
|
for (const switchCase of switchBlock.cases) {
|
|
23576
24193
|
const cView = unit.job.allocateView(unit.xref);
|
|
23577
|
-
|
|
23578
|
-
|
|
24194
|
+
let switchCaseI18nMeta = undefined;
|
|
24195
|
+
if (switchCase.i18n !== undefined) {
|
|
24196
|
+
if (!(switchCase.i18n instanceof BlockPlaceholder)) {
|
|
24197
|
+
throw Error(`Unhandled i18n metadata type for switch block: ${switchCase.i18n?.constructor.name}`);
|
|
24198
|
+
}
|
|
24199
|
+
switchCaseI18nMeta = switchCase.i18n;
|
|
24200
|
+
}
|
|
24201
|
+
const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, null, 'Case', Namespace.HTML, switchCaseI18nMeta, switchCase.sourceSpan);
|
|
24202
|
+
unit.create.push(templateOp);
|
|
23579
24203
|
if (firstXref === null) {
|
|
23580
24204
|
firstXref = cView.xref;
|
|
23581
|
-
firstSlotHandle =
|
|
24205
|
+
firstSlotHandle = templateOp.handle;
|
|
23582
24206
|
}
|
|
23583
24207
|
const caseExpr = switchCase.expression ?
|
|
23584
24208
|
convertAst(switchCase.expression, unit.job, switchBlock.startSourceSpan) :
|
|
23585
24209
|
null;
|
|
23586
|
-
const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr,
|
|
24210
|
+
const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, templateOp.xref, templateOp.handle);
|
|
23587
24211
|
conditions.push(conditionalCaseExpr);
|
|
23588
24212
|
ingestNodes(cView, switchCase.children);
|
|
23589
24213
|
}
|
|
23590
24214
|
const conditional = createConditionalOp(firstXref, firstSlotHandle, convertAst(switchBlock.expression, unit.job, null), conditions, switchBlock.sourceSpan);
|
|
23591
24215
|
unit.update.push(conditional);
|
|
23592
24216
|
}
|
|
23593
|
-
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
|
+
}
|
|
23594
24221
|
if (children === undefined) {
|
|
23595
24222
|
return null;
|
|
23596
24223
|
}
|
|
23597
24224
|
const secondaryView = unit.job.allocateView(unit.xref);
|
|
23598
24225
|
ingestNodes(secondaryView, children);
|
|
23599
|
-
const templateOp = createTemplateOp(secondaryView.xref, null, `Defer${suffix}`, Namespace.HTML,
|
|
24226
|
+
const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, i18nMeta, sourceSpan);
|
|
23600
24227
|
unit.create.push(templateOp);
|
|
23601
24228
|
return templateOp;
|
|
23602
24229
|
}
|
|
@@ -23606,10 +24233,10 @@ function ingestDeferBlock(unit, deferBlock) {
|
|
|
23606
24233
|
throw new Error(`AssertionError: unable to find metadata for deferred block`);
|
|
23607
24234
|
}
|
|
23608
24235
|
// Generate the defer main view and all secondary views.
|
|
23609
|
-
const main = ingestDeferView(unit, '', deferBlock.children, deferBlock.sourceSpan);
|
|
23610
|
-
const loading = ingestDeferView(unit, 'Loading', deferBlock.loading?.children, deferBlock.loading?.sourceSpan);
|
|
23611
|
-
const placeholder = ingestDeferView(unit, 'Placeholder', deferBlock.placeholder?.children, deferBlock.placeholder?.sourceSpan);
|
|
23612
|
-
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);
|
|
23613
24240
|
// Create the main defer op, and ops for all secondary views.
|
|
23614
24241
|
const deferXref = unit.job.allocateXrefId();
|
|
23615
24242
|
const deferOp = createDeferOp(deferXref, main.xref, main.handle, blockMeta, deferBlock.sourceSpan);
|
|
@@ -23691,12 +24318,7 @@ function ingestIcu(unit, icu) {
|
|
|
23691
24318
|
const xref = unit.job.allocateXrefId();
|
|
23692
24319
|
const icuNode = icu.i18n.nodes[0];
|
|
23693
24320
|
unit.create.push(createIcuStartOp(xref, icu.i18n, icuFromI18nMessage(icu.i18n).name, null));
|
|
23694
|
-
const
|
|
23695
|
-
if (expressionPlaceholder === undefined || icu.vars[expressionPlaceholder] === undefined) {
|
|
23696
|
-
throw Error('ICU should have a text binding');
|
|
23697
|
-
}
|
|
23698
|
-
ingestBoundText(unit, icu.vars[expressionPlaceholder], [expressionPlaceholder]);
|
|
23699
|
-
for (const [placeholder, text] of Object.entries(icu.placeholders)) {
|
|
24321
|
+
for (const [placeholder, text] of Object.entries({ ...icu.vars, ...icu.placeholders })) {
|
|
23700
24322
|
if (text instanceof BoundText) {
|
|
23701
24323
|
ingestBoundText(unit, text, [placeholder]);
|
|
23702
24324
|
}
|
|
@@ -23748,8 +24370,17 @@ function ingestForBlock(unit, forBlock) {
|
|
|
23748
24370
|
$odd: forBlock.contextVariables.$odd.name,
|
|
23749
24371
|
$implicit: forBlock.item.name,
|
|
23750
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;
|
|
23751
24382
|
const tagName = ingestControlFlowInsertionPoint(unit, repeaterView.xref, forBlock);
|
|
23752
|
-
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);
|
|
23753
24384
|
unit.create.push(repeaterCreate);
|
|
23754
24385
|
const expression = convertAst(forBlock.expression, unit.job, convertSourceSpan(forBlock.expression.span, forBlock.sourceSpan));
|
|
23755
24386
|
const repeater = createRepeaterOp(repeaterCreate.xref, repeaterCreate.handle, expression, forBlock.sourceSpan);
|
|
@@ -23792,6 +24423,16 @@ function convertAst(ast, job, baseSourceSpan) {
|
|
|
23792
24423
|
else if (ast instanceof LiteralPrimitive) {
|
|
23793
24424
|
return literal(ast.value, undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
23794
24425
|
}
|
|
24426
|
+
else if (ast instanceof Unary) {
|
|
24427
|
+
switch (ast.operator) {
|
|
24428
|
+
case '+':
|
|
24429
|
+
return new UnaryOperatorExpr(UnaryOperator.Plus, convertAst(ast.expr, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
24430
|
+
case '-':
|
|
24431
|
+
return new UnaryOperatorExpr(UnaryOperator.Minus, convertAst(ast.expr, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
24432
|
+
default:
|
|
24433
|
+
throw new Error(`AssertionError: unknown unary operator ${ast.operator}`);
|
|
24434
|
+
}
|
|
24435
|
+
}
|
|
23795
24436
|
else if (ast instanceof Binary) {
|
|
23796
24437
|
const operator = BINARY_OPERATORS.get(ast.operation);
|
|
23797
24438
|
if (operator === undefined) {
|
|
@@ -23850,10 +24491,34 @@ function convertAst(ast, job, baseSourceSpan) {
|
|
|
23850
24491
|
else if (ast instanceof EmptyExpr$1) {
|
|
23851
24492
|
return new EmptyExpr(convertSourceSpan(ast.span, baseSourceSpan));
|
|
23852
24493
|
}
|
|
24494
|
+
else if (ast instanceof PrefixNot) {
|
|
24495
|
+
return not(convertAst(ast.expression, job, baseSourceSpan), convertSourceSpan(ast.span, baseSourceSpan));
|
|
24496
|
+
}
|
|
23853
24497
|
else {
|
|
23854
24498
|
throw new Error(`Unhandled expression type "${ast.constructor.name}" in file "${baseSourceSpan?.start.file.url}"`);
|
|
23855
24499
|
}
|
|
23856
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
|
+
]);
|
|
23857
24522
|
/**
|
|
23858
24523
|
* Checks whether the given template is a plain ng-template (as opposed to another kind of template
|
|
23859
24524
|
* such as a structural directive template or control flow template). This is checked based on the
|
|
@@ -23872,128 +24537,184 @@ function convertAst(ast, job, baseSourceSpan) {
|
|
|
23872
24537
|
* | `<ng-template *ngIf>` (structural) | null |
|
|
23873
24538
|
*/
|
|
23874
24539
|
function isPlainTemplate(tmpl) {
|
|
23875
|
-
return splitNsName(tmpl.tagName ?? '')[1] ===
|
|
24540
|
+
return splitNsName(tmpl.tagName ?? '')[1] === NG_TEMPLATE_TAG_NAME$1;
|
|
23876
24541
|
}
|
|
23877
24542
|
/**
|
|
23878
|
-
*
|
|
23879
|
-
* to their IR representation.
|
|
24543
|
+
* Ensures that the i18nMeta, if provided, is an i18n.Message.
|
|
23880
24544
|
*/
|
|
23881
|
-
function
|
|
23882
|
-
|
|
23883
|
-
|
|
23884
|
-
|
|
23885
|
-
|
|
23886
|
-
|
|
23887
|
-
}
|
|
23888
|
-
const templateAttrFlags = flags | BindingFlags.BindingTargetsTemplate | BindingFlags.IsStructuralTemplateAttribute;
|
|
23889
|
-
for (const attr of element.templateAttrs) {
|
|
23890
|
-
if (attr instanceof TextAttribute) {
|
|
23891
|
-
ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, templateAttrFlags | BindingFlags.TextValue);
|
|
23892
|
-
}
|
|
23893
|
-
else {
|
|
23894
|
-
ingestBinding(unit, op.xref, attr.name, attr.value, attr.type, attr.unit, attr.securityContext, attr.sourceSpan, templateAttrFlags);
|
|
23895
|
-
}
|
|
23896
|
-
}
|
|
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}`);
|
|
23897
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();
|
|
23898
24560
|
for (const attr of element.attributes) {
|
|
23899
|
-
//
|
|
23900
|
-
|
|
23901
|
-
|
|
23902
|
-
ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, flags | BindingFlags.TextValue);
|
|
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));
|
|
23903
24564
|
}
|
|
23904
24565
|
for (const input of element.inputs) {
|
|
23905
|
-
|
|
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));
|
|
23906
24568
|
}
|
|
24569
|
+
unit.create.push(bindings.filter((b) => b?.kind === OpKind.ExtractedAttribute));
|
|
24570
|
+
unit.update.push(bindings.filter((b) => b?.kind === OpKind.Binding));
|
|
23907
24571
|
for (const output of element.outputs) {
|
|
23908
|
-
|
|
23909
|
-
|
|
23910
|
-
if (output.phase === null) {
|
|
23911
|
-
throw Error('Animation listener should have a phase');
|
|
23912
|
-
}
|
|
23913
|
-
}
|
|
23914
|
-
if (element instanceof Template && !isPlainTemplate(element)) {
|
|
23915
|
-
unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null));
|
|
23916
|
-
continue;
|
|
23917
|
-
}
|
|
23918
|
-
listenerOp = createListenerOp(op.xref, op.handle, output.name, op.tag, output.phase, false, output.sourceSpan);
|
|
23919
|
-
// if output.handler is a chain, then push each statement from the chain separately, and
|
|
23920
|
-
// return the last one?
|
|
23921
|
-
let handlerExprs;
|
|
23922
|
-
let handler = output.handler;
|
|
23923
|
-
if (handler instanceof ASTWithSource) {
|
|
23924
|
-
handler = handler.ast;
|
|
24572
|
+
if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) {
|
|
24573
|
+
throw Error('Animation listener should have a phase');
|
|
23925
24574
|
}
|
|
23926
|
-
|
|
23927
|
-
|
|
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));
|
|
23928
24593
|
}
|
|
23929
24594
|
else {
|
|
23930
|
-
|
|
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));
|
|
23931
24596
|
}
|
|
23932
|
-
|
|
23933
|
-
|
|
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');
|
|
23934
24612
|
}
|
|
23935
|
-
|
|
23936
|
-
|
|
23937
|
-
|
|
23938
|
-
|
|
23939
|
-
|
|
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));
|
|
24615
|
+
}
|
|
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));
|
|
23940
24621
|
}
|
|
23941
|
-
listenerOp.handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
|
|
23942
|
-
unit.create.push(listenerOp);
|
|
23943
|
-
}
|
|
23944
|
-
}
|
|
23945
|
-
const BINDING_KINDS = new Map([
|
|
23946
|
-
[0 /* e.BindingType.Property */, BindingKind.Property],
|
|
23947
|
-
[1 /* e.BindingType.Attribute */, BindingKind.Attribute],
|
|
23948
|
-
[2 /* e.BindingType.Class */, BindingKind.ClassName],
|
|
23949
|
-
[3 /* e.BindingType.Style */, BindingKind.StyleProperty],
|
|
23950
|
-
[4 /* e.BindingType.Animation */, BindingKind.Animation],
|
|
23951
|
-
]);
|
|
23952
|
-
var BindingFlags;
|
|
23953
|
-
(function (BindingFlags) {
|
|
23954
|
-
BindingFlags[BindingFlags["None"] = 0] = "None";
|
|
23955
|
-
/**
|
|
23956
|
-
* The binding is to a static text literal and not to an expression.
|
|
23957
|
-
*/
|
|
23958
|
-
BindingFlags[BindingFlags["TextValue"] = 1] = "TextValue";
|
|
23959
|
-
/**
|
|
23960
|
-
* The binding belongs to the `<ng-template>` side of a `t.Template`.
|
|
23961
|
-
*/
|
|
23962
|
-
BindingFlags[BindingFlags["BindingTargetsTemplate"] = 2] = "BindingTargetsTemplate";
|
|
23963
|
-
/**
|
|
23964
|
-
* The binding is on a structural directive.
|
|
23965
|
-
*/
|
|
23966
|
-
BindingFlags[BindingFlags["IsStructuralTemplateAttribute"] = 4] = "IsStructuralTemplateAttribute";
|
|
23967
|
-
/**
|
|
23968
|
-
* The binding is on a `t.Template`.
|
|
23969
|
-
*/
|
|
23970
|
-
BindingFlags[BindingFlags["OnNgTemplateElement"] = 8] = "OnNgTemplateElement";
|
|
23971
|
-
})(BindingFlags || (BindingFlags = {}));
|
|
23972
|
-
function ingestBinding(view, xref, name, value, type, unit, securityContext, sourceSpan, flags) {
|
|
23973
|
-
if (value instanceof ASTWithSource) {
|
|
23974
|
-
value = value.ast;
|
|
23975
24622
|
}
|
|
23976
|
-
|
|
23977
|
-
|
|
23978
|
-
|
|
23979
|
-
// created.
|
|
23980
|
-
view.create.push(createExtractedAttributeOp(xref, BindingKind.Property, name, null));
|
|
23981
|
-
return;
|
|
24623
|
+
// TODO: Perhaps we could do this in a phase? (It likely wouldn't change the slot indices.)
|
|
24624
|
+
if (bindings.some(b => b?.i18nMessage) !== null) {
|
|
24625
|
+
unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
|
|
23982
24626
|
}
|
|
23983
|
-
|
|
23984
|
-
|
|
23985
|
-
|
|
23986
|
-
|
|
23987
|
-
|
|
24627
|
+
}
|
|
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;
|
|
24676
|
+
}
|
|
23988
24677
|
}
|
|
23989
|
-
|
|
23990
|
-
|
|
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;
|
|
24699
|
+
}
|
|
23991
24700
|
}
|
|
23992
|
-
|
|
23993
|
-
|
|
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.');
|
|
23994
24709
|
}
|
|
23995
|
-
const
|
|
23996
|
-
|
|
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;
|
|
23997
24718
|
}
|
|
23998
24719
|
/**
|
|
23999
24720
|
* Process all of the local references on an element-like structure in the template AST and
|
|
@@ -24081,11 +24802,12 @@ function ingestControlFlowInsertionPoint(unit, xref, node) {
|
|
|
24081
24802
|
// and they can be used in directive matching (in the case of `Template.templateAttrs`).
|
|
24082
24803
|
if (root !== null) {
|
|
24083
24804
|
for (const attr of root.attributes) {
|
|
24084
|
-
|
|
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));
|
|
24085
24807
|
}
|
|
24086
24808
|
const tagName = root instanceof Element$1 ? root.name : root.tagName;
|
|
24087
24809
|
// Don't pass along `ng-template` tag name since it enables directive matching.
|
|
24088
|
-
return tagName ===
|
|
24810
|
+
return tagName === NG_TEMPLATE_TAG_NAME$1 ? null : tagName;
|
|
24089
24811
|
}
|
|
24090
24812
|
return null;
|
|
24091
24813
|
}
|
|
@@ -27164,7 +27886,7 @@ class TemplateDefinitionBuilder {
|
|
|
27164
27886
|
else {
|
|
27165
27887
|
// ... otherwise we need to activate post-processing
|
|
27166
27888
|
// to replace ICU placeholders with proper values
|
|
27167
|
-
const placeholder = wrapI18nPlaceholder(`${I18N_ICU_MAPPING_PREFIX}${key}`);
|
|
27889
|
+
const placeholder = wrapI18nPlaceholder(`${I18N_ICU_MAPPING_PREFIX$1}${key}`);
|
|
27168
27890
|
params[key] = literal(placeholder);
|
|
27169
27891
|
icuMapping[key] = literalArr(refs);
|
|
27170
27892
|
}
|
|
@@ -28784,24 +29506,6 @@ class TrackByBindingScope extends BindingScope {
|
|
|
28784
29506
|
return this.componentAccessCount;
|
|
28785
29507
|
}
|
|
28786
29508
|
}
|
|
28787
|
-
/**
|
|
28788
|
-
* Creates a `CssSelector` given a tag name and a map of attributes
|
|
28789
|
-
*/
|
|
28790
|
-
function createCssSelector(elementName, attributes) {
|
|
28791
|
-
const cssSelector = new CssSelector();
|
|
28792
|
-
const elementNameNoNs = splitNsName(elementName)[1];
|
|
28793
|
-
cssSelector.setElement(elementNameNoNs);
|
|
28794
|
-
Object.getOwnPropertyNames(attributes).forEach((name) => {
|
|
28795
|
-
const nameNoNs = splitNsName(name)[1];
|
|
28796
|
-
const value = attributes[name];
|
|
28797
|
-
cssSelector.addAttribute(nameNoNs, value);
|
|
28798
|
-
if (name.toLowerCase() === 'class') {
|
|
28799
|
-
const classes = value.trim().split(/\s+/);
|
|
28800
|
-
classes.forEach(className => cssSelector.addClassName(className));
|
|
28801
|
-
}
|
|
28802
|
-
});
|
|
28803
|
-
return cssSelector;
|
|
28804
|
-
}
|
|
28805
29509
|
/**
|
|
28806
29510
|
* Creates an array of expressions out of an `ngProjectAs` attributes
|
|
28807
29511
|
* which can be added to the instruction parameters.
|
|
@@ -29530,6 +30234,7 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
|
|
|
29530
30234
|
}
|
|
29531
30235
|
const hostJob = ingestHostBinding({
|
|
29532
30236
|
componentName: name,
|
|
30237
|
+
componentSelector: selector,
|
|
29533
30238
|
properties: bindings,
|
|
29534
30239
|
events: eventBindings,
|
|
29535
30240
|
attributes: hostBindingsMetadata.attributes,
|
|
@@ -29542,6 +30247,8 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
|
|
|
29542
30247
|
}
|
|
29543
30248
|
return emitHostBindingFunction(hostJob);
|
|
29544
30249
|
}
|
|
30250
|
+
let bindingId = 0;
|
|
30251
|
+
const getNextBindingId = () => `${bindingId++}`;
|
|
29545
30252
|
const bindingContext = variable(CONTEXT_NAME);
|
|
29546
30253
|
const styleBuilder = new StylingBuilder(bindingContext);
|
|
29547
30254
|
const { styleAttr, classAttr } = hostBindingsMetadata.specialAttributes;
|
|
@@ -29594,7 +30301,7 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
|
|
|
29594
30301
|
for (const binding of allOtherBindings) {
|
|
29595
30302
|
// resolve literal arrays and literal objects
|
|
29596
30303
|
const value = binding.expression.visit(getValueConverter());
|
|
29597
|
-
const bindingExpr = bindingFn(bindingContext, value);
|
|
30304
|
+
const bindingExpr = bindingFn(bindingContext, value, getNextBindingId);
|
|
29598
30305
|
const { bindingName, instruction, isAttribute } = getBindingNameAndInstruction(binding);
|
|
29599
30306
|
const securityContexts = bindingParser.calcPossibleSecurityContexts(selector, bindingName, isAttribute)
|
|
29600
30307
|
.filter(context => context !== SecurityContext.NONE);
|
|
@@ -29673,10 +30380,12 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
|
|
|
29673
30380
|
// at the top of this method when all the input bindings were counted.
|
|
29674
30381
|
totalHostVarsCount +=
|
|
29675
30382
|
Math.max(call.allocateBindingSlots - MIN_STYLING_BINDING_SLOTS_REQUIRED, 0);
|
|
30383
|
+
const { params, stmts } = convertStylingCall(call, bindingContext, bindingFn, getNextBindingId);
|
|
30384
|
+
updateVariables.push(...stmts);
|
|
29676
30385
|
updateInstructions.push({
|
|
29677
30386
|
reference: instruction.reference,
|
|
29678
|
-
paramsOrFn:
|
|
29679
|
-
span: null
|
|
30387
|
+
paramsOrFn: params,
|
|
30388
|
+
span: null,
|
|
29680
30389
|
});
|
|
29681
30390
|
}
|
|
29682
30391
|
});
|
|
@@ -29697,11 +30406,19 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
|
|
|
29697
30406
|
}
|
|
29698
30407
|
return null;
|
|
29699
30408
|
}
|
|
29700
|
-
function bindingFn(implicit, value) {
|
|
29701
|
-
return convertPropertyBinding(null, implicit, value,
|
|
30409
|
+
function bindingFn(implicit, value, getNextBindingIdFn) {
|
|
30410
|
+
return convertPropertyBinding(null, implicit, value, getNextBindingIdFn());
|
|
29702
30411
|
}
|
|
29703
|
-
function convertStylingCall(call, bindingContext, bindingFn) {
|
|
29704
|
-
|
|
30412
|
+
function convertStylingCall(call, bindingContext, bindingFn, getNextBindingIdFn) {
|
|
30413
|
+
const stmts = [];
|
|
30414
|
+
const params = call.params(value => {
|
|
30415
|
+
const result = bindingFn(bindingContext, value, getNextBindingIdFn);
|
|
30416
|
+
if (Array.isArray(result.stmts) && result.stmts.length > 0) {
|
|
30417
|
+
stmts.push(...result.stmts);
|
|
30418
|
+
}
|
|
30419
|
+
return result.currValExpr;
|
|
30420
|
+
});
|
|
30421
|
+
return { params, stmts };
|
|
29705
30422
|
}
|
|
29706
30423
|
function getBindingNameAndInstruction(binding) {
|
|
29707
30424
|
let bindingName = binding.name;
|
|
@@ -30140,15 +30857,15 @@ class DirectiveBinder {
|
|
|
30140
30857
|
template.forEach(node => node.visit(this));
|
|
30141
30858
|
}
|
|
30142
30859
|
visitElement(element) {
|
|
30143
|
-
this.visitElementOrTemplate(element
|
|
30860
|
+
this.visitElementOrTemplate(element);
|
|
30144
30861
|
}
|
|
30145
30862
|
visitTemplate(template) {
|
|
30146
|
-
this.visitElementOrTemplate(
|
|
30863
|
+
this.visitElementOrTemplate(template);
|
|
30147
30864
|
}
|
|
30148
|
-
visitElementOrTemplate(
|
|
30865
|
+
visitElementOrTemplate(node) {
|
|
30149
30866
|
// First, determine the HTML shape of the node for the purpose of directive matching.
|
|
30150
30867
|
// Do this by building up a `CssSelector` for the node.
|
|
30151
|
-
const cssSelector =
|
|
30868
|
+
const cssSelector = createCssSelectorFromNode(node);
|
|
30152
30869
|
// Next, use the `SelectorMatcher` to get the list of directives on the node.
|
|
30153
30870
|
const directives = [];
|
|
30154
30871
|
this.matcher.match(cssSelector, (_selector, results) => directives.push(...results));
|
|
@@ -31277,7 +31994,7 @@ function publishFacade(global) {
|
|
|
31277
31994
|
* @description
|
|
31278
31995
|
* Entry point for all public APIs of the compiler package.
|
|
31279
31996
|
*/
|
|
31280
|
-
const VERSION = new Version('17.0.
|
|
31997
|
+
const VERSION = new Version('17.0.7');
|
|
31281
31998
|
|
|
31282
31999
|
class CompilerConfig {
|
|
31283
32000
|
constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
|
|
@@ -32843,7 +33560,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
|
|
|
32843
33560
|
function compileDeclareClassMetadata(metadata) {
|
|
32844
33561
|
const definitionMap = new DefinitionMap();
|
|
32845
33562
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
|
|
32846
|
-
definitionMap.set('version', literal('17.0.
|
|
33563
|
+
definitionMap.set('version', literal('17.0.7'));
|
|
32847
33564
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
32848
33565
|
definitionMap.set('type', metadata.type);
|
|
32849
33566
|
definitionMap.set('decorators', metadata.decorators);
|
|
@@ -32951,7 +33668,7 @@ function createDirectiveDefinitionMap(meta) {
|
|
|
32951
33668
|
// in 16.1 is actually used.
|
|
32952
33669
|
const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
|
|
32953
33670
|
definitionMap.set('minVersion', literal(minVersion));
|
|
32954
|
-
definitionMap.set('version', literal('17.0.
|
|
33671
|
+
definitionMap.set('version', literal('17.0.7'));
|
|
32955
33672
|
// e.g. `type: MyDirective`
|
|
32956
33673
|
definitionMap.set('type', meta.type.value);
|
|
32957
33674
|
if (meta.isStandalone) {
|
|
@@ -33228,7 +33945,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
|
|
|
33228
33945
|
function compileDeclareFactoryFunction(meta) {
|
|
33229
33946
|
const definitionMap = new DefinitionMap();
|
|
33230
33947
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
|
|
33231
|
-
definitionMap.set('version', literal('17.0.
|
|
33948
|
+
definitionMap.set('version', literal('17.0.7'));
|
|
33232
33949
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33233
33950
|
definitionMap.set('type', meta.type.value);
|
|
33234
33951
|
definitionMap.set('deps', compileDependencies(meta.deps));
|
|
@@ -33263,7 +33980,7 @@ function compileDeclareInjectableFromMetadata(meta) {
|
|
|
33263
33980
|
function createInjectableDefinitionMap(meta) {
|
|
33264
33981
|
const definitionMap = new DefinitionMap();
|
|
33265
33982
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
|
|
33266
|
-
definitionMap.set('version', literal('17.0.
|
|
33983
|
+
definitionMap.set('version', literal('17.0.7'));
|
|
33267
33984
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33268
33985
|
definitionMap.set('type', meta.type.value);
|
|
33269
33986
|
// Only generate providedIn property if it has a non-null value
|
|
@@ -33314,7 +34031,7 @@ function compileDeclareInjectorFromMetadata(meta) {
|
|
|
33314
34031
|
function createInjectorDefinitionMap(meta) {
|
|
33315
34032
|
const definitionMap = new DefinitionMap();
|
|
33316
34033
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
|
|
33317
|
-
definitionMap.set('version', literal('17.0.
|
|
34034
|
+
definitionMap.set('version', literal('17.0.7'));
|
|
33318
34035
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33319
34036
|
definitionMap.set('type', meta.type.value);
|
|
33320
34037
|
definitionMap.set('providers', meta.providers);
|
|
@@ -33347,7 +34064,7 @@ function createNgModuleDefinitionMap(meta) {
|
|
|
33347
34064
|
throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
|
|
33348
34065
|
}
|
|
33349
34066
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
|
|
33350
|
-
definitionMap.set('version', literal('17.0.
|
|
34067
|
+
definitionMap.set('version', literal('17.0.7'));
|
|
33351
34068
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33352
34069
|
definitionMap.set('type', meta.type.value);
|
|
33353
34070
|
// We only generate the keys in the metadata if the arrays contain values.
|
|
@@ -33398,7 +34115,7 @@ function compileDeclarePipeFromMetadata(meta) {
|
|
|
33398
34115
|
function createPipeDefinitionMap(meta) {
|
|
33399
34116
|
const definitionMap = new DefinitionMap();
|
|
33400
34117
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
|
|
33401
|
-
definitionMap.set('version', literal('17.0.
|
|
34118
|
+
definitionMap.set('version', literal('17.0.7'));
|
|
33402
34119
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33403
34120
|
// e.g. `type: MyPipe`
|
|
33404
34121
|
definitionMap.set('type', meta.type.value);
|
|
@@ -33431,5 +34148,5 @@ publishFacade(_global);
|
|
|
33431
34148
|
|
|
33432
34149
|
// This file is not used to build this module. It is only used during editing
|
|
33433
34150
|
|
|
33434
|
-
export { AST, ASTWithName, ASTWithSource, AbsoluteSourceSpan, ArrayType, ArrowFunctionExpr, AstMemoryEfficientTransformer, AstTransformer, Attribute, Binary, BinaryOperator, BinaryOperatorExpr, BindingPipe, Block, BlockParameter, BoundElementProperty, BuiltinType, BuiltinTypeName, CUSTOM_ELEMENTS_SCHEMA, Call, Chain, ChangeDetectionStrategy, CommaExpr, Comment, CompilerConfig, Conditional, ConditionalExpr, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DYNAMIC_TYPE, DeclareFunctionStmt, DeclareVarStmt, DomElementSchemaRegistry, DynamicImportExpr, EOF, Element, ElementSchemaRegistry, EmitterVisitorContext, EmptyExpr$1 as EmptyExpr, Expansion, ExpansionCase, Expression, ExpressionBinding, ExpressionStatement, ExpressionType, ExternalExpr, ExternalReference, FactoryTarget$1 as FactoryTarget, FunctionExpr, HtmlParser, HtmlTagDefinition, I18NHtmlParser, IfStmt, ImplicitReceiver, InstantiateExpr, Interpolation$1 as Interpolation, InterpolationConfig, InvokeFunctionExpr, JSDocComment, JitEvaluator, KeyedRead, KeyedWrite, LeadingComment, Lexer, LiteralArray, LiteralArrayExpr, LiteralExpr, LiteralMap, LiteralMapExpr, LiteralPrimitive, LocalizedString, MapType, MessageBundle, NONE_TYPE, NO_ERRORS_SCHEMA, NodeWithI18n, NonNullAssert, NotExpr, ParseError, ParseErrorLevel, ParseLocation, ParseSourceFile, ParseSourceSpan, ParseSpan, ParseTreeResult, ParsedEvent, ParsedProperty, ParsedPropertyType, ParsedVariable, Parser$1 as Parser, ParserError, PrefixNot, PropertyRead, PropertyWrite, R3BoundTarget, Identifiers as R3Identifiers, R3NgModuleMetadataKind, R3SelectorScopeMode, R3TargetBinder, R3TemplateDependencyKind, ReadKeyExpr, ReadPropExpr, ReadVarExpr, RecursiveAstVisitor, RecursiveVisitor, ResourceLoader, ReturnStatement, STRING_TYPE, SafeCall, SafeKeyedRead, SafePropertyRead, SelectorContext, SelectorListContext, SelectorMatcher, Serializer, SplitInterpolation, Statement, StmtModifier, TagContentType, TaggedTemplateExpr, TemplateBindingParseResult, TemplateLiteral, TemplateLiteralElement, Text, ThisReceiver, BoundAttribute as TmplAstBoundAttribute, BoundDeferredTrigger as TmplAstBoundDeferredTrigger, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, DeferredBlock as TmplAstDeferredBlock, DeferredBlockError as TmplAstDeferredBlockError, DeferredBlockLoading as TmplAstDeferredBlockLoading, DeferredBlockPlaceholder as TmplAstDeferredBlockPlaceholder, DeferredTrigger as TmplAstDeferredTrigger, Element$1 as TmplAstElement, ForLoopBlock as TmplAstForLoopBlock, ForLoopBlockEmpty as TmplAstForLoopBlockEmpty, HoverDeferredTrigger as TmplAstHoverDeferredTrigger, Icu$1 as TmplAstIcu, IdleDeferredTrigger as TmplAstIdleDeferredTrigger, IfBlock as TmplAstIfBlock, IfBlockBranch as TmplAstIfBlockBranch, ImmediateDeferredTrigger as TmplAstImmediateDeferredTrigger, InteractionDeferredTrigger as TmplAstInteractionDeferredTrigger, RecursiveVisitor$1 as TmplAstRecursiveVisitor, Reference as TmplAstReference, SwitchBlock as TmplAstSwitchBlock, SwitchBlockCase as TmplAstSwitchBlockCase, Template as TmplAstTemplate, Text$3 as TmplAstText, TextAttribute as TmplAstTextAttribute, TimerDeferredTrigger as TmplAstTimerDeferredTrigger, UnknownBlock as TmplAstUnknownBlock, Variable as TmplAstVariable, ViewportDeferredTrigger as TmplAstViewportDeferredTrigger, Token, TokenType, TransplantedType, TreeError, Type, TypeModifier, TypeofExpr, Unary, UnaryOperator, UnaryOperatorExpr, VERSION, VariableBinding, Version, ViewEncapsulation, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, Xliff, Xliff2, Xmb, XmlParser, Xtb, _ParseAST, compileClassDebugInfo, compileClassMetadata, compileComponentClassMetadata, compileComponentFromMetadata, compileDeclareClassMetadata, compileDeclareComponentFromMetadata, compileDeclareDirectiveFromMetadata, compileDeclareFactoryFunction, compileDeclareInjectableFromMetadata, compileDeclareInjectorFromMetadata, compileDeclareNgModuleFromMetadata, compileDeclarePipeFromMetadata, compileDirectiveFromMetadata, compileFactoryFunction, compileInjectable, compileInjector, compileNgModule, compilePipeFromMetadata, computeMsgId, core, createInjectableType, createMayBeForwardRefExpression, devOnlyGuardedExpression, emitDistinctChangesOnlyDefaultValue, getHtmlTagDefinition, getNsPrefix, getSafePropertyAccessString, identifierName, isIdentifier, isNgContainer, isNgContent, isNgTemplate, jsDocComment, leadingComment, literal, literalMap, makeBindingParser, mergeNsAndName, output_ast as outputAst, parseHostBindings, parseTemplate, preserveWhitespacesDefault, publishFacade, r3JitTypeSourceSpan, sanitizeIdentifier, splitNsName, verifyHostBindings, visitAll };
|
|
34151
|
+
export { AST, ASTWithName, ASTWithSource, AbsoluteSourceSpan, ArrayType, ArrowFunctionExpr, AstMemoryEfficientTransformer, AstTransformer, Attribute, Binary, BinaryOperator, BinaryOperatorExpr, BindingPipe, Block, BlockParameter, BoundElementProperty, BuiltinType, BuiltinTypeName, CUSTOM_ELEMENTS_SCHEMA, Call, Chain, ChangeDetectionStrategy, CommaExpr, Comment, CompilerConfig, Conditional, ConditionalExpr, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DYNAMIC_TYPE, DeclareFunctionStmt, DeclareVarStmt, DomElementSchemaRegistry, DynamicImportExpr, EOF, Element, ElementSchemaRegistry, EmitterVisitorContext, EmptyExpr$1 as EmptyExpr, Expansion, ExpansionCase, Expression, ExpressionBinding, ExpressionStatement, ExpressionType, ExternalExpr, ExternalReference, FactoryTarget$1 as FactoryTarget, FunctionExpr, HtmlParser, HtmlTagDefinition, I18NHtmlParser, IfStmt, ImplicitReceiver, InstantiateExpr, Interpolation$1 as Interpolation, InterpolationConfig, InvokeFunctionExpr, JSDocComment, JitEvaluator, KeyedRead, KeyedWrite, LeadingComment, Lexer, LiteralArray, LiteralArrayExpr, LiteralExpr, LiteralMap, LiteralMapExpr, LiteralPrimitive, LocalizedString, MapType, MessageBundle, NONE_TYPE, NO_ERRORS_SCHEMA, NodeWithI18n, NonNullAssert, NotExpr, ParseError, ParseErrorLevel, ParseLocation, ParseSourceFile, ParseSourceSpan, ParseSpan, ParseTreeResult, ParsedEvent, ParsedProperty, ParsedPropertyType, ParsedVariable, Parser$1 as Parser, ParserError, PrefixNot, PropertyRead, PropertyWrite, R3BoundTarget, Identifiers as R3Identifiers, R3NgModuleMetadataKind, R3SelectorScopeMode, R3TargetBinder, R3TemplateDependencyKind, ReadKeyExpr, ReadPropExpr, ReadVarExpr, RecursiveAstVisitor, RecursiveVisitor, ResourceLoader, ReturnStatement, STRING_TYPE, SafeCall, SafeKeyedRead, SafePropertyRead, SelectorContext, SelectorListContext, SelectorMatcher, Serializer, SplitInterpolation, Statement, StmtModifier, TagContentType, TaggedTemplateExpr, TemplateBindingParseResult, TemplateLiteral, TemplateLiteralElement, Text, ThisReceiver, BoundAttribute as TmplAstBoundAttribute, BoundDeferredTrigger as TmplAstBoundDeferredTrigger, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, DeferredBlock as TmplAstDeferredBlock, DeferredBlockError as TmplAstDeferredBlockError, DeferredBlockLoading as TmplAstDeferredBlockLoading, DeferredBlockPlaceholder as TmplAstDeferredBlockPlaceholder, DeferredTrigger as TmplAstDeferredTrigger, Element$1 as TmplAstElement, ForLoopBlock as TmplAstForLoopBlock, ForLoopBlockEmpty as TmplAstForLoopBlockEmpty, HoverDeferredTrigger as TmplAstHoverDeferredTrigger, Icu$1 as TmplAstIcu, IdleDeferredTrigger as TmplAstIdleDeferredTrigger, IfBlock as TmplAstIfBlock, IfBlockBranch as TmplAstIfBlockBranch, ImmediateDeferredTrigger as TmplAstImmediateDeferredTrigger, InteractionDeferredTrigger as TmplAstInteractionDeferredTrigger, RecursiveVisitor$1 as TmplAstRecursiveVisitor, Reference as TmplAstReference, SwitchBlock as TmplAstSwitchBlock, SwitchBlockCase as TmplAstSwitchBlockCase, Template as TmplAstTemplate, Text$3 as TmplAstText, TextAttribute as TmplAstTextAttribute, TimerDeferredTrigger as TmplAstTimerDeferredTrigger, UnknownBlock as TmplAstUnknownBlock, Variable as TmplAstVariable, ViewportDeferredTrigger as TmplAstViewportDeferredTrigger, Token, TokenType, TransplantedType, TreeError, Type, TypeModifier, TypeofExpr, Unary, UnaryOperator, UnaryOperatorExpr, VERSION, VariableBinding, Version, ViewEncapsulation, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, Xliff, Xliff2, Xmb, XmlParser, Xtb, _ParseAST, compileClassDebugInfo, compileClassMetadata, compileComponentClassMetadata, compileComponentFromMetadata, compileDeclareClassMetadata, compileDeclareComponentFromMetadata, compileDeclareDirectiveFromMetadata, compileDeclareFactoryFunction, compileDeclareInjectableFromMetadata, compileDeclareInjectorFromMetadata, compileDeclareNgModuleFromMetadata, compileDeclarePipeFromMetadata, compileDirectiveFromMetadata, compileFactoryFunction, compileInjectable, compileInjector, compileNgModule, compilePipeFromMetadata, computeMsgId, core, createCssSelectorFromNode, createInjectableType, createMayBeForwardRefExpression, devOnlyGuardedExpression, emitDistinctChangesOnlyDefaultValue, getHtmlTagDefinition, getNsPrefix, getSafePropertyAccessString, identifierName, isIdentifier, isNgContainer, isNgContent, isNgTemplate, jsDocComment, leadingComment, literal, literalMap, makeBindingParser, mergeNsAndName, output_ast as outputAst, parseHostBindings, parseTemplate, preserveWhitespacesDefault, publishFacade, r3JitTypeSourceSpan, sanitizeIdentifier, splitNsName, verifyHostBindings, visitAll };
|
|
33435
34152
|
//# sourceMappingURL=compiler.mjs.map
|