@angular/compiler 17.1.0-next.2 → 17.1.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/src/render3/partial/class_metadata.mjs +1 -1
- package/esm2022/src/render3/partial/directive.mjs +1 -1
- package/esm2022/src/render3/partial/factory.mjs +1 -1
- package/esm2022/src/render3/partial/injectable.mjs +1 -1
- package/esm2022/src/render3/partial/injector.mjs +1 -1
- package/esm2022/src/render3/partial/ng_module.mjs +1 -1
- package/esm2022/src/render3/partial/pipe.mjs +1 -1
- package/esm2022/src/render3/view/compiler.mjs +20 -8
- package/esm2022/src/template/pipeline/ir/src/enums.mjs +26 -1
- package/esm2022/src/template/pipeline/ir/src/expression.mjs +8 -1
- package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +25 -7
- package/esm2022/src/template/pipeline/ir/src/ops/host.mjs +3 -2
- package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +23 -14
- package/esm2022/src/template/pipeline/src/emit.mjs +10 -6
- package/esm2022/src/template/pipeline/src/ingest.mjs +86 -36
- package/esm2022/src/template/pipeline/src/instruction.mjs +5 -1
- 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 +43 -5
- package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +19 -5
- package/esm2022/src/template/pipeline/src/phases/binding_specialization.mjs +4 -4
- package/esm2022/src/template/pipeline/src/phases/const_collection.mjs +3 -3
- package/esm2022/src/template/pipeline/src/phases/convert_i18n_bindings.mjs +52 -0
- 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/i18n_const_collection.mjs +143 -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 +3 -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 +5 -1
- package/esm2022/src/template/pipeline/src/phases/reify.mjs +7 -1
- 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 +129 -31
- package/esm2022/src/template/pipeline/src/phases/resolve_i18n_expression_placeholders.mjs +10 -5
- package/esm2022/src/version.mjs +1 -1
- package/fesm2022/compiler.mjs +733 -257
- package/fesm2022/compiler.mjs.map +1 -1
- package/index.d.ts +1 -1
- package/package.json +2 -2
package/fesm2022/compiler.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v17.1.0-next.
|
|
2
|
+
* @license Angular v17.1.0-next.3
|
|
3
3
|
* (c) 2010-2022 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -4712,7 +4712,7 @@ const I18N_ATTR_PREFIX = 'i18n-';
|
|
|
4712
4712
|
/** Prefix of var expressions used in ICUs */
|
|
4713
4713
|
const I18N_ICU_VAR_PREFIX = 'VAR_';
|
|
4714
4714
|
/** Prefix of ICU expressions for post processing */
|
|
4715
|
-
const I18N_ICU_MAPPING_PREFIX = 'I18N_EXP_';
|
|
4715
|
+
const I18N_ICU_MAPPING_PREFIX$1 = 'I18N_EXP_';
|
|
4716
4716
|
/** Placeholder wrapper for i18n expressions **/
|
|
4717
4717
|
const I18N_PLACEHOLDER_SYMBOL = '�';
|
|
4718
4718
|
function isI18nAttribute(name) {
|
|
@@ -8970,6 +8970,10 @@ var OpKind;
|
|
|
8970
8970
|
* An i18n context containing information needed to generate an i18n message.
|
|
8971
8971
|
*/
|
|
8972
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";
|
|
8973
8977
|
})(OpKind || (OpKind = {}));
|
|
8974
8978
|
/**
|
|
8975
8979
|
* Distinguishes different kinds of IR expressions.
|
|
@@ -9191,6 +9195,20 @@ var I18nParamResolutionTime;
|
|
|
9191
9195
|
*/
|
|
9192
9196
|
I18nParamResolutionTime[I18nParamResolutionTime["Postproccessing"] = 1] = "Postproccessing";
|
|
9193
9197
|
})(I18nParamResolutionTime || (I18nParamResolutionTime = {}));
|
|
9198
|
+
/**
|
|
9199
|
+
* The contexts in which an i18n expression can be used.
|
|
9200
|
+
*/
|
|
9201
|
+
var I18nExpressionFor;
|
|
9202
|
+
(function (I18nExpressionFor) {
|
|
9203
|
+
/**
|
|
9204
|
+
* This expression is used as a value (i.e. inside an i18n block).
|
|
9205
|
+
*/
|
|
9206
|
+
I18nExpressionFor[I18nExpressionFor["I18nText"] = 0] = "I18nText";
|
|
9207
|
+
/**
|
|
9208
|
+
* This expression is used in a binding.
|
|
9209
|
+
*/
|
|
9210
|
+
I18nExpressionFor[I18nExpressionFor["I18nAttribute"] = 1] = "I18nAttribute";
|
|
9211
|
+
})(I18nExpressionFor || (I18nExpressionFor = {}));
|
|
9194
9212
|
/**
|
|
9195
9213
|
* Flags that describe what an i18n param value. These determine how the value is serialized into
|
|
9196
9214
|
* the final map.
|
|
@@ -9257,7 +9275,14 @@ var I18nContextKind;
|
|
|
9257
9275
|
(function (I18nContextKind) {
|
|
9258
9276
|
I18nContextKind[I18nContextKind["RootI18n"] = 0] = "RootI18n";
|
|
9259
9277
|
I18nContextKind[I18nContextKind["Icu"] = 1] = "Icu";
|
|
9278
|
+
I18nContextKind[I18nContextKind["Attr"] = 2] = "Attr";
|
|
9260
9279
|
})(I18nContextKind || (I18nContextKind = {}));
|
|
9280
|
+
var TemplateKind;
|
|
9281
|
+
(function (TemplateKind) {
|
|
9282
|
+
TemplateKind[TemplateKind["NgTemplate"] = 0] = "NgTemplate";
|
|
9283
|
+
TemplateKind[TemplateKind["Structural"] = 1] = "Structural";
|
|
9284
|
+
TemplateKind[TemplateKind["Block"] = 2] = "Block";
|
|
9285
|
+
})(TemplateKind || (TemplateKind = {}));
|
|
9261
9286
|
|
|
9262
9287
|
/**
|
|
9263
9288
|
* Marker symbol for `ConsumesSlotOpTrait`.
|
|
@@ -9365,12 +9390,11 @@ const NEW_OP = {
|
|
|
9365
9390
|
/**
|
|
9366
9391
|
* Create an `InterpolationTextOp`.
|
|
9367
9392
|
*/
|
|
9368
|
-
function createInterpolateTextOp(xref, interpolation,
|
|
9393
|
+
function createInterpolateTextOp(xref, interpolation, sourceSpan) {
|
|
9369
9394
|
return {
|
|
9370
9395
|
kind: OpKind.InterpolateText,
|
|
9371
9396
|
target: xref,
|
|
9372
9397
|
interpolation,
|
|
9373
|
-
i18nPlaceholders,
|
|
9374
9398
|
sourceSpan,
|
|
9375
9399
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9376
9400
|
...TRAIT_CONSUMES_VARS,
|
|
@@ -9378,15 +9402,19 @@ function createInterpolateTextOp(xref, interpolation, i18nPlaceholders, sourceSp
|
|
|
9378
9402
|
};
|
|
9379
9403
|
}
|
|
9380
9404
|
class Interpolation {
|
|
9381
|
-
constructor(strings, expressions) {
|
|
9405
|
+
constructor(strings, expressions, i18nPlaceholders) {
|
|
9382
9406
|
this.strings = strings;
|
|
9383
9407
|
this.expressions = expressions;
|
|
9408
|
+
this.i18nPlaceholders = i18nPlaceholders;
|
|
9409
|
+
if (i18nPlaceholders.length !== 0 && i18nPlaceholders.length !== expressions.length) {
|
|
9410
|
+
throw new Error(`Expected ${expressions.length} placeholders to match interpolation expression count, but got ${i18nPlaceholders.length}`);
|
|
9411
|
+
}
|
|
9384
9412
|
}
|
|
9385
9413
|
}
|
|
9386
9414
|
/**
|
|
9387
9415
|
* Create a `BindingOp`, not yet transformed into a particular type of binding.
|
|
9388
9416
|
*/
|
|
9389
|
-
function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute,
|
|
9417
|
+
function createBindingOp(target, kind, name, expression, unit, securityContext, isTextAttribute, isStructuralTemplate, i18nContext, sourceSpan) {
|
|
9390
9418
|
return {
|
|
9391
9419
|
kind: OpKind.Binding,
|
|
9392
9420
|
bindingKind: kind,
|
|
@@ -9396,7 +9424,8 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
|
|
|
9396
9424
|
unit,
|
|
9397
9425
|
securityContext,
|
|
9398
9426
|
isTextAttribute,
|
|
9399
|
-
|
|
9427
|
+
isStructuralTemplate: isStructuralTemplate,
|
|
9428
|
+
i18nContext,
|
|
9400
9429
|
sourceSpan,
|
|
9401
9430
|
...NEW_OP,
|
|
9402
9431
|
};
|
|
@@ -9404,7 +9433,7 @@ function createBindingOp(target, kind, name, expression, unit, securityContext,
|
|
|
9404
9433
|
/**
|
|
9405
9434
|
* Create a `PropertyOp`.
|
|
9406
9435
|
*/
|
|
9407
|
-
function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext,
|
|
9436
|
+
function createPropertyOp(target, name, expression, isAnimationTrigger, securityContext, isStructuralTemplate, i18nContext, sourceSpan) {
|
|
9408
9437
|
return {
|
|
9409
9438
|
kind: OpKind.Property,
|
|
9410
9439
|
target,
|
|
@@ -9413,7 +9442,8 @@ function createPropertyOp(target, name, expression, isAnimationTrigger, security
|
|
|
9413
9442
|
isAnimationTrigger,
|
|
9414
9443
|
securityContext,
|
|
9415
9444
|
sanitizer: null,
|
|
9416
|
-
|
|
9445
|
+
isStructuralTemplate,
|
|
9446
|
+
i18nContext,
|
|
9417
9447
|
sourceSpan,
|
|
9418
9448
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9419
9449
|
...TRAIT_CONSUMES_VARS,
|
|
@@ -9478,7 +9508,7 @@ function createClassMapOp(xref, expression, sourceSpan) {
|
|
|
9478
9508
|
/**
|
|
9479
9509
|
* Create an `AttributeOp`.
|
|
9480
9510
|
*/
|
|
9481
|
-
function createAttributeOp(target, name, expression, securityContext, isTextAttribute,
|
|
9511
|
+
function createAttributeOp(target, name, expression, securityContext, isTextAttribute, isStructuralTemplate, i18nContext, sourceSpan) {
|
|
9482
9512
|
return {
|
|
9483
9513
|
kind: OpKind.Attribute,
|
|
9484
9514
|
target,
|
|
@@ -9487,7 +9517,8 @@ function createAttributeOp(target, name, expression, securityContext, isTextAttr
|
|
|
9487
9517
|
securityContext,
|
|
9488
9518
|
sanitizer: null,
|
|
9489
9519
|
isTextAttribute,
|
|
9490
|
-
|
|
9520
|
+
isStructuralTemplate,
|
|
9521
|
+
i18nContext,
|
|
9491
9522
|
sourceSpan,
|
|
9492
9523
|
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9493
9524
|
...TRAIT_CONSUMES_VARS,
|
|
@@ -9548,15 +9579,18 @@ function createDeferWhenOp(target, expr, prefetch, sourceSpan) {
|
|
|
9548
9579
|
/**
|
|
9549
9580
|
* Create an i18n expression op.
|
|
9550
9581
|
*/
|
|
9551
|
-
function createI18nExpressionOp(context, target, handle, expression, i18nPlaceholder, resolutionTime, sourceSpan) {
|
|
9582
|
+
function createI18nExpressionOp(context, target, i18nOwner, handle, expression, i18nPlaceholder, resolutionTime, usage, name, sourceSpan) {
|
|
9552
9583
|
return {
|
|
9553
9584
|
kind: OpKind.I18nExpression,
|
|
9554
9585
|
context,
|
|
9555
9586
|
target,
|
|
9587
|
+
i18nOwner,
|
|
9556
9588
|
handle,
|
|
9557
9589
|
expression,
|
|
9558
9590
|
i18nPlaceholder,
|
|
9559
9591
|
resolutionTime,
|
|
9592
|
+
usage,
|
|
9593
|
+
name,
|
|
9560
9594
|
sourceSpan,
|
|
9561
9595
|
...NEW_OP,
|
|
9562
9596
|
...TRAIT_CONSUMES_VARS,
|
|
@@ -9564,12 +9598,12 @@ function createI18nExpressionOp(context, target, handle, expression, i18nPlaceho
|
|
|
9564
9598
|
};
|
|
9565
9599
|
}
|
|
9566
9600
|
/**
|
|
9567
|
-
*Creates an op to apply i18n expression ops
|
|
9601
|
+
* Creates an op to apply i18n expression ops.
|
|
9568
9602
|
*/
|
|
9569
|
-
function createI18nApplyOp(
|
|
9603
|
+
function createI18nApplyOp(owner, handle, sourceSpan) {
|
|
9570
9604
|
return {
|
|
9571
9605
|
kind: OpKind.I18nApply,
|
|
9572
|
-
|
|
9606
|
+
owner,
|
|
9573
9607
|
handle,
|
|
9574
9608
|
sourceSpan,
|
|
9575
9609
|
...NEW_OP,
|
|
@@ -10373,6 +10407,7 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10373
10407
|
case OpKind.ProjectionDef:
|
|
10374
10408
|
case OpKind.Template:
|
|
10375
10409
|
case OpKind.Text:
|
|
10410
|
+
case OpKind.I18nAttributes:
|
|
10376
10411
|
// These operations contain no expressions.
|
|
10377
10412
|
break;
|
|
10378
10413
|
default:
|
|
@@ -10393,6 +10428,9 @@ function transformExpressionsInExpression(expr, transform, flags) {
|
|
|
10393
10428
|
expr.lhs = transformExpressionsInExpression(expr.lhs, transform, flags);
|
|
10394
10429
|
expr.rhs = transformExpressionsInExpression(expr.rhs, transform, flags);
|
|
10395
10430
|
}
|
|
10431
|
+
else if (expr instanceof UnaryOperatorExpr) {
|
|
10432
|
+
expr.expr = transformExpressionsInExpression(expr.expr, transform, flags);
|
|
10433
|
+
}
|
|
10396
10434
|
else if (expr instanceof ReadPropExpr) {
|
|
10397
10435
|
expr.receiver = transformExpressionsInExpression(expr.receiver, transform, flags);
|
|
10398
10436
|
}
|
|
@@ -10444,6 +10482,9 @@ function transformExpressionsInExpression(expr, transform, flags) {
|
|
|
10444
10482
|
expr.expressions[i] = transformExpressionsInExpression(expr.expressions[i], transform, flags);
|
|
10445
10483
|
}
|
|
10446
10484
|
}
|
|
10485
|
+
else if (expr instanceof NotExpr) {
|
|
10486
|
+
expr.condition = transformExpressionsInExpression(expr.condition, transform, flags);
|
|
10487
|
+
}
|
|
10447
10488
|
else if (expr instanceof ReadVarExpr || expr instanceof ExternalExpr ||
|
|
10448
10489
|
expr instanceof LiteralExpr) {
|
|
10449
10490
|
// No action for these types.
|
|
@@ -10785,10 +10826,11 @@ function createElementStartOp(tag, xref, namespace, i18nPlaceholder, sourceSpan)
|
|
|
10785
10826
|
/**
|
|
10786
10827
|
* Create a `TemplateOp`.
|
|
10787
10828
|
*/
|
|
10788
|
-
function createTemplateOp(xref, tag, functionNameSuffix, namespace, i18nPlaceholder, sourceSpan) {
|
|
10829
|
+
function createTemplateOp(xref, templateKind, tag, functionNameSuffix, namespace, i18nPlaceholder, sourceSpan) {
|
|
10789
10830
|
return {
|
|
10790
10831
|
kind: OpKind.Template,
|
|
10791
10832
|
xref,
|
|
10833
|
+
templateKind,
|
|
10792
10834
|
attributes: null,
|
|
10793
10835
|
tag,
|
|
10794
10836
|
handle: new SlotHandle(),
|
|
@@ -10911,14 +10953,15 @@ function createProjectionDefOp(def) {
|
|
|
10911
10953
|
...NEW_OP,
|
|
10912
10954
|
};
|
|
10913
10955
|
}
|
|
10914
|
-
function createProjectionOp(xref, selector, sourceSpan) {
|
|
10956
|
+
function createProjectionOp(xref, selector, i18nPlaceholder, attributes, sourceSpan) {
|
|
10915
10957
|
return {
|
|
10916
10958
|
kind: OpKind.Projection,
|
|
10917
10959
|
xref,
|
|
10918
10960
|
handle: new SlotHandle(),
|
|
10919
10961
|
selector,
|
|
10962
|
+
i18nPlaceholder,
|
|
10920
10963
|
projectionSlotIndex: 0,
|
|
10921
|
-
attributes
|
|
10964
|
+
attributes,
|
|
10922
10965
|
localRefs: [],
|
|
10923
10966
|
sourceSpan,
|
|
10924
10967
|
...NEW_OP,
|
|
@@ -10928,13 +10971,14 @@ function createProjectionOp(xref, selector, sourceSpan) {
|
|
|
10928
10971
|
/**
|
|
10929
10972
|
* Create an `ExtractedAttributeOp`.
|
|
10930
10973
|
*/
|
|
10931
|
-
function createExtractedAttributeOp(target, bindingKind, name, expression) {
|
|
10974
|
+
function createExtractedAttributeOp(target, bindingKind, name, expression, i18nContext) {
|
|
10932
10975
|
return {
|
|
10933
10976
|
kind: OpKind.ExtractedAttribute,
|
|
10934
10977
|
target,
|
|
10935
10978
|
bindingKind,
|
|
10936
10979
|
name,
|
|
10937
10980
|
expression,
|
|
10981
|
+
i18nContext,
|
|
10938
10982
|
...NEW_OP,
|
|
10939
10983
|
};
|
|
10940
10984
|
}
|
|
@@ -10977,10 +11021,11 @@ function createDeferOnOp(defer, trigger, prefetch, sourceSpan) {
|
|
|
10977
11021
|
/**
|
|
10978
11022
|
* Create an `ExtractedMessageOp`.
|
|
10979
11023
|
*/
|
|
10980
|
-
function createI18nMessageOp(xref, i18nBlock, message, messagePlaceholder, params, postprocessingParams, needsPostprocessing) {
|
|
11024
|
+
function createI18nMessageOp(xref, i18nContext, i18nBlock, message, messagePlaceholder, params, postprocessingParams, needsPostprocessing) {
|
|
10981
11025
|
return {
|
|
10982
11026
|
kind: OpKind.I18nMessage,
|
|
10983
11027
|
xref,
|
|
11028
|
+
i18nContext,
|
|
10984
11029
|
i18nBlock,
|
|
10985
11030
|
message,
|
|
10986
11031
|
messagePlaceholder,
|
|
@@ -11043,6 +11088,9 @@ function createIcuEndOp(xref) {
|
|
|
11043
11088
|
};
|
|
11044
11089
|
}
|
|
11045
11090
|
function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan) {
|
|
11091
|
+
if (i18nBlock === null && contextKind !== I18nContextKind.Attr) {
|
|
11092
|
+
throw new Error('AssertionError: i18nBlock must be provided for non-attribute contexts.');
|
|
11093
|
+
}
|
|
11046
11094
|
return {
|
|
11047
11095
|
kind: OpKind.I18nContext,
|
|
11048
11096
|
contextKind,
|
|
@@ -11055,6 +11103,17 @@ function createI18nContextOp(contextKind, xref, i18nBlock, message, sourceSpan)
|
|
|
11055
11103
|
...NEW_OP,
|
|
11056
11104
|
};
|
|
11057
11105
|
}
|
|
11106
|
+
function createI18nAttributesOp(xref, handle, target) {
|
|
11107
|
+
return {
|
|
11108
|
+
kind: OpKind.I18nAttributes,
|
|
11109
|
+
xref,
|
|
11110
|
+
handle,
|
|
11111
|
+
target,
|
|
11112
|
+
i18nAttributesConfig: null,
|
|
11113
|
+
...NEW_OP,
|
|
11114
|
+
...TRAIT_CONSUMES_SLOT,
|
|
11115
|
+
};
|
|
11116
|
+
}
|
|
11058
11117
|
function literalOrArrayLiteral$1(value) {
|
|
11059
11118
|
if (Array.isArray(value)) {
|
|
11060
11119
|
return literalArr(value.map(literalOrArrayLiteral$1));
|
|
@@ -11062,12 +11121,13 @@ function literalOrArrayLiteral$1(value) {
|
|
|
11062
11121
|
return literal(value, INFERRED_TYPE);
|
|
11063
11122
|
}
|
|
11064
11123
|
|
|
11065
|
-
function createHostPropertyOp(name, expression, isAnimationTrigger, sourceSpan) {
|
|
11124
|
+
function createHostPropertyOp(name, expression, isAnimationTrigger, i18nContext, sourceSpan) {
|
|
11066
11125
|
return {
|
|
11067
11126
|
kind: OpKind.HostProperty,
|
|
11068
11127
|
name,
|
|
11069
11128
|
expression,
|
|
11070
11129
|
isAnimationTrigger,
|
|
11130
|
+
i18nContext,
|
|
11071
11131
|
sourceSpan,
|
|
11072
11132
|
...TRAIT_CONSUMES_VARS,
|
|
11073
11133
|
...NEW_OP,
|
|
@@ -11304,7 +11364,7 @@ function applyI18nExpressions(job) {
|
|
|
11304
11364
|
// Only add apply after expressions that are not followed by more expressions.
|
|
11305
11365
|
if (op.kind === OpKind.I18nExpression && needsApplication(i18nContexts, op)) {
|
|
11306
11366
|
// TODO: what should be the source span for the apply op?
|
|
11307
|
-
OpList.insertAfter(createI18nApplyOp(op.
|
|
11367
|
+
OpList.insertAfter(createI18nApplyOp(op.i18nOwner, op.handle, null), op);
|
|
11308
11368
|
}
|
|
11309
11369
|
}
|
|
11310
11370
|
}
|
|
@@ -11317,17 +11377,34 @@ function needsApplication(i18nContexts, op) {
|
|
|
11317
11377
|
if (op.next?.kind !== OpKind.I18nExpression) {
|
|
11318
11378
|
return true;
|
|
11319
11379
|
}
|
|
11320
|
-
// If the next op is an expression targeting a different i18n block, we need to apply.
|
|
11321
11380
|
const context = i18nContexts.get(op.context);
|
|
11322
11381
|
const nextContext = i18nContexts.get(op.next.context);
|
|
11323
|
-
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) {
|
|
11324
11400
|
return true;
|
|
11325
11401
|
}
|
|
11326
11402
|
return false;
|
|
11327
11403
|
}
|
|
11328
11404
|
|
|
11329
11405
|
/**
|
|
11330
|
-
* 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.
|
|
11331
11408
|
*/
|
|
11332
11409
|
function assignI18nSlotDependencies(job) {
|
|
11333
11410
|
const i18nLastSlotConsumers = new Map();
|
|
@@ -11344,17 +11421,54 @@ function assignI18nSlotDependencies(job) {
|
|
|
11344
11421
|
currentI18nOp = op;
|
|
11345
11422
|
break;
|
|
11346
11423
|
case OpKind.I18nEnd:
|
|
11424
|
+
if (currentI18nOp === null) {
|
|
11425
|
+
throw new Error('AssertionError: Expected an active I18n block while calculating last slot consumers');
|
|
11426
|
+
}
|
|
11427
|
+
if (lastSlotConsumer === null) {
|
|
11428
|
+
throw new Error('AssertionError: Expected a last slot consumer while calculating last slot consumers');
|
|
11429
|
+
}
|
|
11347
11430
|
i18nLastSlotConsumers.set(currentI18nOp.xref, lastSlotConsumer);
|
|
11348
11431
|
currentI18nOp = null;
|
|
11349
11432
|
break;
|
|
11350
11433
|
}
|
|
11351
11434
|
}
|
|
11352
|
-
//
|
|
11435
|
+
// Expresions that are currently being moved.
|
|
11436
|
+
let opsToMove = [];
|
|
11437
|
+
// Previously we found the last slot-consuming create mode op in the i18n block. That op will be
|
|
11438
|
+
// the new target for any moved i18n expresion inside the i18n block, and that op's slot is
|
|
11439
|
+
// stored here.
|
|
11440
|
+
let moveAfterTarget = null;
|
|
11441
|
+
// This is the last target in the create IR that we saw during iteration. Eventally, it will be
|
|
11442
|
+
// equal to moveAfterTarget. But wait! We need to find the *last* such op whose target is equal
|
|
11443
|
+
// to `moveAfterTarget`.
|
|
11444
|
+
let previousTarget = null;
|
|
11353
11445
|
for (const op of unit.update) {
|
|
11354
|
-
if (op
|
|
11355
|
-
op
|
|
11446
|
+
if (hasDependsOnSlotContextTrait(op)) {
|
|
11447
|
+
// We've found an op that depends on another slot other than the one that we want to move
|
|
11448
|
+
// the expressions to, after previously having seen the one we want to move to.
|
|
11449
|
+
if (moveAfterTarget !== null && previousTarget === moveAfterTarget &&
|
|
11450
|
+
op.target !== previousTarget) {
|
|
11451
|
+
OpList.insertBefore(opsToMove, op);
|
|
11452
|
+
moveAfterTarget = null;
|
|
11453
|
+
opsToMove = [];
|
|
11454
|
+
}
|
|
11455
|
+
previousTarget = op.target;
|
|
11456
|
+
}
|
|
11457
|
+
if (op.kind === OpKind.I18nExpression && op.usage === I18nExpressionFor.I18nText) {
|
|
11458
|
+
// This is an I18nExpressionOps that is used for text (not attributes).
|
|
11459
|
+
OpList.remove(op);
|
|
11460
|
+
opsToMove.push(op);
|
|
11461
|
+
const target = i18nLastSlotConsumers.get(op.i18nOwner);
|
|
11462
|
+
if (target === undefined) {
|
|
11463
|
+
throw new Error('AssertionError: Expected to find a last slot consumer for an I18nExpressionOp');
|
|
11464
|
+
}
|
|
11465
|
+
op.target = target;
|
|
11466
|
+
moveAfterTarget = op.target;
|
|
11356
11467
|
}
|
|
11357
11468
|
}
|
|
11469
|
+
if (moveAfterTarget !== null) {
|
|
11470
|
+
unit.update.push(opsToMove);
|
|
11471
|
+
}
|
|
11358
11472
|
}
|
|
11359
11473
|
}
|
|
11360
11474
|
|
|
@@ -11386,22 +11500,36 @@ function extractAttributes(job) {
|
|
|
11386
11500
|
break;
|
|
11387
11501
|
case OpKind.Property:
|
|
11388
11502
|
if (!op.isAnimationTrigger) {
|
|
11389
|
-
|
|
11503
|
+
let bindingKind;
|
|
11504
|
+
if (op.i18nContext !== null) {
|
|
11505
|
+
// If the binding has an i18n context, it is an i18n attribute, and should have that
|
|
11506
|
+
// kind in the consts array.
|
|
11507
|
+
bindingKind = BindingKind.I18n;
|
|
11508
|
+
}
|
|
11509
|
+
else if (op.isStructuralTemplate) {
|
|
11510
|
+
// TODO: How do i18n attributes on templates work?!
|
|
11511
|
+
bindingKind = BindingKind.Template;
|
|
11512
|
+
}
|
|
11513
|
+
else {
|
|
11514
|
+
bindingKind = BindingKind.Property;
|
|
11515
|
+
}
|
|
11516
|
+
OpList.insertBefore(createExtractedAttributeOp(op.target, bindingKind, op.name, null, null), lookupElement$2(elements, op.target));
|
|
11390
11517
|
}
|
|
11391
11518
|
break;
|
|
11392
11519
|
case OpKind.StyleProp:
|
|
11393
11520
|
case OpKind.ClassProp:
|
|
11521
|
+
// TODO: Can style or class bindings be i18n attributes?
|
|
11394
11522
|
// The old compiler treated empty style bindings as regular bindings for the purpose of
|
|
11395
11523
|
// directive matching. That behavior is incorrect, but we emulate it in compatibility
|
|
11396
11524
|
// mode.
|
|
11397
11525
|
if (unit.job.compatibility === CompatibilityMode.TemplateDefinitionBuilder &&
|
|
11398
11526
|
op.expression instanceof EmptyExpr) {
|
|
11399
|
-
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null), lookupElement$2(elements, op.target));
|
|
11527
|
+
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null, null), lookupElement$2(elements, op.target));
|
|
11400
11528
|
}
|
|
11401
11529
|
break;
|
|
11402
11530
|
case OpKind.Listener:
|
|
11403
11531
|
if (!op.isAnimationListener) {
|
|
11404
|
-
const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null);
|
|
11532
|
+
const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, op.name, null, null);
|
|
11405
11533
|
if (job.kind === CompilationJobKind.Host) {
|
|
11406
11534
|
// This attribute will apply to the enclosing host binding compilation unit, so order
|
|
11407
11535
|
// doesn't matter.
|
|
@@ -11450,7 +11578,7 @@ function extractAttributeOp(unit, op, elements) {
|
|
|
11450
11578
|
}
|
|
11451
11579
|
}
|
|
11452
11580
|
if (extractable) {
|
|
11453
|
-
const extractedAttributeOp = createExtractedAttributeOp(op.target, op.
|
|
11581
|
+
const extractedAttributeOp = createExtractedAttributeOp(op.target, op.isStructuralTemplate ? BindingKind.Template : BindingKind.Attribute, op.name, op.expression, op.i18nContext);
|
|
11454
11582
|
if (unit.job.kind === CompilationJobKind.Host) {
|
|
11455
11583
|
// This attribute will apply to the enclosing host binding compilation unit, so order doesn't
|
|
11456
11584
|
// matter.
|
|
@@ -11497,16 +11625,16 @@ function specializeBindings(job) {
|
|
|
11497
11625
|
target.nonBindable = true;
|
|
11498
11626
|
}
|
|
11499
11627
|
else {
|
|
11500
|
-
OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.
|
|
11628
|
+
OpList.replace(op, createAttributeOp(op.target, op.name, op.expression, op.securityContext, op.isTextAttribute, op.isStructuralTemplate, op.i18nContext, op.sourceSpan));
|
|
11501
11629
|
}
|
|
11502
11630
|
break;
|
|
11503
11631
|
case BindingKind.Property:
|
|
11504
11632
|
case BindingKind.Animation:
|
|
11505
11633
|
if (job.kind === CompilationJobKind.Host) {
|
|
11506
|
-
OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.sourceSpan));
|
|
11634
|
+
OpList.replace(op, createHostPropertyOp(op.name, op.expression, op.bindingKind === BindingKind.Animation, op.i18nContext, op.sourceSpan));
|
|
11507
11635
|
}
|
|
11508
11636
|
else {
|
|
11509
|
-
OpList.replace(op, createPropertyOp(op.target, op.name, op.expression, op.bindingKind === BindingKind.Animation, op.securityContext, op.
|
|
11637
|
+
OpList.replace(op, createPropertyOp(op.target, op.name, op.expression, op.bindingKind === BindingKind.Animation, op.securityContext, op.isStructuralTemplate, op.i18nContext, op.sourceSpan));
|
|
11510
11638
|
}
|
|
11511
11639
|
break;
|
|
11512
11640
|
case BindingKind.I18n:
|
|
@@ -11827,7 +11955,7 @@ class ElementAttributes {
|
|
|
11827
11955
|
array.push(...getAttributeNameLiterals$1(name));
|
|
11828
11956
|
if (kind === BindingKind.Attribute || kind === BindingKind.StyleProperty) {
|
|
11829
11957
|
if (value === null) {
|
|
11830
|
-
throw Error('Attribute & style element attributes must have a value');
|
|
11958
|
+
throw Error('Attribute, i18n attribute, & style element attributes must have a value');
|
|
11831
11959
|
}
|
|
11832
11960
|
array.push(value);
|
|
11833
11961
|
}
|
|
@@ -11881,6 +12009,50 @@ function serializeAttributes({ attributes, bindings, classes, i18n, projectAs, s
|
|
|
11881
12009
|
return literalArr(attrArray);
|
|
11882
12010
|
}
|
|
11883
12011
|
|
|
12012
|
+
/**
|
|
12013
|
+
* Some binding instructions in the update block may actually correspond to i18n bindings. In that
|
|
12014
|
+
* case, they should be replaced with i18nExp instructions for the dynamic portions.
|
|
12015
|
+
*/
|
|
12016
|
+
function convertI18nBindings(job) {
|
|
12017
|
+
const i18nAttributesByElem = new Map();
|
|
12018
|
+
for (const unit of job.units) {
|
|
12019
|
+
for (const op of unit.create) {
|
|
12020
|
+
if (op.kind === OpKind.I18nAttributes) {
|
|
12021
|
+
i18nAttributesByElem.set(op.target, op);
|
|
12022
|
+
}
|
|
12023
|
+
}
|
|
12024
|
+
for (const op of unit.update) {
|
|
12025
|
+
switch (op.kind) {
|
|
12026
|
+
case OpKind.Property:
|
|
12027
|
+
case OpKind.Attribute:
|
|
12028
|
+
if (op.i18nContext === null) {
|
|
12029
|
+
continue;
|
|
12030
|
+
}
|
|
12031
|
+
if (!(op.expression instanceof Interpolation)) {
|
|
12032
|
+
continue;
|
|
12033
|
+
}
|
|
12034
|
+
const i18nAttributesForElem = i18nAttributesByElem.get(op.target);
|
|
12035
|
+
if (i18nAttributesForElem === undefined) {
|
|
12036
|
+
throw new Error('AssertionError: An i18n attribute binding instruction requires the owning element to have an I18nAttributes create instruction');
|
|
12037
|
+
}
|
|
12038
|
+
if (i18nAttributesForElem.target !== op.target) {
|
|
12039
|
+
throw new Error('AssertionError: Expected i18nAttributes target element to match binding target element');
|
|
12040
|
+
}
|
|
12041
|
+
const ops = [];
|
|
12042
|
+
for (let i = 0; i < op.expression.expressions.length; i++) {
|
|
12043
|
+
const expr = op.expression.expressions[i];
|
|
12044
|
+
if (op.expression.i18nPlaceholders.length !== op.expression.expressions.length) {
|
|
12045
|
+
throw new Error(`AssertionError: An i18n attribute binding instruction requires the same number of expressions and placeholders, but found ${op.expression.i18nPlaceholders.length} placeholders and ${op.expression.expressions.length} expressions`);
|
|
12046
|
+
}
|
|
12047
|
+
ops.push(createI18nExpressionOp(op.i18nContext, i18nAttributesForElem.target, i18nAttributesForElem.xref, i18nAttributesForElem.handle, expr, op.expression.i18nPlaceholders[i], I18nParamResolutionTime.Creation, I18nExpressionFor.I18nAttribute, op.name, op.sourceSpan));
|
|
12048
|
+
}
|
|
12049
|
+
OpList.replaceWithMany(op, ops);
|
|
12050
|
+
break;
|
|
12051
|
+
}
|
|
12052
|
+
}
|
|
12053
|
+
}
|
|
12054
|
+
}
|
|
12055
|
+
|
|
11884
12056
|
/**
|
|
11885
12057
|
* Create extracted deps functions for defer ops.
|
|
11886
12058
|
*/
|
|
@@ -12324,7 +12496,7 @@ function ternaryTransform(e) {
|
|
|
12324
12496
|
/**
|
|
12325
12497
|
* The escape sequence used indicate message param values.
|
|
12326
12498
|
*/
|
|
12327
|
-
const ESCAPE = '\uFFFD';
|
|
12499
|
+
const ESCAPE$1 = '\uFFFD';
|
|
12328
12500
|
/**
|
|
12329
12501
|
* Marker used to indicate an element tag.
|
|
12330
12502
|
*/
|
|
@@ -12373,6 +12545,18 @@ function extractI18nMessages(job) {
|
|
|
12373
12545
|
}
|
|
12374
12546
|
}
|
|
12375
12547
|
}
|
|
12548
|
+
// TODO: Miles and I think that contexts have a 1-to-1 correspondence with extracted messages, so
|
|
12549
|
+
// this phase can probably be simplified.
|
|
12550
|
+
// Extract messages from contexts of i18n attributes.
|
|
12551
|
+
for (const unit of job.units) {
|
|
12552
|
+
for (const op of unit.create) {
|
|
12553
|
+
if (op.kind !== OpKind.I18nContext || op.contextKind !== I18nContextKind.Attr) {
|
|
12554
|
+
continue;
|
|
12555
|
+
}
|
|
12556
|
+
const i18nMessageOp = createI18nMessage(job, op);
|
|
12557
|
+
unit.create.push(i18nMessageOp);
|
|
12558
|
+
}
|
|
12559
|
+
}
|
|
12376
12560
|
// Extract messages from root i18n blocks.
|
|
12377
12561
|
const i18nBlockMessages = new Map();
|
|
12378
12562
|
for (const unit of job.units) {
|
|
@@ -12397,6 +12581,9 @@ function extractI18nMessages(job) {
|
|
|
12397
12581
|
}
|
|
12398
12582
|
const i18nContext = i18nContexts.get(op.context);
|
|
12399
12583
|
if (i18nContext.contextKind === I18nContextKind.Icu) {
|
|
12584
|
+
if (i18nContext.i18nBlock === null) {
|
|
12585
|
+
throw Error('ICU context should have its i18n block set.');
|
|
12586
|
+
}
|
|
12400
12587
|
const subMessage = createI18nMessage(job, i18nContext, op.messagePlaceholder);
|
|
12401
12588
|
unit.create.push(subMessage);
|
|
12402
12589
|
const rootI18nId = i18nBlocks.get(i18nContext.i18nBlock).root;
|
|
@@ -12416,119 +12603,86 @@ function extractI18nMessages(job) {
|
|
|
12416
12603
|
* Create an i18n message op from an i18n context op.
|
|
12417
12604
|
*/
|
|
12418
12605
|
function createI18nMessage(job, context, messagePlaceholder) {
|
|
12419
|
-
let
|
|
12420
|
-
const
|
|
12421
|
-
needsPostprocessing
|
|
12422
|
-
|
|
12606
|
+
let formattedParams = formatParams(context.params);
|
|
12607
|
+
const formattedPostprocessingParams = formatParams(context.postprocessingParams);
|
|
12608
|
+
let needsPostprocessing = formattedPostprocessingParams.size > 0;
|
|
12609
|
+
for (const values of context.params.values()) {
|
|
12610
|
+
if (values.length > 1) {
|
|
12611
|
+
needsPostprocessing = true;
|
|
12612
|
+
}
|
|
12613
|
+
}
|
|
12614
|
+
return createI18nMessageOp(job.allocateXrefId(), context.xref, context.i18nBlock, context.message, messagePlaceholder ?? null, formattedParams, formattedPostprocessingParams, needsPostprocessing);
|
|
12423
12615
|
}
|
|
12424
12616
|
/**
|
|
12425
12617
|
* Formats a map of `I18nParamValue[]` values into a map of `Expression` values.
|
|
12426
|
-
* @return A tuple of the formatted params and a boolean indicating whether postprocessing is needed
|
|
12427
|
-
* for any of the params
|
|
12428
12618
|
*/
|
|
12429
12619
|
function formatParams(params) {
|
|
12430
12620
|
const formattedParams = new Map();
|
|
12431
|
-
let needsPostprocessing = false;
|
|
12432
12621
|
for (const [placeholder, placeholderValues] of params) {
|
|
12433
|
-
const
|
|
12434
|
-
needsPostprocessing ||= paramNeedsPostprocessing;
|
|
12622
|
+
const serializedValues = formatParamValues(placeholderValues);
|
|
12435
12623
|
if (serializedValues !== null) {
|
|
12436
12624
|
formattedParams.set(placeholder, literal(serializedValues));
|
|
12437
12625
|
}
|
|
12438
12626
|
}
|
|
12439
|
-
return
|
|
12627
|
+
return formattedParams;
|
|
12440
12628
|
}
|
|
12441
12629
|
/**
|
|
12442
12630
|
* Formats an `I18nParamValue[]` into a string (or null for empty array).
|
|
12443
|
-
* @return A tuple of the formatted value and a boolean indicating whether postprocessing is needed
|
|
12444
|
-
* for the value
|
|
12445
12631
|
*/
|
|
12446
12632
|
function formatParamValues(values) {
|
|
12447
12633
|
if (values.length === 0) {
|
|
12448
|
-
return
|
|
12634
|
+
return null;
|
|
12449
12635
|
}
|
|
12450
|
-
collapseElementTemplatePairs(values);
|
|
12451
12636
|
const serializedValues = values.map(value => formatValue(value));
|
|
12452
12637
|
return serializedValues.length === 1 ?
|
|
12453
|
-
|
|
12454
|
-
|
|
12455
|
-
}
|
|
12456
|
-
/**
|
|
12457
|
-
* Collapses element/template pairs that refer to the same subTemplateIndex, i.e. elements and
|
|
12458
|
-
* templates that refer to the same element instance.
|
|
12459
|
-
*
|
|
12460
|
-
* This accounts for the case of a structural directive inside an i18n block, e.g.:
|
|
12461
|
-
* ```
|
|
12462
|
-
* <div i18n>
|
|
12463
|
-
* <div *ngIf="condition">
|
|
12464
|
-
* </div>
|
|
12465
|
-
* ```
|
|
12466
|
-
*
|
|
12467
|
-
* In this case, both the element start and template start placeholders are the same,
|
|
12468
|
-
* and we collapse them down into a single compound placeholder value. Rather than produce
|
|
12469
|
-
* `[\uFFFD#1:1\uFFFD|\uFFFD*2:1\uFFFD]`, we want to produce `\uFFFD#1:1\uFFFD\uFFFD*2:1\uFFFD`,
|
|
12470
|
-
* likewise for the closing of the element/template.
|
|
12471
|
-
*/
|
|
12472
|
-
function collapseElementTemplatePairs(values) {
|
|
12473
|
-
// Record the indicies of element and template values in the values array by subTemplateIndex.
|
|
12474
|
-
const valueIndiciesBySubTemplateIndex = new Map();
|
|
12475
|
-
for (let i = 0; i < values.length; i++) {
|
|
12476
|
-
const value = values[i];
|
|
12477
|
-
if (value.subTemplateIndex !== null &&
|
|
12478
|
-
(value.flags & (I18nParamValueFlags.ElementTag | I18nParamValueFlags.TemplateTag))) {
|
|
12479
|
-
const valueIndicies = valueIndiciesBySubTemplateIndex.get(value.subTemplateIndex) ?? [];
|
|
12480
|
-
valueIndicies.push(i);
|
|
12481
|
-
valueIndiciesBySubTemplateIndex.set(value.subTemplateIndex, valueIndicies);
|
|
12482
|
-
}
|
|
12483
|
-
}
|
|
12484
|
-
// For each subTemplateIndex, check if any values can be collapsed.
|
|
12485
|
-
for (const [subTemplateIndex, valueIndicies] of valueIndiciesBySubTemplateIndex) {
|
|
12486
|
-
if (valueIndicies.length > 1) {
|
|
12487
|
-
const elementIndex = valueIndicies.find(index => values[index].flags & I18nParamValueFlags.ElementTag);
|
|
12488
|
-
const templateIndex = valueIndicies.find(index => values[index].flags & I18nParamValueFlags.TemplateTag);
|
|
12489
|
-
// If the values list contains both an element and template value, we can collapse.
|
|
12490
|
-
if (elementIndex !== undefined && templateIndex !== undefined) {
|
|
12491
|
-
const elementValue = values[elementIndex];
|
|
12492
|
-
const templateValue = values[templateIndex];
|
|
12493
|
-
// To match the TemplateDefinitionBuilder output, flip the order depending on whether the
|
|
12494
|
-
// values represent a closing or opening tag (or both).
|
|
12495
|
-
// TODO(mmalerba): Figure out if this makes a difference in terms of either functionality,
|
|
12496
|
-
// or the resulting message ID. If not, we can remove the special-casing in the future.
|
|
12497
|
-
let compundValue;
|
|
12498
|
-
if ((elementValue.flags & I18nParamValueFlags.OpenTag) &&
|
|
12499
|
-
(elementValue.flags & I18nParamValueFlags.CloseTag)) {
|
|
12500
|
-
// TODO(mmalerba): Is this a TDB bug? I don't understand why it would put the template
|
|
12501
|
-
// value twice.
|
|
12502
|
-
compundValue = `${formatValue(templateValue)}${formatValue(elementValue)}${formatValue(templateValue)}`;
|
|
12503
|
-
}
|
|
12504
|
-
else if (elementValue.flags & I18nParamValueFlags.OpenTag) {
|
|
12505
|
-
compundValue = `${formatValue(templateValue)}${formatValue(elementValue)}`;
|
|
12506
|
-
}
|
|
12507
|
-
else {
|
|
12508
|
-
compundValue = `${formatValue(elementValue)}${formatValue(templateValue)}`;
|
|
12509
|
-
}
|
|
12510
|
-
// Replace the element value with the combined value.
|
|
12511
|
-
values.splice(elementIndex, 1, { value: compundValue, subTemplateIndex, flags: I18nParamValueFlags.None });
|
|
12512
|
-
// Replace the template value with null to preserve the indicies we calculated earlier.
|
|
12513
|
-
values.splice(templateIndex, 1, null);
|
|
12514
|
-
}
|
|
12515
|
-
}
|
|
12516
|
-
}
|
|
12517
|
-
// Strip out any nulled out values we introduced above.
|
|
12518
|
-
for (let i = values.length - 1; i >= 0; i--) {
|
|
12519
|
-
if (values[i] === null) {
|
|
12520
|
-
values.splice(i, 1);
|
|
12521
|
-
}
|
|
12522
|
-
}
|
|
12638
|
+
serializedValues[0] :
|
|
12639
|
+
`${LIST_START_MARKER}${serializedValues.join(LIST_DELIMITER)}${LIST_END_MARKER}`;
|
|
12523
12640
|
}
|
|
12524
12641
|
/**
|
|
12525
12642
|
* Formats a single `I18nParamValue` into a string
|
|
12526
12643
|
*/
|
|
12527
12644
|
function formatValue(value) {
|
|
12645
|
+
// Element tags with a structural directive use a special form that concatenates the element and
|
|
12646
|
+
// template values.
|
|
12647
|
+
if ((value.flags & I18nParamValueFlags.ElementTag) &&
|
|
12648
|
+
(value.flags & I18nParamValueFlags.TemplateTag)) {
|
|
12649
|
+
if (typeof value.value !== 'object') {
|
|
12650
|
+
throw Error('AssertionError: Expected i18n param value to have an element and template slot');
|
|
12651
|
+
}
|
|
12652
|
+
const elementValue = formatValue({
|
|
12653
|
+
...value,
|
|
12654
|
+
value: value.value.element,
|
|
12655
|
+
flags: value.flags & ~I18nParamValueFlags.TemplateTag
|
|
12656
|
+
});
|
|
12657
|
+
const templateValue = formatValue({
|
|
12658
|
+
...value,
|
|
12659
|
+
value: value.value.template,
|
|
12660
|
+
flags: value.flags & ~I18nParamValueFlags.ElementTag
|
|
12661
|
+
});
|
|
12662
|
+
// TODO(mmalerba): This is likely a bug in TemplateDefinitionBuilder, we should not need to
|
|
12663
|
+
// record the template value twice. For now I'm re-implementing the behavior here to keep the
|
|
12664
|
+
// output consistent with TemplateDefinitionBuilder.
|
|
12665
|
+
if ((value.flags & I18nParamValueFlags.OpenTag) &&
|
|
12666
|
+
(value.flags & I18nParamValueFlags.CloseTag)) {
|
|
12667
|
+
return `${templateValue}${elementValue}${templateValue}`;
|
|
12668
|
+
}
|
|
12669
|
+
// To match the TemplateDefinitionBuilder output, flip the order depending on whether the
|
|
12670
|
+
// values represent a closing or opening tag (or both).
|
|
12671
|
+
// TODO(mmalerba): Figure out if this makes a difference in terms of either functionality,
|
|
12672
|
+
// or the resulting message ID. If not, we can remove the special-casing in the future.
|
|
12673
|
+
return value.flags & I18nParamValueFlags.CloseTag ? `${elementValue}${templateValue}` :
|
|
12674
|
+
`${templateValue}${elementValue}`;
|
|
12675
|
+
}
|
|
12676
|
+
// Self-closing tags use a special form that concatenates the start and close tag values.
|
|
12677
|
+
if ((value.flags & I18nParamValueFlags.OpenTag) &&
|
|
12678
|
+
(value.flags & I18nParamValueFlags.CloseTag)) {
|
|
12679
|
+
return `${formatValue({ ...value, flags: value.flags & ~I18nParamValueFlags.CloseTag })}${formatValue({ ...value, flags: value.flags & ~I18nParamValueFlags.OpenTag })}`;
|
|
12680
|
+
}
|
|
12528
12681
|
// If there are no special flags, just return the raw value.
|
|
12529
12682
|
if (value.flags === I18nParamValueFlags.None) {
|
|
12530
12683
|
return `${value.value}`;
|
|
12531
12684
|
}
|
|
12685
|
+
// Encode the remaining flags as part of the value.
|
|
12532
12686
|
let tagMarker = '';
|
|
12533
12687
|
let closeMarker = '';
|
|
12534
12688
|
if (value.flags & I18nParamValueFlags.ElementTag) {
|
|
@@ -12541,12 +12695,7 @@ function formatValue(value) {
|
|
|
12541
12695
|
closeMarker = value.flags & I18nParamValueFlags.CloseTag ? TAG_CLOSE_MARKER : '';
|
|
12542
12696
|
}
|
|
12543
12697
|
const context = value.subTemplateIndex === null ? '' : `${CONTEXT_MARKER}${value.subTemplateIndex}`;
|
|
12544
|
-
|
|
12545
|
-
if ((value.flags & I18nParamValueFlags.OpenTag) &&
|
|
12546
|
-
(value.flags & I18nParamValueFlags.CloseTag)) {
|
|
12547
|
-
return `${ESCAPE}${tagMarker}${value.value}${context}${ESCAPE}${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
|
|
12548
|
-
}
|
|
12549
|
-
return `${ESCAPE}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE}`;
|
|
12698
|
+
return `${ESCAPE$1}${closeMarker}${tagMarker}${value.value}${context}${ESCAPE$1}`;
|
|
12550
12699
|
}
|
|
12551
12700
|
|
|
12552
12701
|
/**
|
|
@@ -12580,7 +12729,7 @@ function generateAdvance(job) {
|
|
|
12580
12729
|
else if (!slotMap.has(op.target)) {
|
|
12581
12730
|
// We expect ops that _do_ depend on the slot counter to point at declarations that exist in
|
|
12582
12731
|
// the `slotMap`.
|
|
12583
|
-
throw new Error(`AssertionError: reference to unknown slot for
|
|
12732
|
+
throw new Error(`AssertionError: reference to unknown slot for target ${op.target}`);
|
|
12584
12733
|
}
|
|
12585
12734
|
const slot = slotMap.get(op.target);
|
|
12586
12735
|
// Does the slot counter need to be adjusted?
|
|
@@ -19790,35 +19939,132 @@ const NG_I18N_CLOSURE_MODE$1 = 'ngI18nClosureMode';
|
|
|
19790
19939
|
* considers variables like `I18N_0` as constants and throws an error when their value changes.
|
|
19791
19940
|
*/
|
|
19792
19941
|
const TRANSLATION_VAR_PREFIX = 'i18n_';
|
|
19942
|
+
/** Prefix of ICU expressions for post processing */
|
|
19943
|
+
const I18N_ICU_MAPPING_PREFIX = 'I18N_EXP_';
|
|
19944
|
+
/**
|
|
19945
|
+
* The escape sequence used for message param values.
|
|
19946
|
+
*/
|
|
19947
|
+
const ESCAPE = '\uFFFD';
|
|
19793
19948
|
/**
|
|
19794
19949
|
* Lifts i18n properties into the consts array.
|
|
19795
19950
|
* TODO: Can we use `ConstCollectedExpr`?
|
|
19951
|
+
* TODO: The way the various attributes are linked together is very complex. Perhaps we could
|
|
19952
|
+
* simplify the process, maybe by combining the context and message ops?
|
|
19796
19953
|
*/
|
|
19797
19954
|
function collectI18nConsts(job) {
|
|
19798
19955
|
const fileBasedI18nSuffix = job.relativeContextFilePath.replace(/[^A-Za-z0-9]/g, '_').toUpperCase() + '_';
|
|
19799
|
-
|
|
19800
|
-
//
|
|
19956
|
+
// Step One: Build up various lookup maps we need to collect all the consts.
|
|
19957
|
+
// Context Xref -> Extracted Attribute Op
|
|
19958
|
+
const extractedAttributesByI18nContext = new Map();
|
|
19959
|
+
// Element/ElementStart Xref -> I18n Attributes config op
|
|
19960
|
+
const i18nAttributesByElement = new Map();
|
|
19961
|
+
// Element/ElementStart Xref -> All I18n Expression ops for attrs on that target
|
|
19962
|
+
const i18nExpressionsByElement = new Map();
|
|
19963
|
+
// I18n Message Xref -> I18n Message Op (TODO: use a central op map)
|
|
19801
19964
|
const messages = new Map();
|
|
19965
|
+
for (const unit of job.units) {
|
|
19966
|
+
for (const op of unit.ops()) {
|
|
19967
|
+
if (op.kind === OpKind.ExtractedAttribute && op.i18nContext !== null) {
|
|
19968
|
+
extractedAttributesByI18nContext.set(op.i18nContext, op);
|
|
19969
|
+
}
|
|
19970
|
+
else if (op.kind === OpKind.I18nAttributes) {
|
|
19971
|
+
i18nAttributesByElement.set(op.target, op);
|
|
19972
|
+
}
|
|
19973
|
+
else if (op.kind === OpKind.I18nExpression && op.usage === I18nExpressionFor.I18nAttribute) {
|
|
19974
|
+
const expressions = i18nExpressionsByElement.get(op.target) ?? [];
|
|
19975
|
+
expressions.push(op);
|
|
19976
|
+
i18nExpressionsByElement.set(op.target, expressions);
|
|
19977
|
+
}
|
|
19978
|
+
else if (op.kind === OpKind.I18nMessage) {
|
|
19979
|
+
messages.set(op.xref, op);
|
|
19980
|
+
}
|
|
19981
|
+
}
|
|
19982
|
+
}
|
|
19983
|
+
// Step Two: Serialize the extracted i18n messages for root i18n blocks and i18n attributes into
|
|
19984
|
+
// the const array.
|
|
19985
|
+
//
|
|
19986
|
+
// Also, each i18n message will have a variable expression that can refer to its
|
|
19987
|
+
// value. Store these expressions in the appropriate place:
|
|
19988
|
+
// 1. For normal i18n content, it also goes in the const array. We save the const index to use
|
|
19989
|
+
// later.
|
|
19990
|
+
// 2. For extracted attributes, it becomes the value of the extracted attribute instruction.
|
|
19991
|
+
// 3. For i18n bindings, it will go in a separate const array instruction below; for now, we just
|
|
19992
|
+
// save it.
|
|
19993
|
+
const i18nValuesByContext = new Map();
|
|
19994
|
+
const messageConstIndices = new Map();
|
|
19802
19995
|
for (const unit of job.units) {
|
|
19803
19996
|
for (const op of unit.create) {
|
|
19804
19997
|
if (op.kind === OpKind.I18nMessage) {
|
|
19805
|
-
|
|
19998
|
+
if (op.messagePlaceholder === null) {
|
|
19999
|
+
const { mainVar, statements } = collectMessage(job, fileBasedI18nSuffix, messages, op);
|
|
20000
|
+
if (op.i18nBlock !== null) {
|
|
20001
|
+
// This is a regular i18n message with a corresponding i18n block. Collect it into the
|
|
20002
|
+
// const array.
|
|
20003
|
+
const i18nConst = job.addConst(mainVar, statements);
|
|
20004
|
+
messageConstIndices.set(op.i18nBlock, i18nConst);
|
|
20005
|
+
}
|
|
20006
|
+
else {
|
|
20007
|
+
// This is an i18n attribute. Extract the initializers into the const pool.
|
|
20008
|
+
job.constsInitializers.push(...statements);
|
|
20009
|
+
// Save the i18n variable value for later.
|
|
20010
|
+
i18nValuesByContext.set(op.i18nContext, mainVar);
|
|
20011
|
+
// This i18n message may correspond to an individual extracted attribute. If so, The
|
|
20012
|
+
// value of that attribute is updated to read the extracted i18n variable.
|
|
20013
|
+
const attributeForMessage = extractedAttributesByI18nContext.get(op.i18nContext);
|
|
20014
|
+
if (attributeForMessage !== undefined) {
|
|
20015
|
+
attributeForMessage.expression = mainVar;
|
|
20016
|
+
}
|
|
20017
|
+
}
|
|
20018
|
+
}
|
|
19806
20019
|
OpList.remove(op);
|
|
19807
20020
|
}
|
|
19808
20021
|
}
|
|
19809
20022
|
}
|
|
19810
|
-
//
|
|
19811
|
-
|
|
19812
|
-
|
|
19813
|
-
|
|
19814
|
-
|
|
20023
|
+
// Step Three: Serialize I18nAttributes configurations into the const array. Each I18nAttributes
|
|
20024
|
+
// instruction has a config array, which contains k-v pairs describing each binding name, and the
|
|
20025
|
+
// i18n variable that provides the value.
|
|
20026
|
+
for (const unit of job.units) {
|
|
20027
|
+
for (const elem of unit.create) {
|
|
20028
|
+
if (isElementOrContainerOp(elem)) {
|
|
20029
|
+
const i18nAttributes = i18nAttributesByElement.get(elem.xref);
|
|
20030
|
+
if (i18nAttributes === undefined) {
|
|
20031
|
+
// This element is not associated with an i18n attributes configuration instruction.
|
|
20032
|
+
continue;
|
|
20033
|
+
}
|
|
20034
|
+
let i18nExpressions = i18nExpressionsByElement.get(elem.xref);
|
|
20035
|
+
if (i18nExpressions === undefined) {
|
|
20036
|
+
// Unused i18nAttributes should have already been removed.
|
|
20037
|
+
// TODO: Should the removal of those dead instructions be merged with this phase?
|
|
20038
|
+
throw new Error('AssertionError: Could not find any i18n expressions associated with an I18nAttributes instruction');
|
|
20039
|
+
}
|
|
20040
|
+
// Find expressions for all the unique property names, removing duplicates.
|
|
20041
|
+
const seenPropertyNames = new Set();
|
|
20042
|
+
i18nExpressions = i18nExpressions.filter(i18nExpr => {
|
|
20043
|
+
const seen = (seenPropertyNames.has(i18nExpr.name));
|
|
20044
|
+
seenPropertyNames.add(i18nExpr.name);
|
|
20045
|
+
return !seen;
|
|
20046
|
+
});
|
|
20047
|
+
const i18nAttributeConfig = i18nExpressions.flatMap(i18nExpr => {
|
|
20048
|
+
const i18nExprValue = i18nValuesByContext.get(i18nExpr.context);
|
|
20049
|
+
if (i18nExprValue === undefined) {
|
|
20050
|
+
throw new Error('AssertionError: Could not find i18n expression\'s value');
|
|
20051
|
+
}
|
|
20052
|
+
return [literal(i18nExpr.name), i18nExprValue];
|
|
20053
|
+
});
|
|
20054
|
+
i18nAttributes.i18nAttributesConfig =
|
|
20055
|
+
job.addConst(new LiteralArrayExpr(i18nAttributeConfig));
|
|
20056
|
+
}
|
|
19815
20057
|
}
|
|
19816
20058
|
}
|
|
19817
|
-
//
|
|
20059
|
+
// Step Four: Propagate the extracted const index into i18n ops that messages were extracted from.
|
|
19818
20060
|
for (const unit of job.units) {
|
|
19819
20061
|
for (const op of unit.create) {
|
|
19820
20062
|
if (op.kind === OpKind.I18nStart) {
|
|
19821
|
-
|
|
20063
|
+
const msgIndex = messageConstIndices.get(op.root);
|
|
20064
|
+
if (msgIndex === undefined) {
|
|
20065
|
+
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?');
|
|
20066
|
+
}
|
|
20067
|
+
op.messageIndex = msgIndex;
|
|
19822
20068
|
}
|
|
19823
20069
|
}
|
|
19824
20070
|
}
|
|
@@ -19828,18 +20074,23 @@ function collectI18nConsts(job) {
|
|
|
19828
20074
|
* This will recursively collect any sub-messages referenced from the parent message as well.
|
|
19829
20075
|
*/
|
|
19830
20076
|
function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
|
|
19831
|
-
// Recursively collect any sub-messages,
|
|
20077
|
+
// Recursively collect any sub-messages, record each sub-message's main variable under its
|
|
20078
|
+
// placeholder so that we can add them to the params for the parent message. It is possible
|
|
20079
|
+
// that multiple sub-messages will share the same placeholder, so we need to track an array of
|
|
20080
|
+
// variables for each placeholder.
|
|
19832
20081
|
const statements = [];
|
|
20082
|
+
const subMessagePlaceholders = new Map();
|
|
19833
20083
|
for (const subMessageId of messageOp.subMessages) {
|
|
19834
20084
|
const subMessage = messages.get(subMessageId);
|
|
19835
20085
|
const { mainVar: subMessageVar, statements: subMessageStatements } = collectMessage(job, fileBasedI18nSuffix, messages, subMessage);
|
|
19836
20086
|
statements.push(...subMessageStatements);
|
|
19837
|
-
|
|
20087
|
+
const subMessages = subMessagePlaceholders.get(subMessage.messagePlaceholder) ?? [];
|
|
20088
|
+
subMessages.push(subMessageVar);
|
|
20089
|
+
subMessagePlaceholders.set(subMessage.messagePlaceholder, subMessages);
|
|
19838
20090
|
}
|
|
20091
|
+
addSubMessageParams(messageOp, subMessagePlaceholders);
|
|
19839
20092
|
// Sort the params for consistency with TemaplateDefinitionBuilder output.
|
|
19840
20093
|
messageOp.params = new Map([...messageOp.params.entries()].sort());
|
|
19841
|
-
// Check that the message has all of its parameters filled out.
|
|
19842
|
-
assertAllParamsResolved(messageOp);
|
|
19843
20094
|
const mainVar = variable(job.pool.uniqueName(TRANSLATION_VAR_PREFIX));
|
|
19844
20095
|
// Closure Compiler requires const names to start with `MSG_` but disallows any other
|
|
19845
20096
|
// const to start with `MSG_`. We define a variable starting with `MSG_` just for the
|
|
@@ -19850,10 +20101,11 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
|
|
|
19850
20101
|
// set in post-processing.
|
|
19851
20102
|
if (messageOp.needsPostprocessing) {
|
|
19852
20103
|
// Sort the post-processing params for consistency with TemaplateDefinitionBuilder output.
|
|
19853
|
-
|
|
20104
|
+
const postprocessingParams = Object.fromEntries([...messageOp.postprocessingParams.entries()].sort());
|
|
20105
|
+
const formattedPostprocessingParams = formatI18nPlaceholderNamesInMap(postprocessingParams, /* useCamelCase */ false);
|
|
19854
20106
|
const extraTransformFnParams = [];
|
|
19855
20107
|
if (messageOp.postprocessingParams.size > 0) {
|
|
19856
|
-
extraTransformFnParams.push(
|
|
20108
|
+
extraTransformFnParams.push(mapLiteral(formattedPostprocessingParams, /* quoted */ true));
|
|
19857
20109
|
}
|
|
19858
20110
|
transformFn = (expr) => importExpr(Identifiers.i18nPostprocess).callFn([expr, ...extraTransformFnParams]);
|
|
19859
20111
|
}
|
|
@@ -19861,6 +20113,26 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
|
|
|
19861
20113
|
statements.push(...getTranslationDeclStmts$1(messageOp.message, mainVar, closureVar, messageOp.params, transformFn));
|
|
19862
20114
|
return { mainVar, statements };
|
|
19863
20115
|
}
|
|
20116
|
+
/**
|
|
20117
|
+
* Adds the given subMessage placeholders to the given message op.
|
|
20118
|
+
*
|
|
20119
|
+
* If a placeholder only corresponds to a single sub-message variable, we just set that variable
|
|
20120
|
+
* as the param value. However, if the placeholder corresponds to multiple sub-message
|
|
20121
|
+
* variables, we need to add a special placeholder value that is handled by the post-processing
|
|
20122
|
+
* step. We then add the array of variables as a post-processing param.
|
|
20123
|
+
*/
|
|
20124
|
+
function addSubMessageParams(messageOp, subMessagePlaceholders) {
|
|
20125
|
+
for (const [placeholder, subMessages] of subMessagePlaceholders) {
|
|
20126
|
+
if (subMessages.length === 1) {
|
|
20127
|
+
messageOp.params.set(placeholder, subMessages[0]);
|
|
20128
|
+
}
|
|
20129
|
+
else {
|
|
20130
|
+
messageOp.params.set(placeholder, literal(`${ESCAPE}${I18N_ICU_MAPPING_PREFIX}${placeholder}${ESCAPE}`));
|
|
20131
|
+
messageOp.postprocessingParams.set(placeholder, literalArr(subMessages));
|
|
20132
|
+
messageOp.needsPostprocessing = true;
|
|
20133
|
+
}
|
|
20134
|
+
}
|
|
20135
|
+
}
|
|
19864
20136
|
/**
|
|
19865
20137
|
* Generate statements that define a given translation message.
|
|
19866
20138
|
*
|
|
@@ -19883,7 +20155,8 @@ function collectMessage(job, fileBasedI18nSuffix, messages, messageOp) {
|
|
|
19883
20155
|
* @param closureVar The variable for Closure `goog.getMsg` calls, e.g. `MSG_EXTERNAL_XXX`.
|
|
19884
20156
|
* @param params Object mapping placeholder names to their values (e.g.
|
|
19885
20157
|
* `{ "interpolation": "\uFFFD0\uFFFD" }`).
|
|
19886
|
-
* @param transformFn Optional transformation function that will be applied to the translation
|
|
20158
|
+
* @param transformFn Optional transformation function that will be applied to the translation
|
|
20159
|
+
* (e.g.
|
|
19887
20160
|
* post-processing).
|
|
19888
20161
|
* @returns An array of statements that defined a given translation.
|
|
19889
20162
|
*/
|
|
@@ -19928,28 +20201,13 @@ function i18nGenerateClosureVar(pool, messageId, fileBasedI18nSuffix, useExterna
|
|
|
19928
20201
|
}
|
|
19929
20202
|
return variable(name);
|
|
19930
20203
|
}
|
|
19931
|
-
/**
|
|
19932
|
-
* Asserts that all of the message's placeholders have values.
|
|
19933
|
-
*/
|
|
19934
|
-
function assertAllParamsResolved(op) {
|
|
19935
|
-
for (let placeholder in op.message.placeholders) {
|
|
19936
|
-
placeholder = placeholder.trimEnd();
|
|
19937
|
-
if (!op.params.has(placeholder) && !op.postprocessingParams.has(placeholder)) {
|
|
19938
|
-
throw Error(`Failed to resolve i18n placeholder: ${placeholder}`);
|
|
19939
|
-
}
|
|
19940
|
-
}
|
|
19941
|
-
for (let placeholder in op.message.placeholderToMessage) {
|
|
19942
|
-
placeholder = placeholder.trimEnd();
|
|
19943
|
-
if (!op.params.has(placeholder) && !op.postprocessingParams.has(placeholder)) {
|
|
19944
|
-
throw Error(`Failed to resolve i18n message placeholder: ${placeholder}`);
|
|
19945
|
-
}
|
|
19946
|
-
}
|
|
19947
|
-
}
|
|
19948
20204
|
|
|
19949
20205
|
/**
|
|
19950
20206
|
* Removes text nodes within i18n blocks since they are already hardcoded into the i18n message.
|
|
20207
|
+
* Also, replaces interpolations on these text nodes with i18n expressions of the non-text portions,
|
|
20208
|
+
* which will be applied later.
|
|
19951
20209
|
*/
|
|
19952
|
-
function
|
|
20210
|
+
function convertI18nText(job) {
|
|
19953
20211
|
for (const unit of job.units) {
|
|
19954
20212
|
// Remove all text nodes within i18n blocks, their content is already captured in the i18n
|
|
19955
20213
|
// message.
|
|
@@ -20004,7 +20262,7 @@ function extractI18nText(job) {
|
|
|
20004
20262
|
const expr = op.interpolation.expressions[i];
|
|
20005
20263
|
// For now, this i18nExpression depends on the slot context of the enclosing i18n block.
|
|
20006
20264
|
// Later, we will modify this, and advance to a different point.
|
|
20007
|
-
ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.handle, expr, op.i18nPlaceholders[i], resolutionTime, expr.sourceSpan ?? op.sourceSpan));
|
|
20265
|
+
ops.push(createI18nExpressionOp(contextId, i18nOp.xref, i18nOp.xref, i18nOp.handle, expr, op.interpolation.i18nPlaceholders[i], resolutionTime, I18nExpressionFor.I18nText, '', expr.sourceSpan ?? op.sourceSpan));
|
|
20008
20266
|
}
|
|
20009
20267
|
OpList.replaceWithMany(op, ops);
|
|
20010
20268
|
break;
|
|
@@ -20542,14 +20800,14 @@ function parseExtractedStyles(job) {
|
|
|
20542
20800
|
if (op.name === 'style') {
|
|
20543
20801
|
const parsedStyles = parse(op.expression.value);
|
|
20544
20802
|
for (let i = 0; i < parsedStyles.length - 1; i += 2) {
|
|
20545
|
-
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1])), op);
|
|
20803
|
+
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.StyleProperty, parsedStyles[i], literal(parsedStyles[i + 1]), null), op);
|
|
20546
20804
|
}
|
|
20547
20805
|
OpList.remove(op);
|
|
20548
20806
|
}
|
|
20549
20807
|
else if (op.name === 'class') {
|
|
20550
20808
|
const parsedClasses = op.expression.value.trim().split(/\s+/g);
|
|
20551
20809
|
for (const parsedClass of parsedClasses) {
|
|
20552
|
-
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null), op);
|
|
20810
|
+
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.ClassName, parsedClass, null, null), op);
|
|
20553
20811
|
}
|
|
20554
20812
|
OpList.remove(op);
|
|
20555
20813
|
}
|
|
@@ -20565,18 +20823,30 @@ function parseExtractedStyles(job) {
|
|
|
20565
20823
|
function removeContentSelectors(job) {
|
|
20566
20824
|
for (const unit of job.units) {
|
|
20567
20825
|
const elements = createOpXrefMap(unit);
|
|
20568
|
-
for (const op of unit.
|
|
20826
|
+
for (const op of unit.ops()) {
|
|
20569
20827
|
switch (op.kind) {
|
|
20570
20828
|
case OpKind.Binding:
|
|
20571
20829
|
const target = lookupInXrefMap(elements, op.target);
|
|
20572
|
-
if (op.name
|
|
20830
|
+
if (isSelectAttribute(op.name) && target.kind === OpKind.Projection) {
|
|
20573
20831
|
OpList.remove(op);
|
|
20574
20832
|
}
|
|
20575
20833
|
break;
|
|
20834
|
+
case OpKind.Projection:
|
|
20835
|
+
// op.attributes is an array of [attr1-name, attr1-value, attr2-name, attr2-value, ...],
|
|
20836
|
+
// find the "select" attribute and remove its name and corresponding value.
|
|
20837
|
+
for (let i = op.attributes.length - 2; i >= 0; i -= 2) {
|
|
20838
|
+
if (isSelectAttribute(op.attributes[i])) {
|
|
20839
|
+
op.attributes.splice(i, 2);
|
|
20840
|
+
}
|
|
20841
|
+
}
|
|
20842
|
+
break;
|
|
20576
20843
|
}
|
|
20577
20844
|
}
|
|
20578
20845
|
}
|
|
20579
20846
|
}
|
|
20847
|
+
function isSelectAttribute(name) {
|
|
20848
|
+
return name.toLowerCase() === 'select';
|
|
20849
|
+
}
|
|
20580
20850
|
/**
|
|
20581
20851
|
* Looks up an element in the given map by xref ID.
|
|
20582
20852
|
*/
|
|
@@ -20696,6 +20966,10 @@ function propagateI18nBlocksToTemplates(unit, subTemplateIndex) {
|
|
|
20696
20966
|
i18nBlock = op;
|
|
20697
20967
|
break;
|
|
20698
20968
|
case OpKind.I18nEnd:
|
|
20969
|
+
// When we exit a root-level i18n block, reset the sub-template index counter.
|
|
20970
|
+
if (i18nBlock.subTemplateIndex === null) {
|
|
20971
|
+
subTemplateIndex = 0;
|
|
20972
|
+
}
|
|
20699
20973
|
i18nBlock = null;
|
|
20700
20974
|
break;
|
|
20701
20975
|
case OpKind.Template:
|
|
@@ -21037,6 +21311,10 @@ function i18n(slot, constIndex, subTemplateIndex) {
|
|
|
21037
21311
|
function i18nEnd() {
|
|
21038
21312
|
return call(Identifiers.i18nEnd, [], null);
|
|
21039
21313
|
}
|
|
21314
|
+
function i18nAttributes(slot, i18nAttributesConfig) {
|
|
21315
|
+
const args = [literal(slot), literal(i18nAttributesConfig)];
|
|
21316
|
+
return call(Identifiers.i18nAttributes, args, null);
|
|
21317
|
+
}
|
|
21040
21318
|
function property(name, expression, sanitizer, sourceSpan) {
|
|
21041
21319
|
const args = [literal(name), expression];
|
|
21042
21320
|
if (sanitizer !== null) {
|
|
@@ -21423,6 +21701,12 @@ function reifyCreateOperations(unit, ops) {
|
|
|
21423
21701
|
case OpKind.I18n:
|
|
21424
21702
|
OpList.replace(op, i18n(op.handle.slot, op.messageIndex, op.subTemplateIndex));
|
|
21425
21703
|
break;
|
|
21704
|
+
case OpKind.I18nAttributes:
|
|
21705
|
+
if (op.i18nAttributesConfig === null) {
|
|
21706
|
+
throw new Error(`AssertionError: i18nAttributesConfig was not set`);
|
|
21707
|
+
}
|
|
21708
|
+
OpList.replace(op, i18nAttributes(op.handle.slot, op.i18nAttributesConfig));
|
|
21709
|
+
break;
|
|
21426
21710
|
case OpKind.Template:
|
|
21427
21711
|
if (!(unit instanceof ViewCompilationUnit)) {
|
|
21428
21712
|
throw new Error(`AssertionError: must be compiling a component`);
|
|
@@ -21764,6 +22048,31 @@ function removeI18nContexts(job) {
|
|
|
21764
22048
|
}
|
|
21765
22049
|
}
|
|
21766
22050
|
|
|
22051
|
+
/**
|
|
22052
|
+
* i18nAttributes ops will be generated for each i18n attribute. However, not all i18n attribues
|
|
22053
|
+
* will contain dynamic content, and so some of these i18nAttributes ops may be unnecessary.
|
|
22054
|
+
*/
|
|
22055
|
+
function removeUnusedI18nAttributesOps(job) {
|
|
22056
|
+
for (const unit of job.units) {
|
|
22057
|
+
const ownersWithI18nExpressions = new Set();
|
|
22058
|
+
for (const op of unit.update) {
|
|
22059
|
+
switch (op.kind) {
|
|
22060
|
+
case OpKind.I18nExpression:
|
|
22061
|
+
ownersWithI18nExpressions.add(op.i18nOwner);
|
|
22062
|
+
}
|
|
22063
|
+
}
|
|
22064
|
+
for (const op of unit.create) {
|
|
22065
|
+
switch (op.kind) {
|
|
22066
|
+
case OpKind.I18nAttributes:
|
|
22067
|
+
if (ownersWithI18nExpressions.has(op.xref)) {
|
|
22068
|
+
continue;
|
|
22069
|
+
}
|
|
22070
|
+
OpList.remove(op);
|
|
22071
|
+
}
|
|
22072
|
+
}
|
|
22073
|
+
}
|
|
22074
|
+
}
|
|
22075
|
+
|
|
21767
22076
|
/**
|
|
21768
22077
|
* Inside the body of a repeater, certain context variables (such as `$first`) are ambiently
|
|
21769
22078
|
* available. This phase finds those variable usages, and replaces them with the appropriate
|
|
@@ -21895,10 +22204,14 @@ function resolveI18nElementPlaceholders(job) {
|
|
|
21895
22204
|
}
|
|
21896
22205
|
resolvePlaceholdersForView(job, job.root, i18nContexts, elements);
|
|
21897
22206
|
}
|
|
21898
|
-
|
|
22207
|
+
/**
|
|
22208
|
+
* Recursively resolves element and template tag placeholders in the given view.
|
|
22209
|
+
*/
|
|
22210
|
+
function resolvePlaceholdersForView(job, unit, i18nContexts, elements, pendingStructuralDirective) {
|
|
21899
22211
|
// Track the current i18n op and corresponding i18n context op as we step through the creation
|
|
21900
22212
|
// IR.
|
|
21901
22213
|
let currentOps = null;
|
|
22214
|
+
let pendingStructuralDirectiveCloses = new Map();
|
|
21902
22215
|
for (const op of unit.create) {
|
|
21903
22216
|
switch (op.kind) {
|
|
21904
22217
|
case OpKind.I18nStart:
|
|
@@ -21917,14 +22230,14 @@ function resolvePlaceholdersForView(job, unit, i18nContexts, elements) {
|
|
|
21917
22230
|
if (currentOps === null) {
|
|
21918
22231
|
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
21919
22232
|
}
|
|
21920
|
-
|
|
21921
|
-
|
|
21922
|
-
//
|
|
21923
|
-
|
|
21924
|
-
|
|
21925
|
-
flags |= I18nParamValueFlags.CloseTag;
|
|
22233
|
+
recordElementStart(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22234
|
+
// If there is a separate close tag placeholder for this element, save the pending
|
|
22235
|
+
// structural directive so we can pass it to the closing tag as well.
|
|
22236
|
+
if (pendingStructuralDirective && op.i18nPlaceholder.closeName) {
|
|
22237
|
+
pendingStructuralDirectiveCloses.set(op.xref, pendingStructuralDirective);
|
|
21926
22238
|
}
|
|
21927
|
-
|
|
22239
|
+
// Clear out the pending structural directive now that its been accounted for.
|
|
22240
|
+
pendingStructuralDirective = undefined;
|
|
21928
22241
|
}
|
|
21929
22242
|
break;
|
|
21930
22243
|
case OpKind.ElementEnd:
|
|
@@ -21933,42 +22246,134 @@ function resolvePlaceholdersForView(job, unit, i18nContexts, elements) {
|
|
|
21933
22246
|
const startOp = elements.get(op.xref);
|
|
21934
22247
|
if (startOp && startOp.i18nPlaceholder !== undefined) {
|
|
21935
22248
|
if (currentOps === null) {
|
|
21936
|
-
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
21937
|
-
}
|
|
21938
|
-
const { closeName } = startOp.i18nPlaceholder;
|
|
21939
|
-
// Self-closing tags don't have a closing tag placeholder.
|
|
21940
|
-
if (closeName !== '') {
|
|
21941
|
-
addParam(currentOps.i18nContext.params, closeName, startOp.handle.slot, currentOps.i18nBlock.subTemplateIndex, I18nParamValueFlags.ElementTag | I18nParamValueFlags.CloseTag);
|
|
22249
|
+
throw Error('AssertionError: i18n tag placeholder should only occur inside an i18n block');
|
|
21942
22250
|
}
|
|
22251
|
+
recordElementClose(startOp, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirectiveCloses.get(op.xref));
|
|
22252
|
+
// Clear out the pending structural directive close that was accounted for.
|
|
22253
|
+
pendingStructuralDirectiveCloses.delete(op.xref);
|
|
21943
22254
|
}
|
|
21944
22255
|
break;
|
|
21945
|
-
case OpKind.
|
|
21946
|
-
// For
|
|
21947
|
-
// corresponding
|
|
22256
|
+
case OpKind.Projection:
|
|
22257
|
+
// For content projections with i18n placeholders, record its slot value in the params map
|
|
22258
|
+
// under the corresponding tag start and close placeholders.
|
|
21948
22259
|
if (op.i18nPlaceholder !== undefined) {
|
|
21949
22260
|
if (currentOps === null) {
|
|
21950
22261
|
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
21951
22262
|
}
|
|
21952
|
-
|
|
21953
|
-
|
|
21954
|
-
|
|
21955
|
-
|
|
21956
|
-
|
|
21957
|
-
|
|
21958
|
-
|
|
21959
|
-
|
|
22263
|
+
recordElementStart(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22264
|
+
recordElementClose(op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22265
|
+
// Clear out the pending structural directive now that its been accounted for.
|
|
22266
|
+
pendingStructuralDirective = undefined;
|
|
22267
|
+
}
|
|
22268
|
+
break;
|
|
22269
|
+
case OpKind.Template:
|
|
22270
|
+
if (op.i18nPlaceholder === undefined) {
|
|
22271
|
+
// If there is no i18n placeholder, just recurse into the view in case it contains i18n
|
|
22272
|
+
// blocks.
|
|
21960
22273
|
resolvePlaceholdersForView(job, job.views.get(op.xref), i18nContexts, elements);
|
|
21961
|
-
if (!isSelfClosing) {
|
|
21962
|
-
addParam(currentOps.i18nContext.params, closeName, op.handle.slot, subTemplateIndex, I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag);
|
|
21963
|
-
}
|
|
21964
22274
|
}
|
|
21965
22275
|
else {
|
|
21966
|
-
|
|
22276
|
+
if (currentOps === null) {
|
|
22277
|
+
throw Error('i18n tag placeholder should only occur inside an i18n block');
|
|
22278
|
+
}
|
|
22279
|
+
if (op.templateKind === TemplateKind.Structural) {
|
|
22280
|
+
// If this is a structural directive template, don't record anything yet. Instead pass
|
|
22281
|
+
// the current template as a pending structural directive to be recorded when we find
|
|
22282
|
+
// the element, content, or template it belongs to. This allows us to create combined
|
|
22283
|
+
// values that represent, e.g. the start of a template and element at the same time.
|
|
22284
|
+
resolvePlaceholdersForView(job, job.views.get(op.xref), i18nContexts, elements, op);
|
|
22285
|
+
}
|
|
22286
|
+
else {
|
|
22287
|
+
// If this is some other kind of template, we can record its start, recurse into its
|
|
22288
|
+
// view, and then record its end.
|
|
22289
|
+
recordTemplateStart(job, op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22290
|
+
resolvePlaceholdersForView(job, job.views.get(op.xref), i18nContexts, elements);
|
|
22291
|
+
recordTemplateClose(job, op, currentOps.i18nContext, currentOps.i18nBlock, pendingStructuralDirective);
|
|
22292
|
+
pendingStructuralDirective = undefined;
|
|
22293
|
+
}
|
|
21967
22294
|
}
|
|
21968
22295
|
break;
|
|
21969
22296
|
}
|
|
21970
22297
|
}
|
|
21971
22298
|
}
|
|
22299
|
+
/**
|
|
22300
|
+
* Records an i18n param value for the start of an element.
|
|
22301
|
+
*/
|
|
22302
|
+
function recordElementStart(op, i18nContext, i18nBlock, structuralDirective) {
|
|
22303
|
+
const { startName, closeName } = op.i18nPlaceholder;
|
|
22304
|
+
let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.OpenTag;
|
|
22305
|
+
let value = op.handle.slot;
|
|
22306
|
+
// If the element is associated with a structural directive, start it as well.
|
|
22307
|
+
if (structuralDirective !== undefined) {
|
|
22308
|
+
flags |= I18nParamValueFlags.TemplateTag;
|
|
22309
|
+
value = { element: value, template: structuralDirective.handle.slot };
|
|
22310
|
+
}
|
|
22311
|
+
// For self-closing tags, there is no close tag placeholder. Instead, the start tag
|
|
22312
|
+
// placeholder accounts for the start and close of the element.
|
|
22313
|
+
if (!closeName) {
|
|
22314
|
+
flags |= I18nParamValueFlags.CloseTag;
|
|
22315
|
+
}
|
|
22316
|
+
addParam(i18nContext.params, startName, value, i18nBlock.subTemplateIndex, flags);
|
|
22317
|
+
}
|
|
22318
|
+
/**
|
|
22319
|
+
* Records an i18n param value for the closing of an element.
|
|
22320
|
+
*/
|
|
22321
|
+
function recordElementClose(op, i18nContext, i18nBlock, structuralDirective) {
|
|
22322
|
+
const { closeName } = op.i18nPlaceholder;
|
|
22323
|
+
// Self-closing tags don't have a closing tag placeholder, instead the element closing is
|
|
22324
|
+
// recorded via an additional flag on the element start value.
|
|
22325
|
+
if (closeName) {
|
|
22326
|
+
let flags = I18nParamValueFlags.ElementTag | I18nParamValueFlags.CloseTag;
|
|
22327
|
+
let value = op.handle.slot;
|
|
22328
|
+
// If the element is associated with a structural directive, close it as well.
|
|
22329
|
+
if (structuralDirective !== undefined) {
|
|
22330
|
+
flags |= I18nParamValueFlags.TemplateTag;
|
|
22331
|
+
value = { element: value, template: structuralDirective.handle.slot };
|
|
22332
|
+
}
|
|
22333
|
+
addParam(i18nContext.params, closeName, value, i18nBlock.subTemplateIndex, flags);
|
|
22334
|
+
}
|
|
22335
|
+
}
|
|
22336
|
+
/**
|
|
22337
|
+
* Records an i18n param value for the start of a template.
|
|
22338
|
+
*/
|
|
22339
|
+
function recordTemplateStart(job, op, i18nContext, i18nBlock, structuralDirective) {
|
|
22340
|
+
let { startName, closeName } = op.i18nPlaceholder;
|
|
22341
|
+
let flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.OpenTag;
|
|
22342
|
+
// For self-closing tags, there is no close tag placeholder. Instead, the start tag
|
|
22343
|
+
// placeholder accounts for the start and close of the element.
|
|
22344
|
+
if (!closeName) {
|
|
22345
|
+
flags |= I18nParamValueFlags.CloseTag;
|
|
22346
|
+
}
|
|
22347
|
+
// If the template is associated with a structural directive, record the structural directive's
|
|
22348
|
+
// start first. Since this template must be in the structural directive's view, we can just
|
|
22349
|
+
// directly use the current i18n block's sub-template index.
|
|
22350
|
+
if (structuralDirective !== undefined) {
|
|
22351
|
+
addParam(i18nContext.params, startName, structuralDirective.handle.slot, i18nBlock.subTemplateIndex, flags);
|
|
22352
|
+
}
|
|
22353
|
+
// Record the start of the template. For the sub-template index, pass the index for the template's
|
|
22354
|
+
// view, rather than the current i18n block's index.
|
|
22355
|
+
addParam(i18nContext.params, startName, op.handle.slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, op), flags);
|
|
22356
|
+
}
|
|
22357
|
+
/**
|
|
22358
|
+
* Records an i18n param value for the closing of a template.
|
|
22359
|
+
*/
|
|
22360
|
+
function recordTemplateClose(job, op, i18nContext, i18nBlock, structuralDirective) {
|
|
22361
|
+
const { startName, closeName } = op.i18nPlaceholder;
|
|
22362
|
+
const flags = I18nParamValueFlags.TemplateTag | I18nParamValueFlags.CloseTag;
|
|
22363
|
+
// Self-closing tags don't have a closing tag placeholder, instead the template's closing is
|
|
22364
|
+
// recorded via an additional flag on the template start value.
|
|
22365
|
+
if (closeName) {
|
|
22366
|
+
// Record the closing of the template. For the sub-template index, pass the index for the
|
|
22367
|
+
// template's view, rather than the current i18n block's index.
|
|
22368
|
+
addParam(i18nContext.params, closeName, op.handle.slot, getSubTemplateIndexForTemplateTag(job, i18nBlock, op), flags);
|
|
22369
|
+
// If the template is associated with a structural directive, record the structural directive's
|
|
22370
|
+
// closing after. Since this template must be in the structural directive's view, we can just
|
|
22371
|
+
// directly use the current i18n block's sub-template index.
|
|
22372
|
+
if (structuralDirective !== undefined) {
|
|
22373
|
+
addParam(i18nContext.params, closeName, structuralDirective.handle.slot, i18nBlock.subTemplateIndex, flags);
|
|
22374
|
+
}
|
|
22375
|
+
}
|
|
22376
|
+
}
|
|
21972
22377
|
/**
|
|
21973
22378
|
* Get the subTemplateIndex for the given template op. For template ops, use the subTemplateIndex of
|
|
21974
22379
|
* the child i18n block inside the template.
|
|
@@ -21981,7 +22386,9 @@ function getSubTemplateIndexForTemplateTag(job, i18nOp, op) {
|
|
|
21981
22386
|
}
|
|
21982
22387
|
return i18nOp.subTemplateIndex;
|
|
21983
22388
|
}
|
|
21984
|
-
/**
|
|
22389
|
+
/**
|
|
22390
|
+
* Add a param value to the given params map.
|
|
22391
|
+
*/
|
|
21985
22392
|
function addParam(params, placeholder, value, subTemplateIndex, flags) {
|
|
21986
22393
|
const values = params.get(placeholder) ?? [];
|
|
21987
22394
|
values.push({ value, subTemplateIndex, flags });
|
|
@@ -22007,14 +22414,19 @@ function resolveI18nExpressionPlaceholders(job) {
|
|
|
22007
22414
|
}
|
|
22008
22415
|
}
|
|
22009
22416
|
}
|
|
22010
|
-
// Keep track of the next available expression index
|
|
22417
|
+
// Keep track of the next available expression index for each i18n message.
|
|
22011
22418
|
const expressionIndices = new Map();
|
|
22419
|
+
// Keep track of a reference index for each expression.
|
|
22420
|
+
// We use different references for normal i18n expressio and attribute i18n expressions. This is
|
|
22421
|
+
// because child i18n blocks in templates don't get their own context, since they're rolled into
|
|
22422
|
+
// the translated message of the parent, but they may target a different slot.
|
|
22423
|
+
const referenceIndex = (op) => op.usage === I18nExpressionFor.I18nText ? op.i18nOwner : op.context;
|
|
22012
22424
|
for (const unit of job.units) {
|
|
22013
22425
|
for (const op of unit.update) {
|
|
22014
22426
|
if (op.kind === OpKind.I18nExpression) {
|
|
22015
22427
|
const i18nContext = i18nContexts.get(op.context);
|
|
22016
|
-
const index = expressionIndices.get(op
|
|
22017
|
-
const subTemplateIndex = subTemplateIndicies.get(op.
|
|
22428
|
+
const index = expressionIndices.get(referenceIndex(op)) || 0;
|
|
22429
|
+
const subTemplateIndex = subTemplateIndicies.get(op.i18nOwner) ?? null;
|
|
22018
22430
|
// Add the expression index in the appropriate params map.
|
|
22019
22431
|
const params = op.resolutionTime === I18nParamResolutionTime.Creation ?
|
|
22020
22432
|
i18nContext.params :
|
|
@@ -22026,7 +22438,7 @@ function resolveI18nExpressionPlaceholders(job) {
|
|
|
22026
22438
|
flags: I18nParamValueFlags.ExpressionIndex
|
|
22027
22439
|
});
|
|
22028
22440
|
params.set(op.i18nPlaceholder, values);
|
|
22029
|
-
expressionIndices.set(op
|
|
22441
|
+
expressionIndices.set(referenceIndex(op), index + 1);
|
|
22030
22442
|
}
|
|
22031
22443
|
}
|
|
22032
22444
|
}
|
|
@@ -23173,11 +23585,11 @@ const phases = [
|
|
|
23173
23585
|
{ kind: CompilationJobKind.Tmpl, fn: removeContentSelectors },
|
|
23174
23586
|
{ kind: CompilationJobKind.Host, fn: parseHostStyleProperties },
|
|
23175
23587
|
{ kind: CompilationJobKind.Tmpl, fn: emitNamespaceChanges },
|
|
23176
|
-
{ kind: CompilationJobKind.Both, fn: specializeStyleBindings },
|
|
23177
|
-
{ kind: CompilationJobKind.Both, fn: specializeBindings },
|
|
23178
23588
|
{ kind: CompilationJobKind.Tmpl, fn: propagateI18nBlocks },
|
|
23179
23589
|
{ kind: CompilationJobKind.Tmpl, fn: wrapI18nIcus },
|
|
23180
23590
|
{ kind: CompilationJobKind.Tmpl, fn: createI18nContexts },
|
|
23591
|
+
{ kind: CompilationJobKind.Both, fn: specializeStyleBindings },
|
|
23592
|
+
{ kind: CompilationJobKind.Both, fn: specializeBindings },
|
|
23181
23593
|
{ kind: CompilationJobKind.Both, fn: extractAttributes },
|
|
23182
23594
|
{ kind: CompilationJobKind.Both, fn: parseExtractedStyles },
|
|
23183
23595
|
{ kind: CompilationJobKind.Tmpl, fn: removeEmptyBindings },
|
|
@@ -23186,7 +23598,10 @@ const phases = [
|
|
|
23186
23598
|
{ kind: CompilationJobKind.Tmpl, fn: generateConditionalExpressions },
|
|
23187
23599
|
{ kind: CompilationJobKind.Tmpl, fn: createPipes },
|
|
23188
23600
|
{ kind: CompilationJobKind.Tmpl, fn: configureDeferInstructions },
|
|
23189
|
-
{ kind: CompilationJobKind.Tmpl, fn:
|
|
23601
|
+
{ kind: CompilationJobKind.Tmpl, fn: convertI18nText },
|
|
23602
|
+
{ kind: CompilationJobKind.Tmpl, fn: convertI18nBindings },
|
|
23603
|
+
{ kind: CompilationJobKind.Tmpl, fn: removeUnusedI18nAttributesOps },
|
|
23604
|
+
{ kind: CompilationJobKind.Tmpl, fn: assignI18nSlotDependencies },
|
|
23190
23605
|
{ kind: CompilationJobKind.Tmpl, fn: applyI18nExpressions },
|
|
23191
23606
|
{ kind: CompilationJobKind.Tmpl, fn: createVariadicPipes },
|
|
23192
23607
|
{ kind: CompilationJobKind.Both, fn: generatePureLiteralStructures },
|
|
@@ -23216,7 +23631,6 @@ const phases = [
|
|
|
23216
23631
|
{ kind: CompilationJobKind.Tmpl, fn: collectI18nConsts },
|
|
23217
23632
|
{ kind: CompilationJobKind.Tmpl, fn: collectConstExpressions },
|
|
23218
23633
|
{ kind: CompilationJobKind.Both, fn: collectElementConsts },
|
|
23219
|
-
{ kind: CompilationJobKind.Tmpl, fn: assignI18nSlotDependencies },
|
|
23220
23634
|
{ kind: CompilationJobKind.Tmpl, fn: removeI18nContexts },
|
|
23221
23635
|
{ kind: CompilationJobKind.Both, fn: countVariables },
|
|
23222
23636
|
{ kind: CompilationJobKind.Tmpl, fn: generateAdvance },
|
|
@@ -23371,7 +23785,7 @@ function ingestHostProperty(job, property, isTextAttribute) {
|
|
|
23371
23785
|
let expression;
|
|
23372
23786
|
const ast = property.expression.ast;
|
|
23373
23787
|
if (ast instanceof Interpolation$1) {
|
|
23374
|
-
expression = new Interpolation(ast.strings, ast.expressions.map(expr => convertAst(expr, job, property.sourceSpan)));
|
|
23788
|
+
expression = new Interpolation(ast.strings, ast.expressions.map(expr => convertAst(expr, job, property.sourceSpan)), []);
|
|
23375
23789
|
}
|
|
23376
23790
|
else {
|
|
23377
23791
|
expression = convertAst(ast, job, property.sourceSpan);
|
|
@@ -23386,10 +23800,11 @@ function ingestHostProperty(job, property, isTextAttribute) {
|
|
|
23386
23800
|
bindingKind = BindingKind.Animation;
|
|
23387
23801
|
}
|
|
23388
23802
|
job.root.update.push(createBindingOp(job.root.xref, bindingKind, property.name, expression, null, SecurityContext
|
|
23389
|
-
.NONE /* TODO: what should we pass as security context? Passing NONE for now. */, isTextAttribute, false, property.sourceSpan));
|
|
23803
|
+
.NONE /* TODO: what should we pass as security context? Passing NONE for now. */, isTextAttribute, false, /* TODO: How do Host bindings handle i18n attrs? */ null, property.sourceSpan));
|
|
23390
23804
|
}
|
|
23391
23805
|
function ingestHostAttribute(job, name, value) {
|
|
23392
23806
|
const attrBinding = createBindingOp(job.root.xref, BindingKind.Attribute, name, value, null, SecurityContext.NONE, true, false,
|
|
23807
|
+
/* TODO */ null,
|
|
23393
23808
|
/* TODO: host attribute source spans */ null);
|
|
23394
23809
|
job.root.update.push(attrBinding);
|
|
23395
23810
|
}
|
|
@@ -23487,10 +23902,11 @@ function ingestTemplate(unit, tmpl) {
|
|
|
23487
23902
|
const functionNameSuffix = tagNameWithoutNamespace === null ?
|
|
23488
23903
|
'' :
|
|
23489
23904
|
prefixWithNamespace(tagNameWithoutNamespace, namespace);
|
|
23490
|
-
const
|
|
23491
|
-
|
|
23492
|
-
|
|
23493
|
-
|
|
23905
|
+
const templateKind = isPlainTemplate(tmpl) ? TemplateKind.NgTemplate : TemplateKind.Structural;
|
|
23906
|
+
const templateOp = createTemplateOp(childView.xref, templateKind, tagNameWithoutNamespace, functionNameSuffix, namespace, i18nPlaceholder, tmpl.startSourceSpan);
|
|
23907
|
+
unit.create.push(templateOp);
|
|
23908
|
+
ingestBindings(unit, templateOp, tmpl);
|
|
23909
|
+
ingestReferences(templateOp, tmpl);
|
|
23494
23910
|
ingestNodes(childView, tmpl.children);
|
|
23495
23911
|
for (const { name, value } of tmpl.variables) {
|
|
23496
23912
|
childView.contextVariables.set(name, value !== '' ? value : '$implicit');
|
|
@@ -23498,7 +23914,7 @@ function ingestTemplate(unit, tmpl) {
|
|
|
23498
23914
|
// If this is a plain template and there is an i18n message associated with it, insert i18n start
|
|
23499
23915
|
// and end ops. For structural directive templates, the i18n ops will be added when ingesting the
|
|
23500
23916
|
// element/template the directive is placed on.
|
|
23501
|
-
if (
|
|
23917
|
+
if (templateKind === TemplateKind.NgTemplate && tmpl.i18n instanceof Message) {
|
|
23502
23918
|
const id = unit.job.allocateXrefId();
|
|
23503
23919
|
OpList.insertAfter(createI18nStartOp(id, tmpl.i18n), childView.create.head);
|
|
23504
23920
|
OpList.insertBefore(createI18nEndOp(id), childView.create.tail);
|
|
@@ -23508,9 +23924,13 @@ function ingestTemplate(unit, tmpl) {
|
|
|
23508
23924
|
* Ingest a literal text node from the AST into the given `ViewCompilation`.
|
|
23509
23925
|
*/
|
|
23510
23926
|
function ingestContent(unit, content) {
|
|
23511
|
-
|
|
23927
|
+
if (content.i18n !== undefined && !(content.i18n instanceof TagPlaceholder)) {
|
|
23928
|
+
throw Error(`Unhandled i18n metadata type for element: ${content.i18n.constructor.name}`);
|
|
23929
|
+
}
|
|
23930
|
+
const attrs = content.attributes.flatMap(a => [a.name, a.value]);
|
|
23931
|
+
const op = createProjectionOp(unit.job.allocateXrefId(), content.selector, content.i18n, attrs, content.sourceSpan);
|
|
23512
23932
|
for (const attr of content.attributes) {
|
|
23513
|
-
ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue);
|
|
23933
|
+
ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue, attr.i18n);
|
|
23514
23934
|
}
|
|
23515
23935
|
unit.create.push(op);
|
|
23516
23936
|
}
|
|
@@ -23535,6 +23955,7 @@ function ingestBoundText(unit, text, i18nPlaceholders) {
|
|
|
23535
23955
|
throw Error(`Unhandled i18n metadata type for text interpolation: ${text.i18n?.constructor.name}`);
|
|
23536
23956
|
}
|
|
23537
23957
|
if (i18nPlaceholders === undefined) {
|
|
23958
|
+
// TODO: We probably can just use the placeholders field, instead of walking the AST.
|
|
23538
23959
|
i18nPlaceholders = text.i18n instanceof Container ?
|
|
23539
23960
|
text.i18n.children
|
|
23540
23961
|
.filter((node) => node instanceof Placeholder)
|
|
@@ -23550,7 +23971,7 @@ function ingestBoundText(unit, text, i18nPlaceholders) {
|
|
|
23550
23971
|
// interpolation. We copy that behavior in compatibility mode.
|
|
23551
23972
|
// TODO: is it actually correct to generate these extra maps in modern mode?
|
|
23552
23973
|
const baseSourceSpan = unit.job.compatibility ? null : text.sourceSpan;
|
|
23553
|
-
unit.update.push(createInterpolateTextOp(textXref, new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, unit.job, baseSourceSpan))
|
|
23974
|
+
unit.update.push(createInterpolateTextOp(textXref, new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, unit.job, baseSourceSpan)), i18nPlaceholders), text.sourceSpan));
|
|
23554
23975
|
}
|
|
23555
23976
|
/**
|
|
23556
23977
|
* Ingest an `@if` block into the given `ViewCompilation`.
|
|
@@ -23571,14 +23992,21 @@ function ingestIfBlock(unit, ifBlock) {
|
|
|
23571
23992
|
if (ifCase.expressionAlias !== null) {
|
|
23572
23993
|
cView.contextVariables.set(ifCase.expressionAlias.name, CTX_REF);
|
|
23573
23994
|
}
|
|
23574
|
-
|
|
23575
|
-
|
|
23995
|
+
let ifCaseI18nMeta = undefined;
|
|
23996
|
+
if (ifCase.i18n !== undefined) {
|
|
23997
|
+
if (!(ifCase.i18n instanceof BlockPlaceholder)) {
|
|
23998
|
+
throw Error(`Unhandled i18n metadata type for if block: ${ifCase.i18n?.constructor.name}`);
|
|
23999
|
+
}
|
|
24000
|
+
ifCaseI18nMeta = ifCase.i18n;
|
|
24001
|
+
}
|
|
24002
|
+
const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, tagName, 'Conditional', Namespace.HTML, ifCaseI18nMeta, ifCase.sourceSpan);
|
|
24003
|
+
unit.create.push(templateOp);
|
|
23576
24004
|
if (firstXref === null) {
|
|
23577
24005
|
firstXref = cView.xref;
|
|
23578
|
-
firstSlotHandle =
|
|
24006
|
+
firstSlotHandle = templateOp.handle;
|
|
23579
24007
|
}
|
|
23580
24008
|
const caseExpr = ifCase.expression ? convertAst(ifCase.expression, unit.job, null) : null;
|
|
23581
|
-
const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr,
|
|
24009
|
+
const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, templateOp.xref, templateOp.handle, ifCase.expressionAlias);
|
|
23582
24010
|
conditions.push(conditionalCaseExpr);
|
|
23583
24011
|
ingestNodes(cView, ifCase.children);
|
|
23584
24012
|
}
|
|
@@ -23594,16 +24022,23 @@ function ingestSwitchBlock(unit, switchBlock) {
|
|
|
23594
24022
|
let conditions = [];
|
|
23595
24023
|
for (const switchCase of switchBlock.cases) {
|
|
23596
24024
|
const cView = unit.job.allocateView(unit.xref);
|
|
23597
|
-
|
|
23598
|
-
|
|
24025
|
+
let switchCaseI18nMeta = undefined;
|
|
24026
|
+
if (switchCase.i18n !== undefined) {
|
|
24027
|
+
if (!(switchCase.i18n instanceof BlockPlaceholder)) {
|
|
24028
|
+
throw Error(`Unhandled i18n metadata type for switch block: ${switchCase.i18n?.constructor.name}`);
|
|
24029
|
+
}
|
|
24030
|
+
switchCaseI18nMeta = switchCase.i18n;
|
|
24031
|
+
}
|
|
24032
|
+
const templateOp = createTemplateOp(cView.xref, TemplateKind.Block, null, 'Case', Namespace.HTML, switchCaseI18nMeta, switchCase.sourceSpan);
|
|
24033
|
+
unit.create.push(templateOp);
|
|
23599
24034
|
if (firstXref === null) {
|
|
23600
24035
|
firstXref = cView.xref;
|
|
23601
|
-
firstSlotHandle =
|
|
24036
|
+
firstSlotHandle = templateOp.handle;
|
|
23602
24037
|
}
|
|
23603
24038
|
const caseExpr = switchCase.expression ?
|
|
23604
24039
|
convertAst(switchCase.expression, unit.job, switchBlock.startSourceSpan) :
|
|
23605
24040
|
null;
|
|
23606
|
-
const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr,
|
|
24041
|
+
const conditionalCaseExpr = new ConditionalCaseExpr(caseExpr, templateOp.xref, templateOp.handle);
|
|
23607
24042
|
conditions.push(conditionalCaseExpr);
|
|
23608
24043
|
ingestNodes(cView, switchCase.children);
|
|
23609
24044
|
}
|
|
@@ -23616,7 +24051,7 @@ function ingestDeferView(unit, suffix, children, sourceSpan) {
|
|
|
23616
24051
|
}
|
|
23617
24052
|
const secondaryView = unit.job.allocateView(unit.xref);
|
|
23618
24053
|
ingestNodes(secondaryView, children);
|
|
23619
|
-
const templateOp = createTemplateOp(secondaryView.xref, null, `Defer${suffix}`, Namespace.HTML, undefined, sourceSpan);
|
|
24054
|
+
const templateOp = createTemplateOp(secondaryView.xref, TemplateKind.Block, null, `Defer${suffix}`, Namespace.HTML, undefined, sourceSpan);
|
|
23620
24055
|
unit.create.push(templateOp);
|
|
23621
24056
|
return templateOp;
|
|
23622
24057
|
}
|
|
@@ -23711,12 +24146,7 @@ function ingestIcu(unit, icu) {
|
|
|
23711
24146
|
const xref = unit.job.allocateXrefId();
|
|
23712
24147
|
const icuNode = icu.i18n.nodes[0];
|
|
23713
24148
|
unit.create.push(createIcuStartOp(xref, icu.i18n, icuFromI18nMessage(icu.i18n).name, null));
|
|
23714
|
-
const
|
|
23715
|
-
if (expressionPlaceholder === undefined || icu.vars[expressionPlaceholder] === undefined) {
|
|
23716
|
-
throw Error('ICU should have a text binding');
|
|
23717
|
-
}
|
|
23718
|
-
ingestBoundText(unit, icu.vars[expressionPlaceholder], [expressionPlaceholder]);
|
|
23719
|
-
for (const [placeholder, text] of Object.entries(icu.placeholders)) {
|
|
24149
|
+
for (const [placeholder, text] of Object.entries({ ...icu.vars, ...icu.placeholders })) {
|
|
23720
24150
|
if (text instanceof BoundText) {
|
|
23721
24151
|
ingestBoundText(unit, text, [placeholder]);
|
|
23722
24152
|
}
|
|
@@ -23812,6 +24242,16 @@ function convertAst(ast, job, baseSourceSpan) {
|
|
|
23812
24242
|
else if (ast instanceof LiteralPrimitive) {
|
|
23813
24243
|
return literal(ast.value, undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
23814
24244
|
}
|
|
24245
|
+
else if (ast instanceof Unary) {
|
|
24246
|
+
switch (ast.operator) {
|
|
24247
|
+
case '+':
|
|
24248
|
+
return new UnaryOperatorExpr(UnaryOperator.Plus, convertAst(ast.expr, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
24249
|
+
case '-':
|
|
24250
|
+
return new UnaryOperatorExpr(UnaryOperator.Minus, convertAst(ast.expr, job, baseSourceSpan), undefined, convertSourceSpan(ast.span, baseSourceSpan));
|
|
24251
|
+
default:
|
|
24252
|
+
throw new Error(`AssertionError: unknown unary operator ${ast.operator}`);
|
|
24253
|
+
}
|
|
24254
|
+
}
|
|
23815
24255
|
else if (ast instanceof Binary) {
|
|
23816
24256
|
const operator = BINARY_OPERATORS.get(ast.operation);
|
|
23817
24257
|
if (operator === undefined) {
|
|
@@ -23870,6 +24310,9 @@ function convertAst(ast, job, baseSourceSpan) {
|
|
|
23870
24310
|
else if (ast instanceof EmptyExpr$1) {
|
|
23871
24311
|
return new EmptyExpr(convertSourceSpan(ast.span, baseSourceSpan));
|
|
23872
24312
|
}
|
|
24313
|
+
else if (ast instanceof PrefixNot) {
|
|
24314
|
+
return not(convertAst(ast.expression, job, baseSourceSpan), convertSourceSpan(ast.span, baseSourceSpan));
|
|
24315
|
+
}
|
|
23873
24316
|
else {
|
|
23874
24317
|
throw new Error(`Unhandled expression type "${ast.constructor.name}" in file "${baseSourceSpan?.start.file.url}"`);
|
|
23875
24318
|
}
|
|
@@ -23900,6 +24343,7 @@ function isPlainTemplate(tmpl) {
|
|
|
23900
24343
|
*/
|
|
23901
24344
|
function ingestBindings(unit, op, element) {
|
|
23902
24345
|
let flags = BindingFlags.None;
|
|
24346
|
+
let hasI18nAttributes = false;
|
|
23903
24347
|
if (element instanceof Template) {
|
|
23904
24348
|
flags |= BindingFlags.OnNgTemplateElement;
|
|
23905
24349
|
if (element instanceof Template && isPlainTemplate(element)) {
|
|
@@ -23908,10 +24352,12 @@ function ingestBindings(unit, op, element) {
|
|
|
23908
24352
|
const templateAttrFlags = flags | BindingFlags.BindingTargetsTemplate | BindingFlags.IsStructuralTemplateAttribute;
|
|
23909
24353
|
for (const attr of element.templateAttrs) {
|
|
23910
24354
|
if (attr instanceof TextAttribute) {
|
|
23911
|
-
ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, templateAttrFlags | BindingFlags.TextValue);
|
|
24355
|
+
ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, templateAttrFlags | BindingFlags.TextValue, attr.i18n);
|
|
24356
|
+
hasI18nAttributes ||= attr.i18n !== undefined;
|
|
23912
24357
|
}
|
|
23913
24358
|
else {
|
|
23914
|
-
ingestBinding(unit, op.xref, attr.name, attr.value, attr.type, attr.unit, attr.securityContext, attr.sourceSpan, templateAttrFlags);
|
|
24359
|
+
ingestBinding(unit, op.xref, attr.name, attr.value, attr.type, attr.unit, attr.securityContext, attr.sourceSpan, templateAttrFlags, attr.i18n);
|
|
24360
|
+
hasI18nAttributes ||= attr.i18n !== undefined;
|
|
23915
24361
|
}
|
|
23916
24362
|
}
|
|
23917
24363
|
}
|
|
@@ -23919,10 +24365,12 @@ function ingestBindings(unit, op, element) {
|
|
|
23919
24365
|
// This is only attribute TextLiteral bindings, such as `attr.foo="bar"`. This can never be
|
|
23920
24366
|
// `[attr.foo]="bar"` or `attr.foo="{{bar}}"`, both of which will be handled as inputs with
|
|
23921
24367
|
// `BindingType.Attribute`.
|
|
23922
|
-
ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, flags | BindingFlags.TextValue);
|
|
24368
|
+
ingestBinding(unit, op.xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, flags | BindingFlags.TextValue, attr.i18n);
|
|
24369
|
+
hasI18nAttributes ||= attr.i18n !== undefined;
|
|
23923
24370
|
}
|
|
23924
24371
|
for (const input of element.inputs) {
|
|
23925
|
-
ingestBinding(unit, op.xref, input.name, input.value, input.type, input.unit, input.securityContext, input.sourceSpan, flags);
|
|
24372
|
+
ingestBinding(unit, op.xref, input.name, input.value, input.type, input.unit, input.securityContext, input.sourceSpan, flags, input.i18n);
|
|
24373
|
+
hasI18nAttributes ||= input.i18n !== undefined;
|
|
23926
24374
|
}
|
|
23927
24375
|
for (const output of element.outputs) {
|
|
23928
24376
|
let listenerOp;
|
|
@@ -23932,7 +24380,7 @@ function ingestBindings(unit, op, element) {
|
|
|
23932
24380
|
}
|
|
23933
24381
|
}
|
|
23934
24382
|
if (element instanceof Template && !isPlainTemplate(element)) {
|
|
23935
|
-
unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null));
|
|
24383
|
+
unit.create.push(createExtractedAttributeOp(op.xref, BindingKind.Property, output.name, null, null));
|
|
23936
24384
|
continue;
|
|
23937
24385
|
}
|
|
23938
24386
|
listenerOp = createListenerOp(op.xref, op.handle, output.name, op.tag, output.phase, false, output.sourceSpan);
|
|
@@ -23961,6 +24409,10 @@ function ingestBindings(unit, op, element) {
|
|
|
23961
24409
|
listenerOp.handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
|
|
23962
24410
|
unit.create.push(listenerOp);
|
|
23963
24411
|
}
|
|
24412
|
+
// TODO: Perhaps we could do this in a phase? (It likely wouldn't change the slot indices.)
|
|
24413
|
+
if (hasI18nAttributes) {
|
|
24414
|
+
unit.create.push(createI18nAttributesOp(unit.job.allocateXrefId(), new SlotHandle(), op.xref));
|
|
24415
|
+
}
|
|
23964
24416
|
}
|
|
23965
24417
|
const BINDING_KINDS = new Map([
|
|
23966
24418
|
[0 /* e.BindingType.Property */, BindingKind.Property],
|
|
@@ -23989,22 +24441,34 @@ var BindingFlags;
|
|
|
23989
24441
|
*/
|
|
23990
24442
|
BindingFlags[BindingFlags["OnNgTemplateElement"] = 8] = "OnNgTemplateElement";
|
|
23991
24443
|
})(BindingFlags || (BindingFlags = {}));
|
|
23992
|
-
function ingestBinding(view, xref, name, value, type, unit, securityContext, sourceSpan, flags) {
|
|
24444
|
+
function ingestBinding(view, xref, name, value, type, unit, securityContext, sourceSpan, flags, i18nMeta) {
|
|
23993
24445
|
if (value instanceof ASTWithSource) {
|
|
23994
24446
|
value = value.ast;
|
|
23995
24447
|
}
|
|
24448
|
+
let i18nContext = null;
|
|
24449
|
+
if (i18nMeta !== undefined) {
|
|
24450
|
+
if (!(i18nMeta instanceof Message)) {
|
|
24451
|
+
throw Error(`Unhandled i18n metadata type for binding: ${i18nMeta.constructor.name}`);
|
|
24452
|
+
}
|
|
24453
|
+
i18nContext = view.job.allocateXrefId();
|
|
24454
|
+
view.create.push(createI18nContextOp(I18nContextKind.Attr, i18nContext, null, i18nMeta, null));
|
|
24455
|
+
}
|
|
23996
24456
|
if (flags & BindingFlags.OnNgTemplateElement && !(flags & BindingFlags.BindingTargetsTemplate) &&
|
|
23997
24457
|
type === 0 /* e.BindingType.Property */) {
|
|
23998
24458
|
// This binding only exists for later const extraction, and is not an actual binding to be
|
|
23999
24459
|
// created.
|
|
24000
|
-
view.create.push(createExtractedAttributeOp(xref, BindingKind.Property, name, null));
|
|
24460
|
+
view.create.push(createExtractedAttributeOp(xref, BindingKind.Property, name, null, i18nContext));
|
|
24001
24461
|
return;
|
|
24002
24462
|
}
|
|
24003
24463
|
let expression;
|
|
24004
24464
|
// TODO: We could easily generate source maps for subexpressions in these cases, but
|
|
24005
24465
|
// TemplateDefinitionBuilder does not. Should we do so?
|
|
24006
24466
|
if (value instanceof Interpolation$1) {
|
|
24007
|
-
|
|
24467
|
+
let i18nPlaceholders = [];
|
|
24468
|
+
if (i18nMeta !== undefined) {
|
|
24469
|
+
i18nPlaceholders = Object.keys(i18nMeta.placeholders);
|
|
24470
|
+
}
|
|
24471
|
+
expression = new Interpolation(value.strings, value.expressions.map(expr => convertAst(expr, view.job, null)), i18nPlaceholders);
|
|
24008
24472
|
}
|
|
24009
24473
|
else if (value instanceof AST) {
|
|
24010
24474
|
expression = convertAst(value, view.job, null);
|
|
@@ -24013,7 +24477,7 @@ function ingestBinding(view, xref, name, value, type, unit, securityContext, sou
|
|
|
24013
24477
|
expression = value;
|
|
24014
24478
|
}
|
|
24015
24479
|
const kind = BINDING_KINDS.get(type);
|
|
24016
|
-
view.update.push(createBindingOp(xref, kind, name, expression, unit, securityContext, !!(flags & BindingFlags.TextValue), !!(flags & BindingFlags.IsStructuralTemplateAttribute), sourceSpan));
|
|
24480
|
+
view.update.push(createBindingOp(xref, kind, name, expression, unit, securityContext, !!(flags & BindingFlags.TextValue), !!(flags & BindingFlags.IsStructuralTemplateAttribute), i18nContext, sourceSpan));
|
|
24017
24481
|
}
|
|
24018
24482
|
/**
|
|
24019
24483
|
* Process all of the local references on an element-like structure in the template AST and
|
|
@@ -24101,7 +24565,7 @@ function ingestControlFlowInsertionPoint(unit, xref, node) {
|
|
|
24101
24565
|
// and they can be used in directive matching (in the case of `Template.templateAttrs`).
|
|
24102
24566
|
if (root !== null) {
|
|
24103
24567
|
for (const attr of root.attributes) {
|
|
24104
|
-
ingestBinding(unit, xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue);
|
|
24568
|
+
ingestBinding(unit, xref, attr.name, literal(attr.value), 1 /* e.BindingType.Attribute */, null, SecurityContext.NONE, attr.sourceSpan, BindingFlags.TextValue, attr.i18n);
|
|
24105
24569
|
}
|
|
24106
24570
|
const tagName = root instanceof Element$1 ? root.name : root.tagName;
|
|
24107
24571
|
// Don't pass along `ng-template` tag name since it enables directive matching.
|
|
@@ -27184,7 +27648,7 @@ class TemplateDefinitionBuilder {
|
|
|
27184
27648
|
else {
|
|
27185
27649
|
// ... otherwise we need to activate post-processing
|
|
27186
27650
|
// to replace ICU placeholders with proper values
|
|
27187
|
-
const placeholder = wrapI18nPlaceholder(`${I18N_ICU_MAPPING_PREFIX}${key}`);
|
|
27651
|
+
const placeholder = wrapI18nPlaceholder(`${I18N_ICU_MAPPING_PREFIX$1}${key}`);
|
|
27188
27652
|
params[key] = literal(placeholder);
|
|
27189
27653
|
icuMapping[key] = literalArr(refs);
|
|
27190
27654
|
}
|
|
@@ -29544,6 +30008,8 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
|
|
|
29544
30008
|
}
|
|
29545
30009
|
return emitHostBindingFunction(hostJob);
|
|
29546
30010
|
}
|
|
30011
|
+
let bindingId = 0;
|
|
30012
|
+
const getNextBindingId = () => `${bindingId++}`;
|
|
29547
30013
|
const bindingContext = variable(CONTEXT_NAME);
|
|
29548
30014
|
const styleBuilder = new StylingBuilder(bindingContext);
|
|
29549
30015
|
const { styleAttr, classAttr } = hostBindingsMetadata.specialAttributes;
|
|
@@ -29596,7 +30062,7 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
|
|
|
29596
30062
|
for (const binding of allOtherBindings) {
|
|
29597
30063
|
// resolve literal arrays and literal objects
|
|
29598
30064
|
const value = binding.expression.visit(getValueConverter());
|
|
29599
|
-
const bindingExpr = bindingFn(bindingContext, value);
|
|
30065
|
+
const bindingExpr = bindingFn(bindingContext, value, getNextBindingId);
|
|
29600
30066
|
const { bindingName, instruction, isAttribute } = getBindingNameAndInstruction(binding);
|
|
29601
30067
|
const securityContexts = bindingParser.calcPossibleSecurityContexts(selector, bindingName, isAttribute)
|
|
29602
30068
|
.filter(context => context !== SecurityContext.NONE);
|
|
@@ -29675,10 +30141,12 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
|
|
|
29675
30141
|
// at the top of this method when all the input bindings were counted.
|
|
29676
30142
|
totalHostVarsCount +=
|
|
29677
30143
|
Math.max(call.allocateBindingSlots - MIN_STYLING_BINDING_SLOTS_REQUIRED, 0);
|
|
30144
|
+
const { params, stmts } = convertStylingCall(call, bindingContext, bindingFn, getNextBindingId);
|
|
30145
|
+
updateVariables.push(...stmts);
|
|
29678
30146
|
updateInstructions.push({
|
|
29679
30147
|
reference: instruction.reference,
|
|
29680
|
-
paramsOrFn:
|
|
29681
|
-
span: null
|
|
30148
|
+
paramsOrFn: params,
|
|
30149
|
+
span: null,
|
|
29682
30150
|
});
|
|
29683
30151
|
}
|
|
29684
30152
|
});
|
|
@@ -29699,11 +30167,19 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
|
|
|
29699
30167
|
}
|
|
29700
30168
|
return null;
|
|
29701
30169
|
}
|
|
29702
|
-
function bindingFn(implicit, value) {
|
|
29703
|
-
return convertPropertyBinding(null, implicit, value,
|
|
30170
|
+
function bindingFn(implicit, value, getNextBindingIdFn) {
|
|
30171
|
+
return convertPropertyBinding(null, implicit, value, getNextBindingIdFn());
|
|
29704
30172
|
}
|
|
29705
|
-
function convertStylingCall(call, bindingContext, bindingFn) {
|
|
29706
|
-
|
|
30173
|
+
function convertStylingCall(call, bindingContext, bindingFn, getNextBindingIdFn) {
|
|
30174
|
+
const stmts = [];
|
|
30175
|
+
const params = call.params(value => {
|
|
30176
|
+
const result = bindingFn(bindingContext, value, getNextBindingIdFn);
|
|
30177
|
+
if (Array.isArray(result.stmts) && result.stmts.length > 0) {
|
|
30178
|
+
stmts.push(...result.stmts);
|
|
30179
|
+
}
|
|
30180
|
+
return result.currValExpr;
|
|
30181
|
+
});
|
|
30182
|
+
return { params, stmts };
|
|
29707
30183
|
}
|
|
29708
30184
|
function getBindingNameAndInstruction(binding) {
|
|
29709
30185
|
let bindingName = binding.name;
|
|
@@ -31279,7 +31755,7 @@ function publishFacade(global) {
|
|
|
31279
31755
|
* @description
|
|
31280
31756
|
* Entry point for all public APIs of the compiler package.
|
|
31281
31757
|
*/
|
|
31282
|
-
const VERSION = new Version('17.1.0-next.
|
|
31758
|
+
const VERSION = new Version('17.1.0-next.3');
|
|
31283
31759
|
|
|
31284
31760
|
class CompilerConfig {
|
|
31285
31761
|
constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
|
|
@@ -32845,7 +33321,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$6 = '12.0.0';
|
|
|
32845
33321
|
function compileDeclareClassMetadata(metadata) {
|
|
32846
33322
|
const definitionMap = new DefinitionMap();
|
|
32847
33323
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$6));
|
|
32848
|
-
definitionMap.set('version', literal('17.1.0-next.
|
|
33324
|
+
definitionMap.set('version', literal('17.1.0-next.3'));
|
|
32849
33325
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
32850
33326
|
definitionMap.set('type', metadata.type);
|
|
32851
33327
|
definitionMap.set('decorators', metadata.decorators);
|
|
@@ -32953,7 +33429,7 @@ function createDirectiveDefinitionMap(meta) {
|
|
|
32953
33429
|
// in 16.1 is actually used.
|
|
32954
33430
|
const minVersion = hasTransformFunctions ? MINIMUM_PARTIAL_LINKER_VERSION$5 : '14.0.0';
|
|
32955
33431
|
definitionMap.set('minVersion', literal(minVersion));
|
|
32956
|
-
definitionMap.set('version', literal('17.1.0-next.
|
|
33432
|
+
definitionMap.set('version', literal('17.1.0-next.3'));
|
|
32957
33433
|
// e.g. `type: MyDirective`
|
|
32958
33434
|
definitionMap.set('type', meta.type.value);
|
|
32959
33435
|
if (meta.isStandalone) {
|
|
@@ -33230,7 +33706,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
|
|
|
33230
33706
|
function compileDeclareFactoryFunction(meta) {
|
|
33231
33707
|
const definitionMap = new DefinitionMap();
|
|
33232
33708
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
|
|
33233
|
-
definitionMap.set('version', literal('17.1.0-next.
|
|
33709
|
+
definitionMap.set('version', literal('17.1.0-next.3'));
|
|
33234
33710
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33235
33711
|
definitionMap.set('type', meta.type.value);
|
|
33236
33712
|
definitionMap.set('deps', compileDependencies(meta.deps));
|
|
@@ -33265,7 +33741,7 @@ function compileDeclareInjectableFromMetadata(meta) {
|
|
|
33265
33741
|
function createInjectableDefinitionMap(meta) {
|
|
33266
33742
|
const definitionMap = new DefinitionMap();
|
|
33267
33743
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
|
|
33268
|
-
definitionMap.set('version', literal('17.1.0-next.
|
|
33744
|
+
definitionMap.set('version', literal('17.1.0-next.3'));
|
|
33269
33745
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33270
33746
|
definitionMap.set('type', meta.type.value);
|
|
33271
33747
|
// Only generate providedIn property if it has a non-null value
|
|
@@ -33316,7 +33792,7 @@ function compileDeclareInjectorFromMetadata(meta) {
|
|
|
33316
33792
|
function createInjectorDefinitionMap(meta) {
|
|
33317
33793
|
const definitionMap = new DefinitionMap();
|
|
33318
33794
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
|
|
33319
|
-
definitionMap.set('version', literal('17.1.0-next.
|
|
33795
|
+
definitionMap.set('version', literal('17.1.0-next.3'));
|
|
33320
33796
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33321
33797
|
definitionMap.set('type', meta.type.value);
|
|
33322
33798
|
definitionMap.set('providers', meta.providers);
|
|
@@ -33349,7 +33825,7 @@ function createNgModuleDefinitionMap(meta) {
|
|
|
33349
33825
|
throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
|
|
33350
33826
|
}
|
|
33351
33827
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
|
|
33352
|
-
definitionMap.set('version', literal('17.1.0-next.
|
|
33828
|
+
definitionMap.set('version', literal('17.1.0-next.3'));
|
|
33353
33829
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33354
33830
|
definitionMap.set('type', meta.type.value);
|
|
33355
33831
|
// We only generate the keys in the metadata if the arrays contain values.
|
|
@@ -33400,7 +33876,7 @@ function compileDeclarePipeFromMetadata(meta) {
|
|
|
33400
33876
|
function createPipeDefinitionMap(meta) {
|
|
33401
33877
|
const definitionMap = new DefinitionMap();
|
|
33402
33878
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
|
|
33403
|
-
definitionMap.set('version', literal('17.1.0-next.
|
|
33879
|
+
definitionMap.set('version', literal('17.1.0-next.3'));
|
|
33404
33880
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33405
33881
|
// e.g. `type: MyPipe`
|
|
33406
33882
|
definitionMap.set('type', meta.type.value);
|