@angular/compiler 17.2.0-next.0 → 17.2.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/src/compiler_util/expression_converter.mjs +98 -25
- package/esm2022/src/constant_pool.mjs +14 -4
- package/esm2022/src/expression_parser/ast.mjs +2 -3
- package/esm2022/src/expression_parser/parser.mjs +8 -27
- package/esm2022/src/jit_compiler_facade.mjs +9 -2
- 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/r3_class_metadata_compiler.mjs +5 -3
- package/esm2022/src/render3/r3_control_flow.mjs +39 -7
- package/esm2022/src/render3/r3_identifiers.mjs +6 -1
- package/esm2022/src/render3/r3_template_transform.mjs +6 -6
- package/esm2022/src/render3/view/api.mjs +1 -1
- package/esm2022/src/render3/view/compiler.mjs +12 -12
- package/esm2022/src/render3/view/template.mjs +21 -10
- package/esm2022/src/render3/view/util.mjs +4 -2
- package/esm2022/src/template/pipeline/ir/src/enums.mjs +27 -11
- package/esm2022/src/template/pipeline/ir/src/expression.mjs +32 -1
- package/esm2022/src/template/pipeline/ir/src/ops/create.mjs +21 -3
- package/esm2022/src/template/pipeline/ir/src/ops/update.mjs +22 -1
- package/esm2022/src/template/pipeline/src/compilation.mjs +4 -3
- package/esm2022/src/template/pipeline/src/emit.mjs +4 -2
- package/esm2022/src/template/pipeline/src/ingest.mjs +58 -15
- package/esm2022/src/template/pipeline/src/instruction.mjs +14 -1
- package/esm2022/src/template/pipeline/src/phases/attribute_extraction.mjs +15 -1
- package/esm2022/src/template/pipeline/src/phases/binding_specialization.mjs +11 -1
- package/esm2022/src/template/pipeline/src/phases/chaining.mjs +3 -1
- package/esm2022/src/template/pipeline/src/phases/const_collection.mjs +12 -5
- package/esm2022/src/template/pipeline/src/phases/create_defer_deps_fns.mjs +9 -3
- package/esm2022/src/template/pipeline/src/phases/generate_variables.mjs +2 -1
- package/esm2022/src/template/pipeline/src/phases/naming.mjs +14 -2
- package/esm2022/src/template/pipeline/src/phases/next_context_merging.mjs +2 -2
- package/esm2022/src/template/pipeline/src/phases/ordering.mjs +14 -5
- package/esm2022/src/template/pipeline/src/phases/reify.mjs +9 -1
- package/esm2022/src/template/pipeline/src/phases/resolve_contexts.mjs +2 -1
- package/esm2022/src/template/pipeline/src/phases/resolve_dollar_event.mjs +6 -3
- package/esm2022/src/template/pipeline/src/phases/resolve_names.mjs +3 -2
- package/esm2022/src/template/pipeline/src/phases/save_restore_view.mjs +2 -2
- package/esm2022/src/template/pipeline/src/phases/temporary_variables.mjs +2 -2
- package/esm2022/src/template/pipeline/src/phases/transform_two_way_binding_set.mjs +79 -0
- package/esm2022/src/template/pipeline/src/phases/var_counting.mjs +4 -1
- package/esm2022/src/template/pipeline/src/phases/variable_optimization.mjs +3 -3
- package/esm2022/src/template_parser/binding_parser.mjs +45 -17
- package/esm2022/src/version.mjs +1 -1
- package/fesm2022/compiler.mjs +599 -161
- package/fesm2022/compiler.mjs.map +1 -1
- package/index.d.ts +49 -223
- package/package.json +2 -2
package/fesm2022/compiler.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v17.2.0-
|
|
2
|
+
* @license Angular v17.2.0-rc.0
|
|
3
3
|
* (c) 2010-2022 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
@@ -2236,6 +2236,13 @@ class ConstantPool {
|
|
|
2236
2236
|
this.literals = new Map();
|
|
2237
2237
|
this.literalFactories = new Map();
|
|
2238
2238
|
this.sharedConstants = new Map();
|
|
2239
|
+
/**
|
|
2240
|
+
* Constant pool also tracks claimed names from {@link uniqueName}.
|
|
2241
|
+
* This is useful to avoid collisions if variables are intended to be
|
|
2242
|
+
* named a certain way- but may conflict. We wouldn't want to always suffix
|
|
2243
|
+
* them with unique numbers.
|
|
2244
|
+
*/
|
|
2245
|
+
this._claimedNames = new Map();
|
|
2239
2246
|
this.nextNameIndex = 0;
|
|
2240
2247
|
}
|
|
2241
2248
|
getConstLiteral(literal, forceShared) {
|
|
@@ -2358,14 +2365,17 @@ class ConstantPool {
|
|
|
2358
2365
|
return { literalFactory, literalFactoryArguments };
|
|
2359
2366
|
}
|
|
2360
2367
|
/**
|
|
2361
|
-
* Produce a unique name.
|
|
2368
|
+
* Produce a unique name in the context of this pool.
|
|
2362
2369
|
*
|
|
2363
2370
|
* The name might be unique among different prefixes if any of the prefixes end in
|
|
2364
2371
|
* a digit so the prefix should be a constant string (not based on user input) and
|
|
2365
2372
|
* must not end in a digit.
|
|
2366
2373
|
*/
|
|
2367
|
-
uniqueName(
|
|
2368
|
-
|
|
2374
|
+
uniqueName(name, alwaysIncludeSuffix = true) {
|
|
2375
|
+
const count = this._claimedNames.get(name) ?? 0;
|
|
2376
|
+
const result = count === 0 && !alwaysIncludeSuffix ? `${name}` : `${name}${count}`;
|
|
2377
|
+
this._claimedNames.set(name, count + 1);
|
|
2378
|
+
return result;
|
|
2369
2379
|
}
|
|
2370
2380
|
freshName() {
|
|
2371
2381
|
return this.uniqueName(CONSTANT_PREFIX);
|
|
@@ -2632,6 +2642,10 @@ class Identifiers {
|
|
|
2632
2642
|
static { this.viewQuerySignal = { name: 'ɵɵviewQuerySignal', moduleName: CORE }; }
|
|
2633
2643
|
static { this.contentQuerySignal = { name: 'ɵɵcontentQuerySignal', moduleName: CORE }; }
|
|
2634
2644
|
static { this.queryAdvance = { name: 'ɵɵqueryAdvance', moduleName: CORE }; }
|
|
2645
|
+
// Two-way bindings
|
|
2646
|
+
static { this.twoWayProperty = { name: 'ɵɵtwoWayProperty', moduleName: CORE }; }
|
|
2647
|
+
static { this.twoWayBindingSet = { name: 'ɵɵtwoWayBindingSet', moduleName: CORE }; }
|
|
2648
|
+
static { this.twoWayListener = { name: 'ɵɵtwoWayListener', moduleName: CORE }; }
|
|
2635
2649
|
static { this.NgOnChangesFeature = { name: 'ɵɵNgOnChangesFeature', moduleName: CORE }; }
|
|
2636
2650
|
static { this.InheritDefinitionFeature = { name: 'ɵɵInheritDefinitionFeature', moduleName: CORE }; }
|
|
2637
2651
|
static { this.CopyDefinitionFeature = { name: 'ɵɵCopyDefinitionFeature', moduleName: CORE }; }
|
|
@@ -2661,6 +2675,7 @@ class Identifiers {
|
|
|
2661
2675
|
// type-checking
|
|
2662
2676
|
static { this.InputSignalBrandWriteType = { name: 'ɵINPUT_SIGNAL_BRAND_WRITE_TYPE', moduleName: CORE }; }
|
|
2663
2677
|
static { this.UnwrapDirectiveSignalInputs = { name: 'ɵUnwrapDirectiveSignalInputs', moduleName: CORE }; }
|
|
2678
|
+
static { this.unwrapWritableSignal = { name: 'ɵunwrapWritableSignal', moduleName: CORE }; }
|
|
2664
2679
|
}
|
|
2665
2680
|
|
|
2666
2681
|
const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
|
|
@@ -4961,6 +4976,8 @@ const CHAINABLE_INSTRUCTIONS = new Set([
|
|
|
4961
4976
|
Identifiers.textInterpolate8,
|
|
4962
4977
|
Identifiers.textInterpolateV,
|
|
4963
4978
|
Identifiers.templateCreate,
|
|
4979
|
+
Identifiers.twoWayProperty,
|
|
4980
|
+
Identifiers.twoWayListener,
|
|
4964
4981
|
]);
|
|
4965
4982
|
/** Generates a call to a single instruction. */
|
|
4966
4983
|
function invokeInstruction(span, reference, params) {
|
|
@@ -5138,7 +5155,7 @@ function getAttrsForDirectiveMatching(elOrTpl) {
|
|
|
5138
5155
|
}
|
|
5139
5156
|
});
|
|
5140
5157
|
elOrTpl.inputs.forEach(i => {
|
|
5141
|
-
if (i.type === 0 /* BindingType.Property */) {
|
|
5158
|
+
if (i.type === 0 /* BindingType.Property */ || i.type === 5 /* BindingType.TwoWay */) {
|
|
5142
5159
|
attributesMap[i.name] = '';
|
|
5143
5160
|
}
|
|
5144
5161
|
});
|
|
@@ -6930,10 +6947,9 @@ var ParsedPropertyType;
|
|
|
6930
6947
|
ParsedPropertyType[ParsedPropertyType["DEFAULT"] = 0] = "DEFAULT";
|
|
6931
6948
|
ParsedPropertyType[ParsedPropertyType["LITERAL_ATTR"] = 1] = "LITERAL_ATTR";
|
|
6932
6949
|
ParsedPropertyType[ParsedPropertyType["ANIMATION"] = 2] = "ANIMATION";
|
|
6950
|
+
ParsedPropertyType[ParsedPropertyType["TWO_WAY"] = 3] = "TWO_WAY";
|
|
6933
6951
|
})(ParsedPropertyType || (ParsedPropertyType = {}));
|
|
6934
6952
|
class ParsedEvent {
|
|
6935
|
-
// Regular events have a target
|
|
6936
|
-
// Animation events have a phase
|
|
6937
6953
|
constructor(name, targetOrPhase, type, handler, sourceSpan, handlerSpan, keySpan) {
|
|
6938
6954
|
this.name = name;
|
|
6939
6955
|
this.targetOrPhase = targetOrPhase;
|
|
@@ -6977,32 +6993,10 @@ class EventHandlerVars {
|
|
|
6977
6993
|
* used in an action binding (e.g. an event handler).
|
|
6978
6994
|
*/
|
|
6979
6995
|
function convertActionBinding(localResolver, implicitReceiver, action, bindingId, baseSourceSpan, implicitReceiverAccesses, globals) {
|
|
6980
|
-
|
|
6981
|
-
localResolver = new DefaultLocalResolver(globals);
|
|
6982
|
-
}
|
|
6983
|
-
const actionWithoutBuiltins = convertPropertyBindingBuiltins({
|
|
6984
|
-
createLiteralArrayConverter: (argCount) => {
|
|
6985
|
-
// Note: no caching for literal arrays in actions.
|
|
6986
|
-
return (args) => literalArr(args);
|
|
6987
|
-
},
|
|
6988
|
-
createLiteralMapConverter: (keys) => {
|
|
6989
|
-
// Note: no caching for literal maps in actions.
|
|
6990
|
-
return (values) => {
|
|
6991
|
-
const entries = keys.map((k, i) => ({
|
|
6992
|
-
key: k.key,
|
|
6993
|
-
value: values[i],
|
|
6994
|
-
quoted: k.quoted,
|
|
6995
|
-
}));
|
|
6996
|
-
return literalMap(entries);
|
|
6997
|
-
};
|
|
6998
|
-
},
|
|
6999
|
-
createPipeConverter: (name) => {
|
|
7000
|
-
throw new Error(`Illegal State: Actions are not allowed to contain pipes. Pipe: ${name}`);
|
|
7001
|
-
}
|
|
7002
|
-
}, action);
|
|
6996
|
+
localResolver ??= new DefaultLocalResolver(globals);
|
|
7003
6997
|
const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, /* supportsInterpolation */ false, baseSourceSpan, implicitReceiverAccesses);
|
|
7004
6998
|
const actionStmts = [];
|
|
7005
|
-
flattenStatements(
|
|
6999
|
+
flattenStatements(convertActionBuiltins(action).visit(visitor, _Mode.Statement), actionStmts);
|
|
7006
7000
|
prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
|
|
7007
7001
|
if (visitor.usesImplicitReceiver) {
|
|
7008
7002
|
localResolver.notifyImplicitReceiverUse();
|
|
@@ -7017,6 +7011,77 @@ function convertActionBinding(localResolver, implicitReceiver, action, bindingId
|
|
|
7017
7011
|
}
|
|
7018
7012
|
return actionStmts;
|
|
7019
7013
|
}
|
|
7014
|
+
function convertAssignmentActionBinding(localResolver, implicitReceiver, action, bindingId, baseSourceSpan, implicitReceiverAccesses, globals) {
|
|
7015
|
+
localResolver ??= new DefaultLocalResolver(globals);
|
|
7016
|
+
const visitor = new _AstToIrVisitor(localResolver, implicitReceiver, bindingId, /* supportsInterpolation */ false, baseSourceSpan, implicitReceiverAccesses);
|
|
7017
|
+
let convertedAction = convertActionBuiltins(action).visit(visitor, _Mode.Statement);
|
|
7018
|
+
// This should already have been asserted in the parser, but we verify it here just in case.
|
|
7019
|
+
if (!(convertedAction instanceof ExpressionStatement)) {
|
|
7020
|
+
throw new Error(`Illegal state: unsupported expression in two-way action binding.`);
|
|
7021
|
+
}
|
|
7022
|
+
// Converts `[(ngModel)]="name"` to `twoWayBindingSet(ctx.name, $event) || (ctx.name = $event)`.
|
|
7023
|
+
convertedAction = wrapAssignmentAction(convertedAction.expr).toStmt();
|
|
7024
|
+
const actionStmts = [];
|
|
7025
|
+
flattenStatements(convertedAction, actionStmts);
|
|
7026
|
+
prependTemporaryDecls(visitor.temporaryCount, bindingId, actionStmts);
|
|
7027
|
+
// Assignment events always return `$event`.
|
|
7028
|
+
actionStmts.push(new ReturnStatement(EventHandlerVars.event));
|
|
7029
|
+
implicitReceiverAccesses?.add(EventHandlerVars.event.name);
|
|
7030
|
+
if (visitor.usesImplicitReceiver) {
|
|
7031
|
+
localResolver.notifyImplicitReceiverUse();
|
|
7032
|
+
}
|
|
7033
|
+
return actionStmts;
|
|
7034
|
+
}
|
|
7035
|
+
function wrapAssignmentReadExpression(ast) {
|
|
7036
|
+
return new ExternalExpr(Identifiers.twoWayBindingSet)
|
|
7037
|
+
.callFn([ast, EventHandlerVars.event])
|
|
7038
|
+
.or(ast.set(EventHandlerVars.event));
|
|
7039
|
+
}
|
|
7040
|
+
function isReadExpression$1(value) {
|
|
7041
|
+
return value instanceof ReadPropExpr || value instanceof ReadKeyExpr;
|
|
7042
|
+
}
|
|
7043
|
+
function wrapAssignmentAction(ast) {
|
|
7044
|
+
// The only officially supported expressions inside of a two-way binding are read expressions.
|
|
7045
|
+
if (isReadExpression$1(ast)) {
|
|
7046
|
+
return wrapAssignmentReadExpression(ast);
|
|
7047
|
+
}
|
|
7048
|
+
// However, historically the expression parser was handling two-way events by appending `=$event`
|
|
7049
|
+
// to the raw string before attempting to parse it. This has led to bugs over the years (see
|
|
7050
|
+
// #37809) and to unintentionally supporting unassignable events in the two-way binding. The
|
|
7051
|
+
// logic below aims to emulate the old behavior while still supporting the new output format
|
|
7052
|
+
// which uses `twoWayBindingSet`. Note that the generated code doesn't necessarily make sense
|
|
7053
|
+
// based on what the user wrote, for example the event binding for `[(value)]="a ? b : c"`
|
|
7054
|
+
// would produce `ctx.a ? ctx.b : ctx.c = $event`. We aim to reproduce what the parser used
|
|
7055
|
+
// to generate before #54154.
|
|
7056
|
+
if (ast instanceof BinaryOperatorExpr && isReadExpression$1(ast.rhs)) {
|
|
7057
|
+
// `a && b` -> `ctx.a && twoWayBindingSet(ctx.b, $event) || (ctx.b = $event)`
|
|
7058
|
+
return new BinaryOperatorExpr(ast.operator, ast.lhs, wrapAssignmentReadExpression(ast.rhs));
|
|
7059
|
+
}
|
|
7060
|
+
// Note: this also supports nullish coalescing expressions which
|
|
7061
|
+
// would've been downleveled to ternary expressions by this point.
|
|
7062
|
+
if (ast instanceof ConditionalExpr && isReadExpression$1(ast.falseCase)) {
|
|
7063
|
+
// `a ? b : c` -> `ctx.a ? ctx.b : twoWayBindingSet(ctx.c, $event) || (ctx.c = $event)`
|
|
7064
|
+
return new ConditionalExpr(ast.condition, ast.trueCase, wrapAssignmentReadExpression(ast.falseCase));
|
|
7065
|
+
}
|
|
7066
|
+
// `!!a` -> `twoWayBindingSet(ctx.a, $event) || (ctx.a = $event)`
|
|
7067
|
+
// Note: previously we'd actually produce `!!(ctx.a = $event)`, but the wrapping
|
|
7068
|
+
// node doesn't affect the result so we don't need to carry it over.
|
|
7069
|
+
if (ast instanceof NotExpr) {
|
|
7070
|
+
let expr = ast.condition;
|
|
7071
|
+
while (true) {
|
|
7072
|
+
if (expr instanceof NotExpr) {
|
|
7073
|
+
expr = expr.condition;
|
|
7074
|
+
}
|
|
7075
|
+
else {
|
|
7076
|
+
if (isReadExpression$1(expr)) {
|
|
7077
|
+
return wrapAssignmentReadExpression(expr);
|
|
7078
|
+
}
|
|
7079
|
+
break;
|
|
7080
|
+
}
|
|
7081
|
+
}
|
|
7082
|
+
}
|
|
7083
|
+
throw new Error(`Illegal state: unsupported expression in two-way action binding.`);
|
|
7084
|
+
}
|
|
7020
7085
|
function convertPropertyBindingBuiltins(converterFactory, ast) {
|
|
7021
7086
|
return convertBuiltins(converterFactory, ast);
|
|
7022
7087
|
}
|
|
@@ -7100,6 +7165,29 @@ function convertBuiltins(converterFactory, ast) {
|
|
|
7100
7165
|
const visitor = new _BuiltinAstConverter(converterFactory);
|
|
7101
7166
|
return ast.visit(visitor);
|
|
7102
7167
|
}
|
|
7168
|
+
function convertActionBuiltins(action) {
|
|
7169
|
+
const converterFactory = {
|
|
7170
|
+
createLiteralArrayConverter: () => {
|
|
7171
|
+
// Note: no caching for literal arrays in actions.
|
|
7172
|
+
return (args) => literalArr(args);
|
|
7173
|
+
},
|
|
7174
|
+
createLiteralMapConverter: (keys) => {
|
|
7175
|
+
// Note: no caching for literal maps in actions.
|
|
7176
|
+
return (values) => {
|
|
7177
|
+
const entries = keys.map((k, i) => ({
|
|
7178
|
+
key: k.key,
|
|
7179
|
+
value: values[i],
|
|
7180
|
+
quoted: k.quoted,
|
|
7181
|
+
}));
|
|
7182
|
+
return literalMap(entries);
|
|
7183
|
+
};
|
|
7184
|
+
},
|
|
7185
|
+
createPipeConverter: (name) => {
|
|
7186
|
+
throw new Error(`Illegal State: Actions are not allowed to contain pipes. Pipe: ${name}`);
|
|
7187
|
+
}
|
|
7188
|
+
};
|
|
7189
|
+
return convertPropertyBindingBuiltins(converterFactory, action);
|
|
7190
|
+
}
|
|
7103
7191
|
function temporaryName(bindingId, temporaryNumber) {
|
|
7104
7192
|
return `tmp_${bindingId}_${temporaryNumber}`;
|
|
7105
7193
|
}
|
|
@@ -8983,46 +9071,54 @@ var OpKind;
|
|
|
8983
9071
|
* An update up for a repeater.
|
|
8984
9072
|
*/
|
|
8985
9073
|
OpKind[OpKind["Repeater"] = 35] = "Repeater";
|
|
9074
|
+
/**
|
|
9075
|
+
* An operation to bind an expression to the property side of a two-way binding.
|
|
9076
|
+
*/
|
|
9077
|
+
OpKind[OpKind["TwoWayProperty"] = 36] = "TwoWayProperty";
|
|
9078
|
+
/**
|
|
9079
|
+
* An operation declaring the event side of a two-way binding.
|
|
9080
|
+
*/
|
|
9081
|
+
OpKind[OpKind["TwoWayListener"] = 37] = "TwoWayListener";
|
|
8986
9082
|
/**
|
|
8987
9083
|
* The start of an i18n block.
|
|
8988
9084
|
*/
|
|
8989
|
-
OpKind[OpKind["I18nStart"] =
|
|
9085
|
+
OpKind[OpKind["I18nStart"] = 38] = "I18nStart";
|
|
8990
9086
|
/**
|
|
8991
9087
|
* A self-closing i18n on a single element.
|
|
8992
9088
|
*/
|
|
8993
|
-
OpKind[OpKind["I18n"] =
|
|
9089
|
+
OpKind[OpKind["I18n"] = 39] = "I18n";
|
|
8994
9090
|
/**
|
|
8995
9091
|
* The end of an i18n block.
|
|
8996
9092
|
*/
|
|
8997
|
-
OpKind[OpKind["I18nEnd"] =
|
|
9093
|
+
OpKind[OpKind["I18nEnd"] = 40] = "I18nEnd";
|
|
8998
9094
|
/**
|
|
8999
9095
|
* An expression in an i18n message.
|
|
9000
9096
|
*/
|
|
9001
|
-
OpKind[OpKind["I18nExpression"] =
|
|
9097
|
+
OpKind[OpKind["I18nExpression"] = 41] = "I18nExpression";
|
|
9002
9098
|
/**
|
|
9003
9099
|
* An instruction that applies a set of i18n expressions.
|
|
9004
9100
|
*/
|
|
9005
|
-
OpKind[OpKind["I18nApply"] =
|
|
9101
|
+
OpKind[OpKind["I18nApply"] = 42] = "I18nApply";
|
|
9006
9102
|
/**
|
|
9007
9103
|
* An instruction to create an ICU expression.
|
|
9008
9104
|
*/
|
|
9009
|
-
OpKind[OpKind["IcuStart"] =
|
|
9105
|
+
OpKind[OpKind["IcuStart"] = 43] = "IcuStart";
|
|
9010
9106
|
/**
|
|
9011
9107
|
* An instruction to update an ICU expression.
|
|
9012
9108
|
*/
|
|
9013
|
-
OpKind[OpKind["IcuEnd"] =
|
|
9109
|
+
OpKind[OpKind["IcuEnd"] = 44] = "IcuEnd";
|
|
9014
9110
|
/**
|
|
9015
9111
|
* An instruction representing a placeholder in an ICU expression.
|
|
9016
9112
|
*/
|
|
9017
|
-
OpKind[OpKind["IcuPlaceholder"] =
|
|
9113
|
+
OpKind[OpKind["IcuPlaceholder"] = 45] = "IcuPlaceholder";
|
|
9018
9114
|
/**
|
|
9019
9115
|
* An i18n context containing information needed to generate an i18n message.
|
|
9020
9116
|
*/
|
|
9021
|
-
OpKind[OpKind["I18nContext"] =
|
|
9117
|
+
OpKind[OpKind["I18nContext"] = 46] = "I18nContext";
|
|
9022
9118
|
/**
|
|
9023
9119
|
* A creation op that corresponds to i18n attributes on an element.
|
|
9024
9120
|
*/
|
|
9025
|
-
OpKind[OpKind["I18nAttributes"] =
|
|
9121
|
+
OpKind[OpKind["I18nAttributes"] = 47] = "I18nAttributes";
|
|
9026
9122
|
})(OpKind || (OpKind = {}));
|
|
9027
9123
|
/**
|
|
9028
9124
|
* Distinguishes different kinds of IR expressions.
|
|
@@ -9134,6 +9230,10 @@ var ExpressionKind;
|
|
|
9134
9230
|
* An expression that will be automatically extracted to the component const array.
|
|
9135
9231
|
*/
|
|
9136
9232
|
ExpressionKind[ExpressionKind["ConstCollected"] = 25] = "ConstCollected";
|
|
9233
|
+
/**
|
|
9234
|
+
* Operation that sets the value of a two-way binding.
|
|
9235
|
+
*/
|
|
9236
|
+
ExpressionKind[ExpressionKind["TwoWayBindingSet"] = 26] = "TwoWayBindingSet";
|
|
9137
9237
|
})(ExpressionKind || (ExpressionKind = {}));
|
|
9138
9238
|
var VariableFlags;
|
|
9139
9239
|
(function (VariableFlags) {
|
|
@@ -9219,6 +9319,10 @@ var BindingKind;
|
|
|
9219
9319
|
* Animation property bindings.
|
|
9220
9320
|
*/
|
|
9221
9321
|
BindingKind[BindingKind["Animation"] = 6] = "Animation";
|
|
9322
|
+
/**
|
|
9323
|
+
* Property side of a two-way binding.
|
|
9324
|
+
*/
|
|
9325
|
+
BindingKind[BindingKind["TwoWayProperty"] = 7] = "TwoWayProperty";
|
|
9222
9326
|
})(BindingKind || (BindingKind = {}));
|
|
9223
9327
|
/**
|
|
9224
9328
|
* Enumeration of possible times i18n params can be resolved.
|
|
@@ -9485,6 +9589,27 @@ function createPropertyOp(target, name, expression, isAnimationTrigger, security
|
|
|
9485
9589
|
...NEW_OP,
|
|
9486
9590
|
};
|
|
9487
9591
|
}
|
|
9592
|
+
/**
|
|
9593
|
+
* Create a `TwoWayPropertyOp`.
|
|
9594
|
+
*/
|
|
9595
|
+
function createTwoWayPropertyOp(target, name, expression, securityContext, isStructuralTemplateAttribute, templateKind, i18nContext, i18nMessage, sourceSpan) {
|
|
9596
|
+
return {
|
|
9597
|
+
kind: OpKind.TwoWayProperty,
|
|
9598
|
+
target,
|
|
9599
|
+
name,
|
|
9600
|
+
expression,
|
|
9601
|
+
securityContext,
|
|
9602
|
+
sanitizer: null,
|
|
9603
|
+
isStructuralTemplateAttribute,
|
|
9604
|
+
templateKind,
|
|
9605
|
+
i18nContext,
|
|
9606
|
+
i18nMessage,
|
|
9607
|
+
sourceSpan,
|
|
9608
|
+
...TRAIT_DEPENDS_ON_SLOT_CONTEXT,
|
|
9609
|
+
...TRAIT_CONSUMES_VARS,
|
|
9610
|
+
...NEW_OP,
|
|
9611
|
+
};
|
|
9612
|
+
}
|
|
9488
9613
|
/** Create a `StylePropOp`. */
|
|
9489
9614
|
function createStylePropOp(xref, name, expression, unit, sourceSpan) {
|
|
9490
9615
|
return {
|
|
@@ -9862,6 +9987,31 @@ class ResetViewExpr extends ExpressionBase {
|
|
|
9862
9987
|
return new ResetViewExpr(this.expr.clone());
|
|
9863
9988
|
}
|
|
9864
9989
|
}
|
|
9990
|
+
class TwoWayBindingSetExpr extends ExpressionBase {
|
|
9991
|
+
constructor(target, value) {
|
|
9992
|
+
super();
|
|
9993
|
+
this.target = target;
|
|
9994
|
+
this.value = value;
|
|
9995
|
+
this.kind = ExpressionKind.TwoWayBindingSet;
|
|
9996
|
+
}
|
|
9997
|
+
visitExpression(visitor, context) {
|
|
9998
|
+
this.target.visitExpression(visitor, context);
|
|
9999
|
+
this.value.visitExpression(visitor, context);
|
|
10000
|
+
}
|
|
10001
|
+
isEquivalent(other) {
|
|
10002
|
+
return this.target.isEquivalent(other.target) && this.value.isEquivalent(other.value);
|
|
10003
|
+
}
|
|
10004
|
+
isConstant() {
|
|
10005
|
+
return false;
|
|
10006
|
+
}
|
|
10007
|
+
transformInternalExpressions(transform, flags) {
|
|
10008
|
+
this.target = transformExpressionsInExpression(this.target, transform, flags);
|
|
10009
|
+
this.value = transformExpressionsInExpression(this.value, transform, flags);
|
|
10010
|
+
}
|
|
10011
|
+
clone() {
|
|
10012
|
+
return new TwoWayBindingSetExpr(this.target, this.value);
|
|
10013
|
+
}
|
|
10014
|
+
}
|
|
9865
10015
|
/**
|
|
9866
10016
|
* Read of a variable declared as an `ir.VariableOp` and referenced through its `ir.XrefId`.
|
|
9867
10017
|
*/
|
|
@@ -10321,6 +10471,11 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10321
10471
|
op.sanitizer =
|
|
10322
10472
|
op.sanitizer && transformExpressionsInExpression(op.sanitizer, transform, flags);
|
|
10323
10473
|
break;
|
|
10474
|
+
case OpKind.TwoWayProperty:
|
|
10475
|
+
op.expression = transformExpressionsInExpression(op.expression, transform, flags);
|
|
10476
|
+
op.sanitizer =
|
|
10477
|
+
op.sanitizer && transformExpressionsInExpression(op.sanitizer, transform, flags);
|
|
10478
|
+
break;
|
|
10324
10479
|
case OpKind.I18nExpression:
|
|
10325
10480
|
op.expression = transformExpressionsInExpression(op.expression, transform, flags);
|
|
10326
10481
|
break;
|
|
@@ -10349,6 +10504,7 @@ function transformExpressionsInOp(op, transform, flags) {
|
|
|
10349
10504
|
}
|
|
10350
10505
|
break;
|
|
10351
10506
|
case OpKind.Listener:
|
|
10507
|
+
case OpKind.TwoWayListener:
|
|
10352
10508
|
for (const innerOp of op.handlerOps) {
|
|
10353
10509
|
transformExpressionsInOp(innerOp, transform, flags | VisitorContextFlag.InChildOperation);
|
|
10354
10510
|
}
|
|
@@ -10967,6 +11123,24 @@ function createListenerOp(target, targetSlot, name, tag, handlerOps, animationPh
|
|
|
10967
11123
|
...NEW_OP,
|
|
10968
11124
|
};
|
|
10969
11125
|
}
|
|
11126
|
+
/**
|
|
11127
|
+
* Create a `TwoWayListenerOp`.
|
|
11128
|
+
*/
|
|
11129
|
+
function createTwoWayListenerOp(target, targetSlot, name, tag, handlerOps, sourceSpan) {
|
|
11130
|
+
const handlerList = new OpList();
|
|
11131
|
+
handlerList.push(handlerOps);
|
|
11132
|
+
return {
|
|
11133
|
+
kind: OpKind.TwoWayListener,
|
|
11134
|
+
target,
|
|
11135
|
+
targetSlot,
|
|
11136
|
+
tag,
|
|
11137
|
+
name,
|
|
11138
|
+
handlerOps: handlerList,
|
|
11139
|
+
handlerFnName: null,
|
|
11140
|
+
sourceSpan,
|
|
11141
|
+
...NEW_OP,
|
|
11142
|
+
};
|
|
11143
|
+
}
|
|
10970
11144
|
function createPipeOp(xref, slot, name) {
|
|
10971
11145
|
return {
|
|
10972
11146
|
kind: OpKind.Pipe,
|
|
@@ -11024,7 +11198,7 @@ function createExtractedAttributeOp(target, bindingKind, namespace, name, expres
|
|
|
11024
11198
|
...NEW_OP,
|
|
11025
11199
|
};
|
|
11026
11200
|
}
|
|
11027
|
-
function createDeferOp(xref, main, mainSlot, metadata, sourceSpan) {
|
|
11201
|
+
function createDeferOp(xref, main, mainSlot, metadata, resolverFn, sourceSpan) {
|
|
11028
11202
|
return {
|
|
11029
11203
|
kind: OpKind.Defer,
|
|
11030
11204
|
xref,
|
|
@@ -11043,7 +11217,7 @@ function createDeferOp(xref, main, mainSlot, metadata, sourceSpan) {
|
|
|
11043
11217
|
errorView: null,
|
|
11044
11218
|
errorSlot: null,
|
|
11045
11219
|
metadata,
|
|
11046
|
-
resolverFn
|
|
11220
|
+
resolverFn,
|
|
11047
11221
|
sourceSpan,
|
|
11048
11222
|
...NEW_OP,
|
|
11049
11223
|
...TRAIT_CONSUMES_SLOT,
|
|
@@ -11232,11 +11406,12 @@ class CompilationJob {
|
|
|
11232
11406
|
* embedded views or host bindings.
|
|
11233
11407
|
*/
|
|
11234
11408
|
class ComponentCompilationJob extends CompilationJob {
|
|
11235
|
-
constructor(componentName, pool, compatibility, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta) {
|
|
11409
|
+
constructor(componentName, pool, compatibility, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta, allDeferrableDepsFn) {
|
|
11236
11410
|
super(componentName, pool, compatibility);
|
|
11237
11411
|
this.relativeContextFilePath = relativeContextFilePath;
|
|
11238
11412
|
this.i18nUseExternalIds = i18nUseExternalIds;
|
|
11239
11413
|
this.deferBlocksMeta = deferBlocksMeta;
|
|
11414
|
+
this.allDeferrableDepsFn = allDeferrableDepsFn;
|
|
11240
11415
|
this.kind = CompilationJobKind.Tmpl;
|
|
11241
11416
|
this.fnSuffix = 'Template';
|
|
11242
11417
|
this.views = new Map();
|
|
@@ -11323,7 +11498,7 @@ class CompilationUnit {
|
|
|
11323
11498
|
*ops() {
|
|
11324
11499
|
for (const op of this.create) {
|
|
11325
11500
|
yield op;
|
|
11326
|
-
if (op.kind === OpKind.Listener) {
|
|
11501
|
+
if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
|
|
11327
11502
|
for (const listenerOp of op.handlerOps) {
|
|
11328
11503
|
yield listenerOp;
|
|
11329
11504
|
}
|
|
@@ -11569,6 +11744,11 @@ function extractAttributes(job) {
|
|
|
11569
11744
|
/* i18nMessage */ null, op.securityContext), lookupElement$2(elements, op.target));
|
|
11570
11745
|
}
|
|
11571
11746
|
break;
|
|
11747
|
+
case OpKind.TwoWayProperty:
|
|
11748
|
+
OpList.insertBefore(createExtractedAttributeOp(op.target, BindingKind.TwoWayProperty, null, op.name, /* expression */ null,
|
|
11749
|
+
/* i18nContext */ null,
|
|
11750
|
+
/* i18nMessage */ null, op.securityContext), lookupElement$2(elements, op.target));
|
|
11751
|
+
break;
|
|
11572
11752
|
case OpKind.StyleProp:
|
|
11573
11753
|
case OpKind.ClassProp:
|
|
11574
11754
|
// TODO: Can style or class bindings be i18n attributes?
|
|
@@ -11602,6 +11782,15 @@ function extractAttributes(job) {
|
|
|
11602
11782
|
}
|
|
11603
11783
|
}
|
|
11604
11784
|
break;
|
|
11785
|
+
case OpKind.TwoWayListener:
|
|
11786
|
+
// Two-way listeners aren't supported in host bindings.
|
|
11787
|
+
if (job.kind !== CompilationJobKind.Host) {
|
|
11788
|
+
const extractedAttributeOp = createExtractedAttributeOp(op.target, BindingKind.Property, null, op.name, /* expression */ null,
|
|
11789
|
+
/* i18nContext */ null,
|
|
11790
|
+
/* i18nMessage */ null, SecurityContext.NONE);
|
|
11791
|
+
OpList.insertBefore(extractedAttributeOp, lookupElement$2(elements, op.target));
|
|
11792
|
+
}
|
|
11793
|
+
break;
|
|
11605
11794
|
}
|
|
11606
11795
|
}
|
|
11607
11796
|
}
|
|
@@ -11690,6 +11879,15 @@ function specializeBindings(job) {
|
|
|
11690
11879
|
OpList.replace(op, createPropertyOp(op.target, op.name, op.expression, op.bindingKind === BindingKind.Animation, op.securityContext, op.isStructuralTemplateAttribute, op.templateKind, op.i18nContext, op.i18nMessage, op.sourceSpan));
|
|
11691
11880
|
}
|
|
11692
11881
|
break;
|
|
11882
|
+
case BindingKind.TwoWayProperty:
|
|
11883
|
+
if (!(op.expression instanceof Expression)) {
|
|
11884
|
+
// We shouldn't be able to hit this code path since interpolations in two-way bindings
|
|
11885
|
+
// result in a parser error. We assert here so that downstream we can assume that
|
|
11886
|
+
// the value is always an expression.
|
|
11887
|
+
throw new Error(`Expected value of two-way property binding "${op.name}" to be an expression`);
|
|
11888
|
+
}
|
|
11889
|
+
OpList.replace(op, createTwoWayPropertyOp(op.target, op.name, op.expression, op.securityContext, op.isStructuralTemplateAttribute, op.templateKind, op.i18nContext, op.i18nMessage, op.sourceSpan));
|
|
11890
|
+
break;
|
|
11693
11891
|
case BindingKind.I18n:
|
|
11694
11892
|
case BindingKind.ClassName:
|
|
11695
11893
|
case BindingKind.StyleProperty:
|
|
@@ -11726,6 +11924,8 @@ const CHAINABLE = new Set([
|
|
|
11726
11924
|
Identifiers.syntheticHostListener,
|
|
11727
11925
|
Identifiers.syntheticHostProperty,
|
|
11728
11926
|
Identifiers.templateCreate,
|
|
11927
|
+
Identifiers.twoWayProperty,
|
|
11928
|
+
Identifiers.twoWayListener,
|
|
11729
11929
|
]);
|
|
11730
11930
|
/**
|
|
11731
11931
|
* Post-process a reified view compilation and convert sequential calls to chainable instructions
|
|
@@ -11999,7 +12199,7 @@ class ElementAttributes {
|
|
|
11999
12199
|
return this.byKind.get(BindingKind.StyleProperty) ?? FLYWEIGHT_ARRAY;
|
|
12000
12200
|
}
|
|
12001
12201
|
get bindings() {
|
|
12002
|
-
return this.
|
|
12202
|
+
return this.propertyBindings ?? FLYWEIGHT_ARRAY;
|
|
12003
12203
|
}
|
|
12004
12204
|
get template() {
|
|
12005
12205
|
return this.byKind.get(BindingKind.Template) ?? FLYWEIGHT_ARRAY;
|
|
@@ -12011,6 +12211,7 @@ class ElementAttributes {
|
|
|
12011
12211
|
this.compatibility = compatibility;
|
|
12012
12212
|
this.known = new Map();
|
|
12013
12213
|
this.byKind = new Map;
|
|
12214
|
+
this.propertyBindings = null;
|
|
12014
12215
|
this.projectAs = null;
|
|
12015
12216
|
}
|
|
12016
12217
|
isKnown(kind, name, value) {
|
|
@@ -12060,10 +12261,16 @@ class ElementAttributes {
|
|
|
12060
12261
|
}
|
|
12061
12262
|
}
|
|
12062
12263
|
arrayFor(kind) {
|
|
12063
|
-
if (
|
|
12064
|
-
this.
|
|
12264
|
+
if (kind === BindingKind.Property || kind === BindingKind.TwoWayProperty) {
|
|
12265
|
+
this.propertyBindings ??= [];
|
|
12266
|
+
return this.propertyBindings;
|
|
12267
|
+
}
|
|
12268
|
+
else {
|
|
12269
|
+
if (!this.byKind.has(kind)) {
|
|
12270
|
+
this.byKind.set(kind, []);
|
|
12271
|
+
}
|
|
12272
|
+
return this.byKind.get(kind);
|
|
12065
12273
|
}
|
|
12066
|
-
return this.byKind.get(kind);
|
|
12067
12274
|
}
|
|
12068
12275
|
}
|
|
12069
12276
|
/**
|
|
@@ -12159,11 +12366,16 @@ function createDeferDepsFns(job) {
|
|
|
12159
12366
|
if (op.metadata.deps.length === 0) {
|
|
12160
12367
|
continue;
|
|
12161
12368
|
}
|
|
12369
|
+
if (op.resolverFn !== null) {
|
|
12370
|
+
continue;
|
|
12371
|
+
}
|
|
12162
12372
|
const dependencies = [];
|
|
12163
12373
|
for (const dep of op.metadata.deps) {
|
|
12164
12374
|
if (dep.isDeferrable) {
|
|
12165
12375
|
// Callback function, e.g. `m () => m.MyCmp;`.
|
|
12166
|
-
const innerFn = arrowFn(
|
|
12376
|
+
const innerFn = arrowFn(
|
|
12377
|
+
// Default imports are always accessed through the `default` property.
|
|
12378
|
+
[new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(dep.isDefaultImport ? 'default' : dep.symbolName));
|
|
12167
12379
|
// Dynamic import, e.g. `import('./a').then(...)`.
|
|
12168
12380
|
const importExpr = (new DynamicImportExpr(dep.importPath)).prop('then').callFn([innerFn]);
|
|
12169
12381
|
dependencies.push(importExpr);
|
|
@@ -12177,7 +12389,8 @@ function createDeferDepsFns(job) {
|
|
|
12177
12389
|
if (op.handle.slot === null) {
|
|
12178
12390
|
throw new Error('AssertionError: slot must be assigned bfore extracting defer deps functions');
|
|
12179
12391
|
}
|
|
12180
|
-
|
|
12392
|
+
const fullPathName = unit.fnName?.replace(`_Template`, ``);
|
|
12393
|
+
op.resolverFn = job.pool.getSharedFunctionReference(depsFnExpr, `${fullPathName}_Defer_${op.handle.slot}_DepsFn`,
|
|
12181
12394
|
/* Don't use unique names for TDB compatibility */ false);
|
|
12182
12395
|
}
|
|
12183
12396
|
}
|
|
@@ -12989,6 +13202,7 @@ function recursivelyProcessView(view, parentScope) {
|
|
|
12989
13202
|
}
|
|
12990
13203
|
break;
|
|
12991
13204
|
case OpKind.Listener:
|
|
13205
|
+
case OpKind.TwoWayListener:
|
|
12992
13206
|
// Prepend variables to listener handler functions.
|
|
12993
13207
|
op.handlerOps.prepend(generateVariablesInScopeForView(view, scope));
|
|
12994
13208
|
break;
|
|
@@ -13652,15 +13866,12 @@ class Parser$1 {
|
|
|
13652
13866
|
this._lexer = _lexer;
|
|
13653
13867
|
this.errors = [];
|
|
13654
13868
|
}
|
|
13655
|
-
parseAction(input,
|
|
13869
|
+
parseAction(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
|
13656
13870
|
this._checkNoInterpolation(input, location, interpolationConfig);
|
|
13657
13871
|
const sourceToLex = this._stripComments(input);
|
|
13658
13872
|
const tokens = this._lexer.tokenize(sourceToLex);
|
|
13659
|
-
|
|
13660
|
-
|
|
13661
|
-
flags |= 2 /* ParseFlags.AssignmentEvent */;
|
|
13662
|
-
}
|
|
13663
|
-
const ast = new _ParseAST(input, location, absoluteOffset, tokens, flags, this.errors, 0).parseChain();
|
|
13873
|
+
const ast = new _ParseAST(input, location, absoluteOffset, tokens, 1 /* ParseFlags.Action */, this.errors, 0)
|
|
13874
|
+
.parseChain();
|
|
13664
13875
|
return new ASTWithSource(ast, input, location, absoluteOffset, this.errors);
|
|
13665
13876
|
}
|
|
13666
13877
|
parseBinding(input, location, absoluteOffset, interpolationConfig = DEFAULT_INTERPOLATION_CONFIG) {
|
|
@@ -14137,7 +14348,7 @@ class _ParseAST {
|
|
|
14137
14348
|
let result = this.parseExpression();
|
|
14138
14349
|
if (this.consumeOptionalOperator('|')) {
|
|
14139
14350
|
if (this.parseFlags & 1 /* ParseFlags.Action */) {
|
|
14140
|
-
this.error(
|
|
14351
|
+
this.error(`Cannot have a pipe in an action expression`);
|
|
14141
14352
|
}
|
|
14142
14353
|
do {
|
|
14143
14354
|
const nameStart = this.inputIndex;
|
|
@@ -14479,7 +14690,7 @@ class _ParseAST {
|
|
|
14479
14690
|
const nameSpan = this.sourceSpan(nameStart);
|
|
14480
14691
|
let receiver;
|
|
14481
14692
|
if (isSafe) {
|
|
14482
|
-
if (this.
|
|
14693
|
+
if (this.consumeOptionalOperator('=')) {
|
|
14483
14694
|
this.error('The \'?.\' operator cannot be used in the assignment');
|
|
14484
14695
|
receiver = new EmptyExpr$1(this.span(start), this.sourceSpan(start));
|
|
14485
14696
|
}
|
|
@@ -14488,7 +14699,7 @@ class _ParseAST {
|
|
|
14488
14699
|
}
|
|
14489
14700
|
}
|
|
14490
14701
|
else {
|
|
14491
|
-
if (this.
|
|
14702
|
+
if (this.consumeOptionalOperator('=')) {
|
|
14492
14703
|
if (!(this.parseFlags & 1 /* ParseFlags.Action */)) {
|
|
14493
14704
|
this.error('Bindings cannot contain assignments');
|
|
14494
14705
|
return new EmptyExpr$1(this.span(start), this.sourceSpan(start));
|
|
@@ -14515,22 +14726,6 @@ class _ParseAST {
|
|
|
14515
14726
|
return isSafe ? new SafeCall(span, sourceSpan, receiver, args, argumentSpan) :
|
|
14516
14727
|
new Call(span, sourceSpan, receiver, args, argumentSpan);
|
|
14517
14728
|
}
|
|
14518
|
-
consumeOptionalAssignment() {
|
|
14519
|
-
// When parsing assignment events (originating from two-way-binding aka banana-in-a-box syntax),
|
|
14520
|
-
// it is valid for the primary expression to be terminated by the non-null operator. This
|
|
14521
|
-
// primary expression is substituted as LHS of the assignment operator to achieve
|
|
14522
|
-
// two-way-binding, such that the LHS could be the non-null operator. The grammar doesn't
|
|
14523
|
-
// naturally allow for this syntax, so assignment events are parsed specially.
|
|
14524
|
-
if ((this.parseFlags & 2 /* ParseFlags.AssignmentEvent */) && this.next.isOperator('!') &&
|
|
14525
|
-
this.peek(1).isOperator('=')) {
|
|
14526
|
-
// First skip over the ! operator.
|
|
14527
|
-
this.advance();
|
|
14528
|
-
// Then skip over the = operator, to fully consume the optional assignment operator.
|
|
14529
|
-
this.advance();
|
|
14530
|
-
return true;
|
|
14531
|
-
}
|
|
14532
|
-
return this.consumeOptionalOperator('=');
|
|
14533
|
-
}
|
|
14534
14729
|
parseCallArguments() {
|
|
14535
14730
|
if (this.next.isCharacter($RPAREN))
|
|
14536
14731
|
return [];
|
|
@@ -20608,7 +20803,10 @@ function nameFunctionsAndVariables(job) {
|
|
|
20608
20803
|
}
|
|
20609
20804
|
function addNamesToView(unit, baseName, state, compatibility) {
|
|
20610
20805
|
if (unit.fnName === null) {
|
|
20611
|
-
|
|
20806
|
+
// Ensure unique names for view units. This is necessary because there might be multiple
|
|
20807
|
+
// components with same names in the context of the same pool. Only add the suffix
|
|
20808
|
+
// if really needed.
|
|
20809
|
+
unit.fnName = unit.job.pool.uniqueName(sanitizeIdentifier(`${baseName}_${unit.job.fnSuffix}`), /* alwaysIncludeSuffix */ false);
|
|
20612
20810
|
}
|
|
20613
20811
|
// Keep track of the names we assign to variables in the view. We'll need to propagate these
|
|
20614
20812
|
// into reads of those variables afterwards.
|
|
@@ -20641,6 +20839,15 @@ function addNamesToView(unit, baseName, state, compatibility) {
|
|
|
20641
20839
|
}
|
|
20642
20840
|
op.handlerFnName = sanitizeIdentifier(op.handlerFnName);
|
|
20643
20841
|
break;
|
|
20842
|
+
case OpKind.TwoWayListener:
|
|
20843
|
+
if (op.handlerFnName !== null) {
|
|
20844
|
+
break;
|
|
20845
|
+
}
|
|
20846
|
+
if (op.targetSlot.slot === null) {
|
|
20847
|
+
throw new Error(`Expected a slot to be assigned`);
|
|
20848
|
+
}
|
|
20849
|
+
op.handlerFnName = sanitizeIdentifier(`${unit.fnName}_${op.tag.replace('-', '_')}_${op.name}_${op.targetSlot.slot}_listener`);
|
|
20850
|
+
break;
|
|
20644
20851
|
case OpKind.Variable:
|
|
20645
20852
|
varNames.set(op.xref, getVariableName(unit, op.variable, state));
|
|
20646
20853
|
break;
|
|
@@ -20755,7 +20962,7 @@ function stripImportant(name) {
|
|
|
20755
20962
|
function mergeNextContextExpressions(job) {
|
|
20756
20963
|
for (const unit of job.units) {
|
|
20757
20964
|
for (const op of unit.create) {
|
|
20758
|
-
if (op.kind === OpKind.Listener) {
|
|
20965
|
+
if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
|
|
20759
20966
|
mergeNextContextsInOps(op.handlerOps);
|
|
20760
20967
|
}
|
|
20761
20968
|
}
|
|
@@ -20896,6 +21103,14 @@ function kindWithInterpolationTest(kind, interpolation) {
|
|
|
20896
21103
|
return op.kind === kind && interpolation === op.expression instanceof Interpolation;
|
|
20897
21104
|
};
|
|
20898
21105
|
}
|
|
21106
|
+
function basicListenerKindTest(op) {
|
|
21107
|
+
return (op.kind === OpKind.Listener && !(op.hostListener && op.isAnimationListener)) ||
|
|
21108
|
+
op.kind === OpKind.TwoWayListener;
|
|
21109
|
+
}
|
|
21110
|
+
function nonInterpolationPropertyKindTest(op) {
|
|
21111
|
+
return (op.kind === OpKind.Property || op.kind === OpKind.TwoWayProperty) &&
|
|
21112
|
+
!(op.expression instanceof Interpolation);
|
|
21113
|
+
}
|
|
20899
21114
|
/**
|
|
20900
21115
|
* Defines the groups based on `OpKind` that ops will be divided into, for the various create
|
|
20901
21116
|
* op kinds. Ops will be collected into groups, then optionally transformed, before recombining
|
|
@@ -20903,7 +21118,7 @@ function kindWithInterpolationTest(kind, interpolation) {
|
|
|
20903
21118
|
*/
|
|
20904
21119
|
const CREATE_ORDERING = [
|
|
20905
21120
|
{ test: op => op.kind === OpKind.Listener && op.hostListener && op.isAnimationListener },
|
|
20906
|
-
{ test:
|
|
21121
|
+
{ test: basicListenerKindTest },
|
|
20907
21122
|
];
|
|
20908
21123
|
/**
|
|
20909
21124
|
* Defines the groups based on `OpKind` that ops will be divided into, for the various update
|
|
@@ -20916,7 +21131,7 @@ const UPDATE_ORDERING = [
|
|
|
20916
21131
|
{ test: kindTest(OpKind.ClassProp) },
|
|
20917
21132
|
{ test: kindWithInterpolationTest(OpKind.Attribute, true) },
|
|
20918
21133
|
{ test: kindWithInterpolationTest(OpKind.Property, true) },
|
|
20919
|
-
{ test:
|
|
21134
|
+
{ test: nonInterpolationPropertyKindTest },
|
|
20920
21135
|
{ test: kindWithInterpolationTest(OpKind.Attribute, false) },
|
|
20921
21136
|
];
|
|
20922
21137
|
/**
|
|
@@ -20935,8 +21150,9 @@ const UPDATE_HOST_ORDERING = [
|
|
|
20935
21150
|
* The set of all op kinds we handle in the reordering phase.
|
|
20936
21151
|
*/
|
|
20937
21152
|
const handledOpKinds = new Set([
|
|
20938
|
-
OpKind.Listener, OpKind.
|
|
20939
|
-
OpKind.
|
|
21153
|
+
OpKind.Listener, OpKind.TwoWayListener, OpKind.StyleMap, OpKind.ClassMap,
|
|
21154
|
+
OpKind.StyleProp, OpKind.ClassProp, OpKind.Property, OpKind.TwoWayProperty,
|
|
21155
|
+
OpKind.HostProperty, OpKind.Attribute
|
|
20940
21156
|
]);
|
|
20941
21157
|
/**
|
|
20942
21158
|
* Many type of operations have ordering constraints that must be respected. For example, a
|
|
@@ -21402,6 +21618,12 @@ function listener(name, handlerFn, eventTargetResolver, syntheticHost, sourceSpa
|
|
|
21402
21618
|
}
|
|
21403
21619
|
return call(syntheticHost ? Identifiers.syntheticHostListener : Identifiers.listener, args, sourceSpan);
|
|
21404
21620
|
}
|
|
21621
|
+
function twoWayBindingSet(target, value) {
|
|
21622
|
+
return importExpr(Identifiers.twoWayBindingSet).callFn([target, value]);
|
|
21623
|
+
}
|
|
21624
|
+
function twoWayListener(name, handlerFn, sourceSpan) {
|
|
21625
|
+
return call(Identifiers.twoWayListener, [literal(name), handlerFn], sourceSpan);
|
|
21626
|
+
}
|
|
21405
21627
|
function pipe(slot, name) {
|
|
21406
21628
|
return call(Identifiers.pipe, [
|
|
21407
21629
|
literal(slot),
|
|
@@ -21562,6 +21784,13 @@ function property(name, expression, sanitizer, sourceSpan) {
|
|
|
21562
21784
|
}
|
|
21563
21785
|
return call(Identifiers.property, args, sourceSpan);
|
|
21564
21786
|
}
|
|
21787
|
+
function twoWayProperty(name, expression, sanitizer, sourceSpan) {
|
|
21788
|
+
const args = [literal(name), expression];
|
|
21789
|
+
if (sanitizer !== null) {
|
|
21790
|
+
args.push(sanitizer);
|
|
21791
|
+
}
|
|
21792
|
+
return call(Identifiers.twoWayProperty, args, sourceSpan);
|
|
21793
|
+
}
|
|
21565
21794
|
function attribute(name, expression, sanitizer, namespace) {
|
|
21566
21795
|
const args = [literal(name), expression];
|
|
21567
21796
|
if (sanitizer !== null || namespace !== null) {
|
|
@@ -22004,6 +22233,9 @@ function reifyCreateOperations(unit, ops) {
|
|
|
22004
22233
|
}
|
|
22005
22234
|
OpList.replace(op, listener(op.name, listenerFn, eventTargetResolver, op.hostListener && op.isAnimationListener, op.sourceSpan));
|
|
22006
22235
|
break;
|
|
22236
|
+
case OpKind.TwoWayListener:
|
|
22237
|
+
OpList.replace(op, twoWayListener(op.name, reifyListenerHandler(unit, op.handlerFnName, op.handlerOps, true), op.sourceSpan));
|
|
22238
|
+
break;
|
|
22007
22239
|
case OpKind.Variable:
|
|
22008
22240
|
if (op.variable.name === null) {
|
|
22009
22241
|
throw new Error(`AssertionError: unnamed variable ${op.xref}`);
|
|
@@ -22112,6 +22344,9 @@ function reifyUpdateOperations(_unit, ops) {
|
|
|
22112
22344
|
OpList.replace(op, property(op.name, op.expression, op.sanitizer, op.sourceSpan));
|
|
22113
22345
|
}
|
|
22114
22346
|
break;
|
|
22347
|
+
case OpKind.TwoWayProperty:
|
|
22348
|
+
OpList.replace(op, twoWayProperty(op.name, op.expression, op.sanitizer, op.sourceSpan));
|
|
22349
|
+
break;
|
|
22115
22350
|
case OpKind.StyleProp:
|
|
22116
22351
|
if (op.expression instanceof Interpolation) {
|
|
22117
22352
|
OpList.replace(op, stylePropInterpolate(op.name, op.expression.strings, op.expression.expressions, op.unit, op.sourceSpan));
|
|
@@ -22209,6 +22444,8 @@ function reifyIrExpression(expr) {
|
|
|
22209
22444
|
return reference(expr.targetSlot.slot + 1 + expr.offset);
|
|
22210
22445
|
case ExpressionKind.LexicalRead:
|
|
22211
22446
|
throw new Error(`AssertionError: unresolved LexicalRead of ${expr.name}`);
|
|
22447
|
+
case ExpressionKind.TwoWayBindingSet:
|
|
22448
|
+
throw new Error(`AssertionError: unresolved TwoWayBindingSet`);
|
|
22212
22449
|
case ExpressionKind.RestoreView:
|
|
22213
22450
|
if (typeof expr.view === 'number') {
|
|
22214
22451
|
throw new Error(`AssertionError: unresolved RestoreView`);
|
|
@@ -22369,6 +22606,7 @@ function processLexicalScope$1(view, ops) {
|
|
|
22369
22606
|
}
|
|
22370
22607
|
break;
|
|
22371
22608
|
case OpKind.Listener:
|
|
22609
|
+
case OpKind.TwoWayListener:
|
|
22372
22610
|
processLexicalScope$1(view, op.handlerOps);
|
|
22373
22611
|
break;
|
|
22374
22612
|
}
|
|
@@ -22404,10 +22642,13 @@ function resolveDollarEvent(job) {
|
|
|
22404
22642
|
}
|
|
22405
22643
|
function transformDollarEvent(unit, ops) {
|
|
22406
22644
|
for (const op of ops) {
|
|
22407
|
-
if (op.kind === OpKind.Listener) {
|
|
22645
|
+
if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
|
|
22408
22646
|
transformExpressionsInOp(op, (expr) => {
|
|
22409
22647
|
if (expr instanceof LexicalReadExpr && expr.name === '$event') {
|
|
22410
|
-
|
|
22648
|
+
// Two-way listeners always consume `$event` so they omit this field.
|
|
22649
|
+
if (op.kind === OpKind.Listener) {
|
|
22650
|
+
op.consumesDollarEvent = true;
|
|
22651
|
+
}
|
|
22411
22652
|
return new ReadVarExpr(expr.name);
|
|
22412
22653
|
}
|
|
22413
22654
|
return expr;
|
|
@@ -22782,6 +23023,7 @@ function processLexicalScope(unit, ops, savedView) {
|
|
|
22782
23023
|
}
|
|
22783
23024
|
break;
|
|
22784
23025
|
case OpKind.Listener:
|
|
23026
|
+
case OpKind.TwoWayListener:
|
|
22785
23027
|
// Listener functions have separate variable declarations, so process them as a separate
|
|
22786
23028
|
// lexical scope.
|
|
22787
23029
|
processLexicalScope(unit, op.handlerOps, savedView);
|
|
@@ -22792,7 +23034,7 @@ function processLexicalScope(unit, ops, savedView) {
|
|
|
22792
23034
|
// scope. Also, look for `ir.RestoreViewExpr`s and match them with the snapshotted view context
|
|
22793
23035
|
// variable.
|
|
22794
23036
|
for (const op of ops) {
|
|
22795
|
-
if (op.kind == OpKind.Listener) {
|
|
23037
|
+
if (op.kind == OpKind.Listener || op.kind === OpKind.TwoWayListener) {
|
|
22796
23038
|
// Listeners were already processed above with their own scopes.
|
|
22797
23039
|
continue;
|
|
22798
23040
|
}
|
|
@@ -22941,6 +23183,75 @@ function getOnlySecurityContext(securityContext) {
|
|
|
22941
23183
|
return securityContext;
|
|
22942
23184
|
}
|
|
22943
23185
|
|
|
23186
|
+
/**
|
|
23187
|
+
* Transforms a `TwoWayBindingSet` expression into an expression that either
|
|
23188
|
+
* sets a value through the `twoWayBindingSet` instruction or falls back to setting
|
|
23189
|
+
* the value directly. E.g. the expression `TwoWayBindingSet(target, value)` becomes:
|
|
23190
|
+
* `ng.twoWayBindingSet(target, value) || (target = value)`.
|
|
23191
|
+
*/
|
|
23192
|
+
function transformTwoWayBindingSet(job) {
|
|
23193
|
+
for (const unit of job.units) {
|
|
23194
|
+
for (const op of unit.create) {
|
|
23195
|
+
if (op.kind === OpKind.TwoWayListener) {
|
|
23196
|
+
transformExpressionsInOp(op, (expr) => {
|
|
23197
|
+
if (expr instanceof TwoWayBindingSetExpr) {
|
|
23198
|
+
return wrapAction(expr.target, expr.value);
|
|
23199
|
+
}
|
|
23200
|
+
return expr;
|
|
23201
|
+
}, VisitorContextFlag.InChildOperation);
|
|
23202
|
+
}
|
|
23203
|
+
}
|
|
23204
|
+
}
|
|
23205
|
+
}
|
|
23206
|
+
function wrapSetOperation(target, value) {
|
|
23207
|
+
return twoWayBindingSet(target, value).or(target.set(value));
|
|
23208
|
+
}
|
|
23209
|
+
function isReadExpression(value) {
|
|
23210
|
+
return value instanceof ReadPropExpr || value instanceof ReadKeyExpr;
|
|
23211
|
+
}
|
|
23212
|
+
function wrapAction(target, value) {
|
|
23213
|
+
// The only officially supported expressions inside of a two-way binding are read expressions.
|
|
23214
|
+
if (isReadExpression(target)) {
|
|
23215
|
+
return wrapSetOperation(target, value);
|
|
23216
|
+
}
|
|
23217
|
+
// However, historically the expression parser was handling two-way events by appending `=$event`
|
|
23218
|
+
// to the raw string before attempting to parse it. This has led to bugs over the years (see
|
|
23219
|
+
// #37809) and to unintentionally supporting unassignable events in the two-way binding. The
|
|
23220
|
+
// logic below aims to emulate the old behavior while still supporting the new output format
|
|
23221
|
+
// which uses `twoWayBindingSet`. Note that the generated code doesn't necessarily make sense
|
|
23222
|
+
// based on what the user wrote, for example the event binding for `[(value)]="a ? b : c"`
|
|
23223
|
+
// would produce `ctx.a ? ctx.b : ctx.c = $event`. We aim to reproduce what the parser used
|
|
23224
|
+
// to generate before #54154.
|
|
23225
|
+
if (target instanceof BinaryOperatorExpr && isReadExpression(target.rhs)) {
|
|
23226
|
+
// `a && b` -> `ctx.a && twoWayBindingSet(ctx.b, $event) || (ctx.b = $event)`
|
|
23227
|
+
return new BinaryOperatorExpr(target.operator, target.lhs, wrapSetOperation(target.rhs, value));
|
|
23228
|
+
}
|
|
23229
|
+
// Note: this also supports nullish coalescing expressions which
|
|
23230
|
+
// would've been downleveled to ternary expressions by this point.
|
|
23231
|
+
if (target instanceof ConditionalExpr && isReadExpression(target.falseCase)) {
|
|
23232
|
+
// `a ? b : c` -> `ctx.a ? ctx.b : twoWayBindingSet(ctx.c, $event) || (ctx.c = $event)`
|
|
23233
|
+
return new ConditionalExpr(target.condition, target.trueCase, wrapSetOperation(target.falseCase, value));
|
|
23234
|
+
}
|
|
23235
|
+
// `!!a` -> `twoWayBindingSet(ctx.a, $event) || (ctx.a = $event)`
|
|
23236
|
+
// Note: previously we'd actually produce `!!(ctx.a = $event)`, but the wrapping
|
|
23237
|
+
// node doesn't affect the result so we don't need to carry it over.
|
|
23238
|
+
if (target instanceof NotExpr) {
|
|
23239
|
+
let expr = target.condition;
|
|
23240
|
+
while (true) {
|
|
23241
|
+
if (expr instanceof NotExpr) {
|
|
23242
|
+
expr = expr.condition;
|
|
23243
|
+
}
|
|
23244
|
+
else {
|
|
23245
|
+
if (isReadExpression(expr)) {
|
|
23246
|
+
return wrapSetOperation(expr, value);
|
|
23247
|
+
}
|
|
23248
|
+
break;
|
|
23249
|
+
}
|
|
23250
|
+
}
|
|
23251
|
+
}
|
|
23252
|
+
throw new Error(`Unsupported expression in two-way action binding.`);
|
|
23253
|
+
}
|
|
23254
|
+
|
|
22944
23255
|
/**
|
|
22945
23256
|
* When inside of a listener, we may need access to one or more enclosing views. Therefore, each
|
|
22946
23257
|
* view should save the current view, and each listener must have the ability to restore the
|
|
@@ -22956,7 +23267,7 @@ function saveAndRestoreView(job) {
|
|
|
22956
23267
|
}, new GetCurrentViewExpr(), VariableFlags.None),
|
|
22957
23268
|
]);
|
|
22958
23269
|
for (const op of unit.create) {
|
|
22959
|
-
if (op.kind !== OpKind.Listener) {
|
|
23270
|
+
if (op.kind !== OpKind.Listener && op.kind !== OpKind.TwoWayListener) {
|
|
22960
23271
|
continue;
|
|
22961
23272
|
}
|
|
22962
23273
|
// Embedded views always need the save/restore view operation.
|
|
@@ -23146,7 +23457,7 @@ function generateTemporaries(ops) {
|
|
|
23146
23457
|
generatedStatements.push(...Array.from(new Set(defs.values()))
|
|
23147
23458
|
.map(name => createStatementOp(new DeclareVarStmt(name))));
|
|
23148
23459
|
opCount++;
|
|
23149
|
-
if (op.kind === OpKind.Listener) {
|
|
23460
|
+
if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
|
|
23150
23461
|
op.handlerOps.prepend(generateTemporaries(op.handlerOps));
|
|
23151
23462
|
}
|
|
23152
23463
|
}
|
|
@@ -23394,6 +23705,9 @@ function varsUsedByOp(op) {
|
|
|
23394
23705
|
slots += op.expression.expressions.length;
|
|
23395
23706
|
}
|
|
23396
23707
|
return slots;
|
|
23708
|
+
case OpKind.TwoWayProperty:
|
|
23709
|
+
// Two-way properties can only have expressions so they only need one variable slot.
|
|
23710
|
+
return 1;
|
|
23397
23711
|
case OpKind.StyleProp:
|
|
23398
23712
|
case OpKind.ClassProp:
|
|
23399
23713
|
case OpKind.StyleMap:
|
|
@@ -23467,14 +23781,14 @@ function optimizeVariables(job) {
|
|
|
23467
23781
|
inlineAlwaysInlineVariables(unit.create);
|
|
23468
23782
|
inlineAlwaysInlineVariables(unit.update);
|
|
23469
23783
|
for (const op of unit.create) {
|
|
23470
|
-
if (op.kind === OpKind.Listener) {
|
|
23784
|
+
if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
|
|
23471
23785
|
inlineAlwaysInlineVariables(op.handlerOps);
|
|
23472
23786
|
}
|
|
23473
23787
|
}
|
|
23474
23788
|
optimizeVariablesInOpList(unit.create, job.compatibility);
|
|
23475
23789
|
optimizeVariablesInOpList(unit.update, job.compatibility);
|
|
23476
23790
|
for (const op of unit.create) {
|
|
23477
|
-
if (op.kind === OpKind.Listener) {
|
|
23791
|
+
if (op.kind === OpKind.Listener || op.kind === OpKind.TwoWayListener) {
|
|
23478
23792
|
optimizeVariablesInOpList(op.handlerOps, job.compatibility);
|
|
23479
23793
|
}
|
|
23480
23794
|
}
|
|
@@ -23929,6 +24243,7 @@ const phases = [
|
|
|
23929
24243
|
{ kind: CompilationJobKind.Tmpl, fn: generateTrackVariables },
|
|
23930
24244
|
{ kind: CompilationJobKind.Both, fn: resolveNames },
|
|
23931
24245
|
{ kind: CompilationJobKind.Tmpl, fn: resolveDeferTargetNames },
|
|
24246
|
+
{ kind: CompilationJobKind.Tmpl, fn: transformTwoWayBindingSet },
|
|
23932
24247
|
{ kind: CompilationJobKind.Tmpl, fn: optimizeTrackFns },
|
|
23933
24248
|
{ kind: CompilationJobKind.Both, fn: resolveContexts },
|
|
23934
24249
|
{ kind: CompilationJobKind.Both, fn: resolveSanitizers },
|
|
@@ -23937,7 +24252,6 @@ const phases = [
|
|
|
23937
24252
|
{ kind: CompilationJobKind.Both, fn: expandSafeReads },
|
|
23938
24253
|
{ kind: CompilationJobKind.Both, fn: generateTemporaryVariables },
|
|
23939
24254
|
{ kind: CompilationJobKind.Tmpl, fn: allocateSlots },
|
|
23940
|
-
{ kind: CompilationJobKind.Tmpl, fn: createDeferDepsFns },
|
|
23941
24255
|
{ kind: CompilationJobKind.Tmpl, fn: resolveI18nElementPlaceholders },
|
|
23942
24256
|
{ kind: CompilationJobKind.Tmpl, fn: resolveI18nExpressionPlaceholders },
|
|
23943
24257
|
{ kind: CompilationJobKind.Tmpl, fn: extractI18nMessages },
|
|
@@ -23950,6 +24264,7 @@ const phases = [
|
|
|
23950
24264
|
{ kind: CompilationJobKind.Tmpl, fn: generateAdvance },
|
|
23951
24265
|
{ kind: CompilationJobKind.Both, fn: optimizeVariables },
|
|
23952
24266
|
{ kind: CompilationJobKind.Both, fn: nameFunctionsAndVariables },
|
|
24267
|
+
{ kind: CompilationJobKind.Tmpl, fn: createDeferDepsFns },
|
|
23953
24268
|
{ kind: CompilationJobKind.Tmpl, fn: mergeNextContextExpressions },
|
|
23954
24269
|
{ kind: CompilationJobKind.Tmpl, fn: generateNgContainerOps },
|
|
23955
24270
|
{ kind: CompilationJobKind.Tmpl, fn: collapseEmptyInstructions },
|
|
@@ -24075,8 +24390,8 @@ const NG_TEMPLATE_TAG_NAME$1 = 'ng-template';
|
|
|
24075
24390
|
* representation.
|
|
24076
24391
|
* TODO: Refactor more of the ingestion code into phases.
|
|
24077
24392
|
*/
|
|
24078
|
-
function ingestComponent(componentName, template, constantPool, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta) {
|
|
24079
|
-
const job = new ComponentCompilationJob(componentName, constantPool, compatibilityMode, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta);
|
|
24393
|
+
function ingestComponent(componentName, template, constantPool, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta, allDeferrableDepsFn) {
|
|
24394
|
+
const job = new ComponentCompilationJob(componentName, constantPool, compatibilityMode, relativeContextFilePath, i18nUseExternalIds, deferBlocksMeta, allDeferrableDepsFn);
|
|
24080
24395
|
ingestNodes(job.root, template);
|
|
24081
24396
|
return job;
|
|
24082
24397
|
}
|
|
@@ -24134,7 +24449,7 @@ function ingestHostAttribute(job, name, value, securityContexts) {
|
|
|
24134
24449
|
job.root.update.push(attrBinding);
|
|
24135
24450
|
}
|
|
24136
24451
|
function ingestHostEvent(job, event) {
|
|
24137
|
-
const [phase, target] = event.type
|
|
24452
|
+
const [phase, target] = event.type !== 1 /* e.ParsedEventType.Animation */ ? [null, event.targetOrPhase] :
|
|
24138
24453
|
[event.targetOrPhase, null];
|
|
24139
24454
|
const eventBinding = createListenerOp(job.root.xref, new SlotHandle(), event.name, null, makeListenerHandlerOps(job.root, event.handler, event.handlerSpan), phase, target, true, event.sourceSpan);
|
|
24140
24455
|
job.root.create.push(eventBinding);
|
|
@@ -24400,7 +24715,7 @@ function ingestDeferBlock(unit, deferBlock) {
|
|
|
24400
24715
|
const error = ingestDeferView(unit, 'Error', deferBlock.error?.i18n, deferBlock.error?.children, deferBlock.error?.sourceSpan);
|
|
24401
24716
|
// Create the main defer op, and ops for all secondary views.
|
|
24402
24717
|
const deferXref = unit.job.allocateXrefId();
|
|
24403
|
-
const deferOp = createDeferOp(deferXref, main.xref, main.handle, blockMeta, deferBlock.sourceSpan);
|
|
24718
|
+
const deferOp = createDeferOp(deferXref, main.xref, main.handle, blockMeta, unit.job.allDeferrableDepsFn, deferBlock.sourceSpan);
|
|
24404
24719
|
deferOp.placeholderView = placeholder?.xref ?? null;
|
|
24405
24720
|
deferOp.placeholderSlot = placeholder?.handle ?? null;
|
|
24406
24721
|
deferOp.loadingSlot = loading?.handle ?? null;
|
|
@@ -24725,6 +25040,7 @@ function convertAstWithInterpolation(job, value, i18nMeta, sourceSpan) {
|
|
|
24725
25040
|
// TODO: Can we populate Template binding kinds in ingest?
|
|
24726
25041
|
const BINDING_KINDS = new Map([
|
|
24727
25042
|
[0 /* e.BindingType.Property */, BindingKind.Property],
|
|
25043
|
+
[5 /* e.BindingType.TwoWay */, BindingKind.TwoWayProperty],
|
|
24728
25044
|
[1 /* e.BindingType.Attribute */, BindingKind.Attribute],
|
|
24729
25045
|
[2 /* e.BindingType.Class */, BindingKind.ClassName],
|
|
24730
25046
|
[3 /* e.BindingType.Style */, BindingKind.StyleProperty],
|
|
@@ -24768,12 +25084,20 @@ function asMessage(i18nMeta) {
|
|
|
24768
25084
|
*/
|
|
24769
25085
|
function ingestElementBindings(unit, op, element) {
|
|
24770
25086
|
let bindings = new Array();
|
|
25087
|
+
let i18nAttributeBindingNames = new Set();
|
|
24771
25088
|
for (const attr of element.attributes) {
|
|
24772
25089
|
// Attribute literal bindings, such as `attr.foo="bar"`.
|
|
24773
25090
|
const securityContext = domSchema.securityContext(element.name, attr.name, true);
|
|
24774
25091
|
bindings.push(createBindingOp(op.xref, BindingKind.Attribute, attr.name, convertAstWithInterpolation(unit.job, attr.value, attr.i18n), null, securityContext, true, false, null, asMessage(attr.i18n), attr.sourceSpan));
|
|
25092
|
+
if (attr.i18n) {
|
|
25093
|
+
i18nAttributeBindingNames.add(attr.name);
|
|
25094
|
+
}
|
|
24775
25095
|
}
|
|
24776
25096
|
for (const input of element.inputs) {
|
|
25097
|
+
if (i18nAttributeBindingNames.has(input.name)) {
|
|
25098
|
+
console.error(`On component ${unit.job.componentName}, the binding ${input
|
|
25099
|
+
.name} is both an i18n attribute and a property. You may want to remove the property binding. This will become a compilation error in future versions of Angular.`);
|
|
25100
|
+
}
|
|
24777
25101
|
// All dynamic bindings (both attribute and property bindings).
|
|
24778
25102
|
bindings.push(createBindingOp(op.xref, BINDING_KINDS.get(input.type), input.name, convertAstWithInterpolation(unit.job, astOf(input.value), input.i18n), input.unit, input.securityContext, false, false, null, asMessage(input.i18n) ?? null, input.sourceSpan));
|
|
24779
25103
|
}
|
|
@@ -24783,7 +25107,12 @@ function ingestElementBindings(unit, op, element) {
|
|
|
24783
25107
|
if (output.type === 1 /* e.ParsedEventType.Animation */ && output.phase === null) {
|
|
24784
25108
|
throw Error('Animation listener should have a phase');
|
|
24785
25109
|
}
|
|
24786
|
-
|
|
25110
|
+
if (output.type === 2 /* e.ParsedEventType.TwoWay */) {
|
|
25111
|
+
unit.create.push(createTwoWayListenerOp(op.xref, op.handle, output.name, op.tag, makeTwoWayListenerHandlerOps(unit, output.handler, output.handlerSpan), output.sourceSpan));
|
|
25112
|
+
}
|
|
25113
|
+
else {
|
|
25114
|
+
unit.create.push(createListenerOp(op.xref, op.handle, output.name, op.tag, makeListenerHandlerOps(unit, output.handler, output.handlerSpan), output.phase, output.target, false, output.sourceSpan));
|
|
25115
|
+
}
|
|
24787
25116
|
}
|
|
24788
25117
|
// If any of the bindings on this element have an i18n message, then an i18n attrs configuration
|
|
24789
25118
|
// op is also required.
|
|
@@ -24822,7 +25151,12 @@ function ingestTemplateBindings(unit, op, template, templateKind) {
|
|
|
24822
25151
|
throw Error('Animation listener should have a phase');
|
|
24823
25152
|
}
|
|
24824
25153
|
if (templateKind === TemplateKind.NgTemplate) {
|
|
24825
|
-
|
|
25154
|
+
if (output.type === 2 /* e.ParsedEventType.TwoWay */) {
|
|
25155
|
+
unit.create.push(createTwoWayListenerOp(op.xref, op.handle, output.name, op.tag, makeTwoWayListenerHandlerOps(unit, output.handler, output.handlerSpan), output.sourceSpan));
|
|
25156
|
+
}
|
|
25157
|
+
else {
|
|
25158
|
+
unit.create.push(createListenerOp(op.xref, op.handle, output.name, op.tag, makeListenerHandlerOps(unit, output.handler, output.handlerSpan), output.phase, output.target, false, output.sourceSpan));
|
|
25159
|
+
}
|
|
24826
25160
|
}
|
|
24827
25161
|
if (templateKind === TemplateKind.Structural &&
|
|
24828
25162
|
output.type !== 1 /* e.ParsedEventType.Animation */) {
|
|
@@ -24869,14 +25203,19 @@ function createTemplateBinding(view, xref, type, name, value, unit, securityCont
|
|
|
24869
25203
|
// If this is a structural template, then several kinds of bindings should not result in an
|
|
24870
25204
|
// update instruction.
|
|
24871
25205
|
if (templateKind === TemplateKind.Structural) {
|
|
24872
|
-
if (!isStructuralTemplateAttribute
|
|
24873
|
-
(type
|
|
24874
|
-
|
|
24875
|
-
|
|
24876
|
-
|
|
24877
|
-
|
|
24878
|
-
|
|
24879
|
-
|
|
25206
|
+
if (!isStructuralTemplateAttribute) {
|
|
25207
|
+
switch (type) {
|
|
25208
|
+
case 0 /* e.BindingType.Property */:
|
|
25209
|
+
case 2 /* e.BindingType.Class */:
|
|
25210
|
+
case 3 /* e.BindingType.Style */:
|
|
25211
|
+
// Because this binding doesn't really target the ng-template, it must be a binding on an
|
|
25212
|
+
// inner node of a structural template. We can't skip it entirely, because we still need
|
|
25213
|
+
// it on the ng-template's consts (e.g. for the purposes of directive matching). However,
|
|
25214
|
+
// we should not generate an update instruction for it.
|
|
25215
|
+
return createExtractedAttributeOp(xref, BindingKind.Property, null, name, null, null, i18nMessage, securityContext);
|
|
25216
|
+
case 5 /* e.BindingType.TwoWay */:
|
|
25217
|
+
return createExtractedAttributeOp(xref, BindingKind.TwoWayProperty, null, name, null, null, i18nMessage, securityContext);
|
|
25218
|
+
}
|
|
24880
25219
|
}
|
|
24881
25220
|
if (!isTextBinding && (type === 1 /* e.BindingType.Attribute */ || type === 4 /* e.BindingType.Animation */)) {
|
|
24882
25221
|
// Again, this binding doesn't really target the ng-template; it actually targets the element
|
|
@@ -24924,6 +25263,25 @@ function makeListenerHandlerOps(unit, handler, handlerSpan) {
|
|
|
24924
25263
|
handlerOps.push(createStatementOp(new ReturnStatement(returnExpr, returnExpr.sourceSpan)));
|
|
24925
25264
|
return handlerOps;
|
|
24926
25265
|
}
|
|
25266
|
+
function makeTwoWayListenerHandlerOps(unit, handler, handlerSpan) {
|
|
25267
|
+
handler = astOf(handler);
|
|
25268
|
+
const handlerOps = new Array();
|
|
25269
|
+
if (handler instanceof Chain) {
|
|
25270
|
+
if (handler.expressions.length === 1) {
|
|
25271
|
+
handler = handler.expressions[0];
|
|
25272
|
+
}
|
|
25273
|
+
else {
|
|
25274
|
+
// This is validated during parsing already, but we do it here just in case.
|
|
25275
|
+
throw new Error('Expected two-way listener to have a single expression.');
|
|
25276
|
+
}
|
|
25277
|
+
}
|
|
25278
|
+
const handlerExpr = convertAst(handler, unit.job, handlerSpan);
|
|
25279
|
+
const eventReference = new LexicalReadExpr('$event');
|
|
25280
|
+
const twoWaySetExpr = new TwoWayBindingSetExpr(handlerExpr, eventReference);
|
|
25281
|
+
handlerOps.push(createStatementOp(new ExpressionStatement(twoWaySetExpr)));
|
|
25282
|
+
handlerOps.push(createStatementOp(new ReturnStatement(eventReference)));
|
|
25283
|
+
return handlerOps;
|
|
25284
|
+
}
|
|
24927
25285
|
function astOf(ast) {
|
|
24928
25286
|
return ast instanceof ASTWithSource ? ast.ast : ast;
|
|
24929
25287
|
}
|
|
@@ -25153,7 +25511,7 @@ class BindingParser {
|
|
|
25153
25511
|
for (const propName of Object.keys(properties)) {
|
|
25154
25512
|
const expression = properties[propName];
|
|
25155
25513
|
if (typeof expression === 'string') {
|
|
25156
|
-
this.parsePropertyBinding(propName, expression, true, sourceSpan, sourceSpan.start.offset, undefined, [],
|
|
25514
|
+
this.parsePropertyBinding(propName, expression, true, false, sourceSpan, sourceSpan.start.offset, undefined, [],
|
|
25157
25515
|
// Use the `sourceSpan` for `keySpan`. This isn't really accurate, but neither is the
|
|
25158
25516
|
// sourceSpan, as it represents the sourceSpan of the host itself rather than the
|
|
25159
25517
|
// source of the host binding (which doesn't exist in the template). Regardless,
|
|
@@ -25249,7 +25607,7 @@ class BindingParser {
|
|
|
25249
25607
|
else if (binding.value) {
|
|
25250
25608
|
const srcSpan = isIvyAst ? bindingSpan : sourceSpan;
|
|
25251
25609
|
const valueSpan = moveParseSourceSpan(sourceSpan, binding.value.ast.sourceSpan);
|
|
25252
|
-
this._parsePropertyAst(key, binding.value, srcSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
25610
|
+
this._parsePropertyAst(key, binding.value, false, srcSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
25253
25611
|
}
|
|
25254
25612
|
else {
|
|
25255
25613
|
targetMatchableAttrs.push([key, '' /* value */]);
|
|
@@ -25302,7 +25660,7 @@ class BindingParser {
|
|
|
25302
25660
|
targetProps.push(new ParsedProperty(name, this._exprParser.wrapLiteralPrimitive(value, '', absoluteOffset), ParsedPropertyType.LITERAL_ATTR, sourceSpan, keySpan, valueSpan));
|
|
25303
25661
|
}
|
|
25304
25662
|
}
|
|
25305
|
-
parsePropertyBinding(name, expression, isHost, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs, targetProps, keySpan) {
|
|
25663
|
+
parsePropertyBinding(name, expression, isHost, isPartOfAssignmentBinding, sourceSpan, absoluteOffset, valueSpan, targetMatchableAttrs, targetProps, keySpan) {
|
|
25306
25664
|
if (name.length === 0) {
|
|
25307
25665
|
this._reportError(`Property name is missing in binding`, sourceSpan);
|
|
25308
25666
|
}
|
|
@@ -25325,20 +25683,20 @@ class BindingParser {
|
|
|
25325
25683
|
this._parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
25326
25684
|
}
|
|
25327
25685
|
else {
|
|
25328
|
-
this._parsePropertyAst(name, this.parseBinding(expression, isHost, valueSpan || sourceSpan, absoluteOffset), sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
25686
|
+
this._parsePropertyAst(name, this.parseBinding(expression, isHost, valueSpan || sourceSpan, absoluteOffset), isPartOfAssignmentBinding, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
25329
25687
|
}
|
|
25330
25688
|
}
|
|
25331
25689
|
parsePropertyInterpolation(name, value, sourceSpan, valueSpan, targetMatchableAttrs, targetProps, keySpan, interpolatedTokens) {
|
|
25332
25690
|
const expr = this.parseInterpolation(value, valueSpan || sourceSpan, interpolatedTokens);
|
|
25333
25691
|
if (expr) {
|
|
25334
|
-
this._parsePropertyAst(name, expr, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
25692
|
+
this._parsePropertyAst(name, expr, false, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps);
|
|
25335
25693
|
return true;
|
|
25336
25694
|
}
|
|
25337
25695
|
return false;
|
|
25338
25696
|
}
|
|
25339
|
-
_parsePropertyAst(name, ast, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
|
|
25697
|
+
_parsePropertyAst(name, ast, isPartOfAssignmentBinding, sourceSpan, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
|
|
25340
25698
|
targetMatchableAttrs.push([name, ast.source]);
|
|
25341
|
-
targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.DEFAULT, sourceSpan, keySpan, valueSpan));
|
|
25699
|
+
targetProps.push(new ParsedProperty(name, ast, isPartOfAssignmentBinding ? ParsedPropertyType.TWO_WAY : ParsedPropertyType.DEFAULT, sourceSpan, keySpan, valueSpan));
|
|
25342
25700
|
}
|
|
25343
25701
|
_parseAnimation(name, expression, sourceSpan, absoluteOffset, keySpan, valueSpan, targetMatchableAttrs, targetProps) {
|
|
25344
25702
|
if (name.length === 0) {
|
|
@@ -25408,7 +25766,8 @@ class BindingParser {
|
|
|
25408
25766
|
const mappedPropName = this._schemaRegistry.getMappedPropName(boundProp.name);
|
|
25409
25767
|
boundPropertyName = mapPropertyName ? mappedPropName : boundProp.name;
|
|
25410
25768
|
securityContexts = calcPossibleSecurityContexts(this._schemaRegistry, elementSelector, mappedPropName, false);
|
|
25411
|
-
bindingType =
|
|
25769
|
+
bindingType =
|
|
25770
|
+
boundProp.type === ParsedPropertyType.TWO_WAY ? 5 /* BindingType.TwoWay */ : 0 /* BindingType.Property */;
|
|
25412
25771
|
if (!skipValidation) {
|
|
25413
25772
|
this._validatePropertyOrAttributeName(mappedPropName, boundProp.sourceSpan, false);
|
|
25414
25773
|
}
|
|
@@ -25425,7 +25784,7 @@ class BindingParser {
|
|
|
25425
25784
|
if (keySpan !== undefined) {
|
|
25426
25785
|
keySpan = moveParseSourceSpan(keySpan, new AbsoluteSourceSpan(keySpan.start.offset + 1, keySpan.end.offset));
|
|
25427
25786
|
}
|
|
25428
|
-
this._parseAnimationEvent(name, expression,
|
|
25787
|
+
this._parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan);
|
|
25429
25788
|
}
|
|
25430
25789
|
else {
|
|
25431
25790
|
this._parseRegularEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan);
|
|
@@ -25435,11 +25794,11 @@ class BindingParser {
|
|
|
25435
25794
|
const prop = this._schemaRegistry.getMappedPropName(propName);
|
|
25436
25795
|
return calcPossibleSecurityContexts(this._schemaRegistry, selector, prop, isAttribute);
|
|
25437
25796
|
}
|
|
25438
|
-
_parseAnimationEvent(name, expression,
|
|
25797
|
+
_parseAnimationEvent(name, expression, sourceSpan, handlerSpan, targetEvents, keySpan) {
|
|
25439
25798
|
const matches = splitAtPeriod(name, [name, '']);
|
|
25440
25799
|
const eventName = matches[0];
|
|
25441
25800
|
const phase = matches[1].toLowerCase();
|
|
25442
|
-
const ast = this._parseAction(expression,
|
|
25801
|
+
const ast = this._parseAction(expression, handlerSpan);
|
|
25443
25802
|
targetEvents.push(new ParsedEvent(eventName, phase, 1 /* ParsedEventType.Animation */, ast, sourceSpan, handlerSpan, keySpan));
|
|
25444
25803
|
if (eventName.length === 0) {
|
|
25445
25804
|
this._reportError(`Animation event name is missing in binding`, sourceSpan);
|
|
@@ -25456,17 +25815,24 @@ class BindingParser {
|
|
|
25456
25815
|
_parseRegularEvent(name, expression, isAssignmentEvent, sourceSpan, handlerSpan, targetMatchableAttrs, targetEvents, keySpan) {
|
|
25457
25816
|
// long format: 'target: eventName'
|
|
25458
25817
|
const [target, eventName] = splitAtColon(name, [null, name]);
|
|
25459
|
-
const
|
|
25818
|
+
const prevErrorCount = this.errors.length;
|
|
25819
|
+
const ast = this._parseAction(expression, handlerSpan);
|
|
25820
|
+
const isValid = this.errors.length === prevErrorCount;
|
|
25460
25821
|
targetMatchableAttrs.push([name, ast.source]);
|
|
25461
|
-
|
|
25822
|
+
// Don't try to validate assignment events if there were other
|
|
25823
|
+
// parsing errors to avoid adding more noise to the error logs.
|
|
25824
|
+
if (isAssignmentEvent && isValid && !this._isAllowedAssignmentEvent(ast)) {
|
|
25825
|
+
this._reportError('Unsupported expression in a two-way binding', sourceSpan);
|
|
25826
|
+
}
|
|
25827
|
+
targetEvents.push(new ParsedEvent(eventName, target, isAssignmentEvent ? 2 /* ParsedEventType.TwoWay */ : 0 /* ParsedEventType.Regular */, ast, sourceSpan, handlerSpan, keySpan));
|
|
25462
25828
|
// Don't detect directives for event names for now,
|
|
25463
25829
|
// so don't add the event name to the matchableAttrs
|
|
25464
25830
|
}
|
|
25465
|
-
_parseAction(value,
|
|
25831
|
+
_parseAction(value, sourceSpan) {
|
|
25466
25832
|
const sourceInfo = (sourceSpan && sourceSpan.start || '(unknown').toString();
|
|
25467
25833
|
const absoluteOffset = (sourceSpan && sourceSpan.start) ? sourceSpan.start.offset : 0;
|
|
25468
25834
|
try {
|
|
25469
|
-
const ast = this._exprParser.parseAction(value,
|
|
25835
|
+
const ast = this._exprParser.parseAction(value, sourceInfo, absoluteOffset, this._interpolationConfig);
|
|
25470
25836
|
if (ast) {
|
|
25471
25837
|
this._reportExpressionParserErrors(ast.errors, sourceSpan);
|
|
25472
25838
|
}
|
|
@@ -25501,6 +25867,26 @@ class BindingParser {
|
|
|
25501
25867
|
this._reportError(report.msg, sourceSpan, ParseErrorLevel.ERROR);
|
|
25502
25868
|
}
|
|
25503
25869
|
}
|
|
25870
|
+
/**
|
|
25871
|
+
* Returns whether a parsed AST is allowed to be used within the event side of a two-way binding.
|
|
25872
|
+
* @param ast Parsed AST to be checked.
|
|
25873
|
+
*/
|
|
25874
|
+
_isAllowedAssignmentEvent(ast) {
|
|
25875
|
+
if (ast instanceof ASTWithSource) {
|
|
25876
|
+
return this._isAllowedAssignmentEvent(ast.ast);
|
|
25877
|
+
}
|
|
25878
|
+
if (ast instanceof NonNullAssert) {
|
|
25879
|
+
return this._isAllowedAssignmentEvent(ast.expression);
|
|
25880
|
+
}
|
|
25881
|
+
if (ast instanceof PropertyRead || ast instanceof KeyedRead) {
|
|
25882
|
+
return true;
|
|
25883
|
+
}
|
|
25884
|
+
if (ast instanceof Binary) {
|
|
25885
|
+
return (ast.operation === '&&' || ast.operation === '||' || ast.operation === '??') &&
|
|
25886
|
+
(ast.right instanceof PropertyRead || ast.right instanceof KeyedRead);
|
|
25887
|
+
}
|
|
25888
|
+
return ast instanceof Conditional || ast instanceof PrefixNot;
|
|
25889
|
+
}
|
|
25504
25890
|
}
|
|
25505
25891
|
class PipeCollector extends RecursiveAstVisitor {
|
|
25506
25892
|
constructor() {
|
|
@@ -25633,11 +26019,16 @@ const FOR_LOOP_EXPRESSION_PATTERN = /^\s*([0-9A-Za-z_$]*)\s+of\s+([\S\s]*)/;
|
|
|
25633
26019
|
/** Pattern for the tracking expression in a for loop block. */
|
|
25634
26020
|
const FOR_LOOP_TRACK_PATTERN = /^track\s+([\S\s]*)/;
|
|
25635
26021
|
/** Pattern for the `as` expression in a conditional block. */
|
|
25636
|
-
const CONDITIONAL_ALIAS_PATTERN = /^as\s+(.*)/;
|
|
26022
|
+
const CONDITIONAL_ALIAS_PATTERN = /^(as\s)+(.*)/;
|
|
25637
26023
|
/** Pattern used to identify an `else if` block. */
|
|
25638
26024
|
const ELSE_IF_PATTERN = /^else[^\S\r\n]+if/;
|
|
25639
26025
|
/** Pattern used to identify a `let` parameter. */
|
|
25640
26026
|
const FOR_LOOP_LET_PATTERN = /^let\s+([\S\s]*)/;
|
|
26027
|
+
/**
|
|
26028
|
+
* Pattern to group a string into leading whitespace, non whitespace, and trailing whitespace.
|
|
26029
|
+
* Useful for getting the variable name span when a span can contain leading and trailing space.
|
|
26030
|
+
*/
|
|
26031
|
+
const CHARACTERS_IN_SURROUNDING_WHITESPACE_PATTERN = /(\s*)(\S+)(\s*)/;
|
|
25641
26032
|
/** Names of variables that are allowed to be used in the `let` expression of a `for` loop. */
|
|
25642
26033
|
const ALLOWED_FOR_LOOP_LET_VARIABLES = new Set(['$index', '$first', '$last', '$even', '$odd', '$count']);
|
|
25643
26034
|
/**
|
|
@@ -25777,8 +26168,13 @@ function parseForLoopParameters(block, errors, bindingParser) {
|
|
|
25777
26168
|
return null;
|
|
25778
26169
|
}
|
|
25779
26170
|
const [, itemName, rawExpression] = match;
|
|
26171
|
+
// `expressionParam.expression` contains the variable declaration and the expression of the
|
|
26172
|
+
// for...of statement, i.e. 'user of users' The variable of a ForOfStatement is _only_ the "const
|
|
26173
|
+
// user" part and does not include "of x".
|
|
26174
|
+
const variableName = expressionParam.expression.split(' ')[0];
|
|
26175
|
+
const variableSpan = new ParseSourceSpan(expressionParam.sourceSpan.start, expressionParam.sourceSpan.start.moveBy(variableName.length));
|
|
25780
26176
|
const result = {
|
|
25781
|
-
itemName: new Variable(itemName, '$implicit',
|
|
26177
|
+
itemName: new Variable(itemName, '$implicit', variableSpan, variableSpan),
|
|
25782
26178
|
trackBy: null,
|
|
25783
26179
|
expression: parseBlockParameterToBinding(expressionParam, bindingParser, rawExpression),
|
|
25784
26180
|
context: {},
|
|
@@ -25786,7 +26182,8 @@ function parseForLoopParameters(block, errors, bindingParser) {
|
|
|
25786
26182
|
for (const param of secondaryParams) {
|
|
25787
26183
|
const letMatch = param.expression.match(FOR_LOOP_LET_PATTERN);
|
|
25788
26184
|
if (letMatch !== null) {
|
|
25789
|
-
|
|
26185
|
+
const variablesSpan = new ParseSourceSpan(param.sourceSpan.start.moveBy(letMatch[0].length - letMatch[1].length), param.sourceSpan.end);
|
|
26186
|
+
parseLetParameter(param.sourceSpan, letMatch[1], variablesSpan, result.context, errors);
|
|
25790
26187
|
continue;
|
|
25791
26188
|
}
|
|
25792
26189
|
const trackMatch = param.expression.match(FOR_LOOP_TRACK_PATTERN);
|
|
@@ -25817,6 +26214,7 @@ function parseForLoopParameters(block, errors, bindingParser) {
|
|
|
25817
26214
|
/** Parses the `let` parameter of a `for` loop block. */
|
|
25818
26215
|
function parseLetParameter(sourceSpan, expression, span, context, errors) {
|
|
25819
26216
|
const parts = expression.split(',');
|
|
26217
|
+
let startSpan = span.start;
|
|
25820
26218
|
for (const part of parts) {
|
|
25821
26219
|
const expressionParts = part.split('=');
|
|
25822
26220
|
const name = expressionParts.length === 2 ? expressionParts[0].trim() : '';
|
|
@@ -25831,8 +26229,26 @@ function parseLetParameter(sourceSpan, expression, span, context, errors) {
|
|
|
25831
26229
|
errors.push(new ParseError(sourceSpan, `Duplicate "let" parameter variable "${variableName}"`));
|
|
25832
26230
|
}
|
|
25833
26231
|
else {
|
|
25834
|
-
|
|
26232
|
+
const [, keyLeadingWhitespace, keyName] = expressionParts[0].match(CHARACTERS_IN_SURROUNDING_WHITESPACE_PATTERN) ?? [];
|
|
26233
|
+
const keySpan = keyLeadingWhitespace !== undefined && expressionParts.length === 2 ?
|
|
26234
|
+
new ParseSourceSpan(
|
|
26235
|
+
/* strip leading spaces */
|
|
26236
|
+
startSpan.moveBy(keyLeadingWhitespace.length),
|
|
26237
|
+
/* advance to end of the variable name */
|
|
26238
|
+
startSpan.moveBy(keyLeadingWhitespace.length + keyName.length)) :
|
|
26239
|
+
span;
|
|
26240
|
+
let valueSpan = undefined;
|
|
26241
|
+
if (expressionParts.length === 2) {
|
|
26242
|
+
const [, valueLeadingWhitespace, implicit] = expressionParts[1].match(CHARACTERS_IN_SURROUNDING_WHITESPACE_PATTERN) ?? [];
|
|
26243
|
+
valueSpan = valueLeadingWhitespace !== undefined ?
|
|
26244
|
+
new ParseSourceSpan(startSpan.moveBy(expressionParts[0].length + 1 + valueLeadingWhitespace.length), startSpan.moveBy(expressionParts[0].length + 1 + valueLeadingWhitespace.length +
|
|
26245
|
+
implicit.length)) :
|
|
26246
|
+
undefined;
|
|
26247
|
+
}
|
|
26248
|
+
const sourceSpan = new ParseSourceSpan(keySpan.start, valueSpan?.end ?? keySpan.end);
|
|
26249
|
+
context[variableName] = new Variable(name, variableName, sourceSpan, keySpan, valueSpan);
|
|
25835
26250
|
}
|
|
26251
|
+
startSpan = startSpan.moveBy(part.length + 1 /* add 1 to move past the comma */);
|
|
25836
26252
|
}
|
|
25837
26253
|
}
|
|
25838
26254
|
/**
|
|
@@ -25944,8 +26360,10 @@ function parseConditionalBlockParameters(block, errors, bindingParser) {
|
|
|
25944
26360
|
errors.push(new ParseError(param.sourceSpan, 'Conditional can only have one "as" expression'));
|
|
25945
26361
|
}
|
|
25946
26362
|
else {
|
|
25947
|
-
const name = aliasMatch[
|
|
25948
|
-
|
|
26363
|
+
const name = aliasMatch[2].trim();
|
|
26364
|
+
const variableStart = param.sourceSpan.start.moveBy(aliasMatch[1].length);
|
|
26365
|
+
const variableSpan = new ParseSourceSpan(variableStart, variableStart.moveBy(name.length));
|
|
26366
|
+
expressionAlias = new Variable(name, name, variableSpan, variableSpan);
|
|
25949
26367
|
}
|
|
25950
26368
|
}
|
|
25951
26369
|
return { expression, expressionAlias };
|
|
@@ -26806,7 +27224,7 @@ class HtmlAstToIvyAst {
|
|
|
26806
27224
|
if (bindParts[KW_BIND_IDX] != null) {
|
|
26807
27225
|
const identifier = bindParts[IDENT_KW_IDX];
|
|
26808
27226
|
const keySpan = createKeySpan(srcSpan, bindParts[KW_BIND_IDX], identifier);
|
|
26809
|
-
this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
27227
|
+
this.bindingParser.parsePropertyBinding(identifier, value, false, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
26810
27228
|
}
|
|
26811
27229
|
else if (bindParts[KW_LET_IDX]) {
|
|
26812
27230
|
if (isTemplateElement) {
|
|
@@ -26833,7 +27251,7 @@ class HtmlAstToIvyAst {
|
|
|
26833
27251
|
else if (bindParts[KW_BINDON_IDX]) {
|
|
26834
27252
|
const identifier = bindParts[IDENT_KW_IDX];
|
|
26835
27253
|
const keySpan = createKeySpan(srcSpan, bindParts[KW_BINDON_IDX], identifier);
|
|
26836
|
-
this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
27254
|
+
this.bindingParser.parsePropertyBinding(identifier, value, false, true, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
26837
27255
|
this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);
|
|
26838
27256
|
}
|
|
26839
27257
|
else if (bindParts[KW_AT_IDX]) {
|
|
@@ -26863,11 +27281,11 @@ class HtmlAstToIvyAst {
|
|
|
26863
27281
|
const identifier = name.substring(delims.start.length, name.length - delims.end.length);
|
|
26864
27282
|
const keySpan = createKeySpan(srcSpan, delims.start, identifier);
|
|
26865
27283
|
if (delims.start === BINDING_DELIMS.BANANA_BOX.start) {
|
|
26866
|
-
this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
27284
|
+
this.bindingParser.parsePropertyBinding(identifier, value, false, true, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
26867
27285
|
this.parseAssignmentEvent(identifier, value, srcSpan, attribute.valueSpan, matchableAttributes, boundEvents, keySpan);
|
|
26868
27286
|
}
|
|
26869
27287
|
else if (delims.start === BINDING_DELIMS.PROPERTY.start) {
|
|
26870
|
-
this.bindingParser.parsePropertyBinding(identifier, value, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
27288
|
+
this.bindingParser.parsePropertyBinding(identifier, value, false, false, srcSpan, absoluteOffset, attribute.valueSpan, matchableAttributes, parsedProperties, keySpan);
|
|
26871
27289
|
}
|
|
26872
27290
|
else {
|
|
26873
27291
|
const events = [];
|
|
@@ -26909,7 +27327,7 @@ class HtmlAstToIvyAst {
|
|
|
26909
27327
|
}
|
|
26910
27328
|
parseAssignmentEvent(name, expression, sourceSpan, valueSpan, targetMatchableAttrs, boundEvents, keySpan) {
|
|
26911
27329
|
const events = [];
|
|
26912
|
-
this.bindingParser.parseEvent(`${name}Change`,
|
|
27330
|
+
this.bindingParser.parseEvent(`${name}Change`, expression, /* isAssignmentEvent */ true, sourceSpan, valueSpan || sourceSpan, targetMatchableAttrs, events, keySpan);
|
|
26913
27331
|
addEvents(events, boundEvents);
|
|
26914
27332
|
}
|
|
26915
27333
|
reportError(message, sourceSpan, level = ParseErrorLevel.ERROR) {
|
|
@@ -27743,7 +28161,9 @@ function prepareEventListenerParameters(eventAst, handlerName = null, scope = nu
|
|
|
27743
28161
|
const implicitReceiverExpr = (scope === null || scope.bindingLevel === 0) ?
|
|
27744
28162
|
variable(CONTEXT_NAME) :
|
|
27745
28163
|
scope.getOrCreateSharedContextVar(0);
|
|
27746
|
-
const bindingStatements =
|
|
28164
|
+
const bindingStatements = eventAst.type === 2 /* ParsedEventType.TwoWay */ ?
|
|
28165
|
+
convertAssignmentActionBinding(scope, implicitReceiverExpr, handler, 'b', eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS) :
|
|
28166
|
+
convertActionBinding(scope, implicitReceiverExpr, handler, 'b', eventAst.handlerSpan, implicitReceiverAccesses, EVENT_BINDING_SCOPE_GLOBALS);
|
|
27747
28167
|
const statements = [];
|
|
27748
28168
|
const variableDeclarations = scope?.variableDeclarations();
|
|
27749
28169
|
const restoreViewStatement = scope?.restoreViewStatement();
|
|
@@ -28265,7 +28685,8 @@ class TemplateDefinitionBuilder {
|
|
|
28265
28685
|
element.inputs.forEach(input => {
|
|
28266
28686
|
const stylingInputWasSet = stylingBuilder.registerBoundInput(input);
|
|
28267
28687
|
if (!stylingInputWasSet) {
|
|
28268
|
-
if (input.type === 0 /* BindingType.Property */
|
|
28688
|
+
if ((input.type === 0 /* BindingType.Property */ || input.type === 5 /* BindingType.TwoWay */) &&
|
|
28689
|
+
input.i18n) {
|
|
28269
28690
|
boundI18nAttrs.push(input);
|
|
28270
28691
|
}
|
|
28271
28692
|
else {
|
|
@@ -28310,7 +28731,7 @@ class TemplateDefinitionBuilder {
|
|
|
28310
28731
|
// Generate Listeners (outputs)
|
|
28311
28732
|
if (element.outputs.length > 0) {
|
|
28312
28733
|
for (const outputAst of element.outputs) {
|
|
28313
|
-
this.creationInstruction(outputAst.sourceSpan, Identifiers.listener, this.prepareListenerParameter(element.name, outputAst, elementIndex));
|
|
28734
|
+
this.creationInstruction(outputAst.sourceSpan, outputAst.type === 2 /* ParsedEventType.TwoWay */ ? Identifiers.twoWayListener : Identifiers.listener, this.prepareListenerParameter(element.name, outputAst, elementIndex));
|
|
28314
28735
|
}
|
|
28315
28736
|
}
|
|
28316
28737
|
// Note: it's important to keep i18n/i18nStart instructions after i18nAttributes and
|
|
@@ -28353,6 +28774,7 @@ class TemplateDefinitionBuilder {
|
|
|
28353
28774
|
this.allocateBindingSlots(value);
|
|
28354
28775
|
propertyBindings.push({
|
|
28355
28776
|
span: input.sourceSpan,
|
|
28777
|
+
reference: Identifiers.property,
|
|
28356
28778
|
paramsOrFn: getBindingFunctionParams(() => hasValue ? this.convertPropertyBinding(value) : emptyValueBindInstruction, prepareSyntheticPropertyName(input.name))
|
|
28357
28779
|
});
|
|
28358
28780
|
}
|
|
@@ -28391,7 +28813,9 @@ class TemplateDefinitionBuilder {
|
|
|
28391
28813
|
}
|
|
28392
28814
|
}
|
|
28393
28815
|
this.allocateBindingSlots(value);
|
|
28394
|
-
|
|
28816
|
+
// Note: we don't separate two-way property bindings and regular ones,
|
|
28817
|
+
// because their assignment order needs to be maintained.
|
|
28818
|
+
if (inputType === 0 /* BindingType.Property */ || inputType === 5 /* BindingType.TwoWay */) {
|
|
28395
28819
|
if (value instanceof Interpolation$1) {
|
|
28396
28820
|
// prop="{{value}}" and friends
|
|
28397
28821
|
this.interpolatedUpdateInstruction(getPropertyInterpolationExpression(value), elementIndex, attrName, input, value, params);
|
|
@@ -28401,6 +28825,7 @@ class TemplateDefinitionBuilder {
|
|
|
28401
28825
|
// Collect all the properties so that we can chain into a single function at the end.
|
|
28402
28826
|
propertyBindings.push({
|
|
28403
28827
|
span: input.sourceSpan,
|
|
28828
|
+
reference: inputType === 5 /* BindingType.TwoWay */ ? Identifiers.twoWayProperty : Identifiers.property,
|
|
28404
28829
|
paramsOrFn: getBindingFunctionParams(() => this.convertPropertyBinding(value), attrName, params)
|
|
28405
28830
|
});
|
|
28406
28831
|
}
|
|
@@ -28433,7 +28858,7 @@ class TemplateDefinitionBuilder {
|
|
|
28433
28858
|
}
|
|
28434
28859
|
});
|
|
28435
28860
|
for (const propertyBinding of propertyBindings) {
|
|
28436
|
-
this.updateInstructionWithAdvance(elementIndex, propertyBinding.span,
|
|
28861
|
+
this.updateInstructionWithAdvance(elementIndex, propertyBinding.span, propertyBinding.reference, propertyBinding.paramsOrFn);
|
|
28437
28862
|
}
|
|
28438
28863
|
for (const attributeBinding of attributeBindings) {
|
|
28439
28864
|
this.updateInstructionWithAdvance(elementIndex, attributeBinding.span, Identifiers.attribute, attributeBinding.paramsOrFn);
|
|
@@ -28466,7 +28891,9 @@ class TemplateDefinitionBuilder {
|
|
|
28466
28891
|
}
|
|
28467
28892
|
}
|
|
28468
28893
|
const contextName = `${this.contextName}${contextNameSuffix}_${index}`;
|
|
28469
|
-
|
|
28894
|
+
// Note: For the unique name, we don't include an unique suffix, unless really needed.
|
|
28895
|
+
// This keeps the generated output more clean as most of the time, we don't expect conflicts.
|
|
28896
|
+
const name = this.constantPool.uniqueName(`${contextName}_Template`, /** alwaysIncludeSuffix */ false);
|
|
28470
28897
|
// Create the template function
|
|
28471
28898
|
const visitor = new TemplateDefinitionBuilder(this.constantPool, this._bindingScope, this.level + 1, contextName, this.i18n, index, name, this._namespace, this.fileBasedI18nSuffix, this.i18nUseExternalIds, this.deferBlocks, this.elementLocations, this.allDeferrableDepsFn, this._constants);
|
|
28472
28899
|
// Nested templates must not be visited until after their parent templates have completed
|
|
@@ -28529,7 +28956,7 @@ class TemplateDefinitionBuilder {
|
|
|
28529
28956
|
}
|
|
28530
28957
|
// Generate listeners for directive output
|
|
28531
28958
|
for (const outputAst of template.outputs) {
|
|
28532
|
-
this.creationInstruction(outputAst.sourceSpan, Identifiers.listener, this.prepareListenerParameter('ng_template', outputAst, templateIndex));
|
|
28959
|
+
this.creationInstruction(outputAst.sourceSpan, outputAst.type === 2 /* ParsedEventType.TwoWay */ ? Identifiers.twoWayListener : Identifiers.listener, this.prepareListenerParameter('ng_template', outputAst, templateIndex));
|
|
28533
28960
|
}
|
|
28534
28961
|
}
|
|
28535
28962
|
}
|
|
@@ -28783,7 +29210,9 @@ class TemplateDefinitionBuilder {
|
|
|
28783
29210
|
for (const deferredDep of metadata.deps) {
|
|
28784
29211
|
if (deferredDep.isDeferrable) {
|
|
28785
29212
|
// Callback function, e.g. `m () => m.MyCmp;`.
|
|
28786
|
-
const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)],
|
|
29213
|
+
const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)],
|
|
29214
|
+
// Default imports are always accessed through the `default` property.
|
|
29215
|
+
variable('m').prop(deferredDep.isDefaultImport ? 'default' : deferredDep.symbolName));
|
|
28787
29216
|
// Dynamic import, e.g. `import('./a').then(...)`.
|
|
28788
29217
|
const importExpr = (new DynamicImportExpr(deferredDep.importPath)).prop('then').callFn([innerFn]);
|
|
28789
29218
|
dependencyExp.push(importExpr);
|
|
@@ -30290,9 +30719,9 @@ function compileDirectiveFromMetadata(meta, constantPool, bindingParser) {
|
|
|
30290
30719
|
function createDeferredDepsFunction(constantPool, name, deps) {
|
|
30291
30720
|
// This defer block has deps for which we need to generate dynamic imports.
|
|
30292
30721
|
const dependencyExp = [];
|
|
30293
|
-
for (const [symbolName, importPath] of deps) {
|
|
30722
|
+
for (const [symbolName, { importPath, isDefaultImport }] of deps) {
|
|
30294
30723
|
// Callback function, e.g. `m () => m.MyCmp;`.
|
|
30295
|
-
const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(symbolName));
|
|
30724
|
+
const innerFn = arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(isDefaultImport ? 'default' : symbolName));
|
|
30296
30725
|
// Dynamic import, e.g. `import('./a').then(...)`.
|
|
30297
30726
|
const importExpr = (new DynamicImportExpr(importPath)).prop('then').callFn([innerFn]);
|
|
30298
30727
|
dependencyExp.push(importExpr);
|
|
@@ -30321,16 +30750,16 @@ function compileComponentFromMetadata(meta, constantPool, bindingParser) {
|
|
|
30321
30750
|
// e.g. `template: function MyComponent_Template(_ctx, _cm) {...}`
|
|
30322
30751
|
const templateTypeName = meta.name;
|
|
30323
30752
|
const templateName = templateTypeName ? `${templateTypeName}_Template` : null;
|
|
30753
|
+
let allDeferrableDepsFn = null;
|
|
30754
|
+
if (meta.deferBlocks.size > 0 && meta.deferrableTypes.size > 0 &&
|
|
30755
|
+
meta.deferBlockDepsEmitMode === 1 /* DeferBlockDepsEmitMode.PerComponent */) {
|
|
30756
|
+
const fnName = `${templateTypeName}_DeferFn`;
|
|
30757
|
+
allDeferrableDepsFn = createDeferredDepsFunction(constantPool, fnName, meta.deferrableTypes);
|
|
30758
|
+
}
|
|
30324
30759
|
// Template compilation is currently conditional as we're in the process of rewriting it.
|
|
30325
|
-
if (!USE_TEMPLATE_PIPELINE) {
|
|
30760
|
+
if (!USE_TEMPLATE_PIPELINE && !meta.useTemplatePipeline) {
|
|
30326
30761
|
// This is the main path currently used in compilation, which compiles the template with the
|
|
30327
30762
|
// legacy `TemplateDefinitionBuilder`.
|
|
30328
|
-
let allDeferrableDepsFn = null;
|
|
30329
|
-
if (meta.deferBlocks.size > 0 && meta.deferrableTypes.size > 0 &&
|
|
30330
|
-
meta.deferBlockDepsEmitMode === 1 /* DeferBlockDepsEmitMode.PerComponent */) {
|
|
30331
|
-
const fnName = `${templateTypeName}_DeferFn`;
|
|
30332
|
-
allDeferrableDepsFn = createDeferredDepsFunction(constantPool, fnName, meta.deferrableTypes);
|
|
30333
|
-
}
|
|
30334
30763
|
const template = meta.template;
|
|
30335
30764
|
const templateBuilder = new TemplateDefinitionBuilder(constantPool, BindingScope.createRootScope(), 0, templateTypeName, null, null, templateName, Identifiers.namespaceHTML, meta.relativeContextFilePath, meta.i18nUseExternalIds, meta.deferBlocks, new Map(), allDeferrableDepsFn);
|
|
30336
30765
|
const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []);
|
|
@@ -30367,7 +30796,7 @@ function compileComponentFromMetadata(meta, constantPool, bindingParser) {
|
|
|
30367
30796
|
else {
|
|
30368
30797
|
// This path compiles the template using the prototype template pipeline. First the template is
|
|
30369
30798
|
// ingested into IR:
|
|
30370
|
-
const tpl = ingestComponent(meta.name, meta.template.nodes, constantPool, meta.relativeContextFilePath, meta.i18nUseExternalIds, meta.deferBlocks);
|
|
30799
|
+
const tpl = ingestComponent(meta.name, meta.template.nodes, constantPool, meta.relativeContextFilePath, meta.i18nUseExternalIds, meta.deferBlocks, allDeferrableDepsFn);
|
|
30371
30800
|
// Then the IR is transformed to prepare it for cod egeneration.
|
|
30372
30801
|
transform(tpl, CompilationJobKind.Tmpl);
|
|
30373
30802
|
// Finally we emit the template function:
|
|
@@ -30562,7 +30991,7 @@ function createHostBindingsFunction(hostBindingsMetadata, typeSourceSpan, bindin
|
|
|
30562
30991
|
const bindings = bindingParser.createBoundHostProperties(hostBindingsMetadata.properties, typeSourceSpan);
|
|
30563
30992
|
// Calculate host event bindings
|
|
30564
30993
|
const eventBindings = bindingParser.createDirectiveHostEventAsts(hostBindingsMetadata.listeners, typeSourceSpan);
|
|
30565
|
-
if (USE_TEMPLATE_PIPELINE) {
|
|
30994
|
+
if (USE_TEMPLATE_PIPELINE || hostBindingsMetadata.useTemplatePipeline) {
|
|
30566
30995
|
// The parser for host bindings treats class and style attributes specially -- they are
|
|
30567
30996
|
// extracted into these separate fields. This is not the case for templates, so the compiler can
|
|
30568
30997
|
// actually already handle these special attributes internally. Therefore, we just drop them
|
|
@@ -31746,6 +32175,7 @@ function extractScopedNodeEntities(rootScope) {
|
|
|
31746
32175
|
class ResourceLoader {
|
|
31747
32176
|
}
|
|
31748
32177
|
|
|
32178
|
+
const SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT = false;
|
|
31749
32179
|
class CompilerFacadeImpl {
|
|
31750
32180
|
constructor(jitEvaluator = new JitEvaluator()) {
|
|
31751
32181
|
this.jitEvaluator = jitEvaluator;
|
|
@@ -31879,6 +32309,7 @@ class CompilerFacadeImpl {
|
|
|
31879
32309
|
null,
|
|
31880
32310
|
relativeContextFilePath: '',
|
|
31881
32311
|
i18nUseExternalIds: true,
|
|
32312
|
+
useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
|
|
31882
32313
|
};
|
|
31883
32314
|
const jitExpressionSourceMap = `ng:///${facade.name}.js`;
|
|
31884
32315
|
return this.compileComponentFromMeta(angularCoreEnv, jitExpressionSourceMap, meta);
|
|
@@ -32001,7 +32432,10 @@ function convertDirectiveFacadeToMetadata(facade) {
|
|
|
32001
32432
|
typeSourceSpan: facade.typeSourceSpan,
|
|
32002
32433
|
type: wrapReference(facade.type),
|
|
32003
32434
|
deps: null,
|
|
32004
|
-
host:
|
|
32435
|
+
host: {
|
|
32436
|
+
...extractHostBindings(facade.propMetadata, facade.typeSourceSpan, facade.host),
|
|
32437
|
+
useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
|
|
32438
|
+
},
|
|
32005
32439
|
inputs: { ...inputsFromMetadata, ...inputsFromType },
|
|
32006
32440
|
outputs: { ...outputsFromMetadata, ...outputsFromType },
|
|
32007
32441
|
queries: facade.queries.map(convertToR3QueryMetadata),
|
|
@@ -32044,6 +32478,7 @@ function convertHostDeclarationToMetadata(host = {}) {
|
|
|
32044
32478
|
classAttr: host.classAttribute,
|
|
32045
32479
|
styleAttr: host.styleAttribute,
|
|
32046
32480
|
},
|
|
32481
|
+
useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
|
|
32047
32482
|
};
|
|
32048
32483
|
}
|
|
32049
32484
|
function convertHostDirectivesToMetadata(metadata) {
|
|
@@ -32116,6 +32551,7 @@ function convertDeclareComponentFacadeToMetadata(decl, typeSourceSpan, sourceMap
|
|
|
32116
32551
|
declarationListEmitMode: 2 /* DeclarationListEmitMode.ClosureResolved */,
|
|
32117
32552
|
relativeContextFilePath: '',
|
|
32118
32553
|
i18nUseExternalIds: true,
|
|
32554
|
+
useTemplatePipeline: SHOULD_USE_TEMPLATE_PIPELINE_FOR_JIT,
|
|
32119
32555
|
};
|
|
32120
32556
|
}
|
|
32121
32557
|
function convertDeclarationFacadeToMetadata(declaration) {
|
|
@@ -32397,7 +32833,7 @@ function publishFacade(global) {
|
|
|
32397
32833
|
* @description
|
|
32398
32834
|
* Entry point for all public APIs of the compiler package.
|
|
32399
32835
|
*/
|
|
32400
|
-
const VERSION = new Version('17.2.0-
|
|
32836
|
+
const VERSION = new Version('17.2.0-rc.0');
|
|
32401
32837
|
|
|
32402
32838
|
class CompilerConfig {
|
|
32403
32839
|
constructor({ defaultEncapsulation = ViewEncapsulation.Emulated, preserveWhitespaces, strictInjectionParameters } = {}) {
|
|
@@ -33898,9 +34334,11 @@ function compileComponentClassMetadata(metadata, deferrableTypes) {
|
|
|
33898
34334
|
}
|
|
33899
34335
|
const dynamicImports = [];
|
|
33900
34336
|
const importedSymbols = [];
|
|
33901
|
-
for (const [symbolName, importPath] of deferrableTypes) {
|
|
34337
|
+
for (const [symbolName, { importPath, isDefaultImport }] of deferrableTypes) {
|
|
33902
34338
|
// e.g. `(m) => m.CmpA`
|
|
33903
|
-
const innerFn =
|
|
34339
|
+
const innerFn =
|
|
34340
|
+
// Default imports are always accessed through the `default` property.
|
|
34341
|
+
arrowFn([new FnParam('m', DYNAMIC_TYPE)], variable('m').prop(isDefaultImport ? 'default' : symbolName));
|
|
33904
34342
|
// e.g. `import('./cmp-a').then(...)`
|
|
33905
34343
|
const importExpr = (new DynamicImportExpr(importPath)).prop('then').callFn([innerFn]);
|
|
33906
34344
|
dynamicImports.push(importExpr);
|
|
@@ -33963,7 +34401,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$5 = '12.0.0';
|
|
|
33963
34401
|
function compileDeclareClassMetadata(metadata) {
|
|
33964
34402
|
const definitionMap = new DefinitionMap();
|
|
33965
34403
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$5));
|
|
33966
|
-
definitionMap.set('version', literal('17.2.0-
|
|
34404
|
+
definitionMap.set('version', literal('17.2.0-rc.0'));
|
|
33967
34405
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
33968
34406
|
definitionMap.set('type', metadata.type);
|
|
33969
34407
|
definitionMap.set('decorators', metadata.decorators);
|
|
@@ -34059,7 +34497,7 @@ function createDirectiveDefinitionMap(meta) {
|
|
|
34059
34497
|
const definitionMap = new DefinitionMap();
|
|
34060
34498
|
const minVersion = getMinimumVersionForPartialOutput(meta);
|
|
34061
34499
|
definitionMap.set('minVersion', literal(minVersion));
|
|
34062
|
-
definitionMap.set('version', literal('17.2.0-
|
|
34500
|
+
definitionMap.set('version', literal('17.2.0-rc.0'));
|
|
34063
34501
|
// e.g. `type: MyDirective`
|
|
34064
34502
|
definitionMap.set('type', meta.type.value);
|
|
34065
34503
|
if (meta.isStandalone) {
|
|
@@ -34451,7 +34889,7 @@ const MINIMUM_PARTIAL_LINKER_VERSION$4 = '12.0.0';
|
|
|
34451
34889
|
function compileDeclareFactoryFunction(meta) {
|
|
34452
34890
|
const definitionMap = new DefinitionMap();
|
|
34453
34891
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$4));
|
|
34454
|
-
definitionMap.set('version', literal('17.2.0-
|
|
34892
|
+
definitionMap.set('version', literal('17.2.0-rc.0'));
|
|
34455
34893
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
34456
34894
|
definitionMap.set('type', meta.type.value);
|
|
34457
34895
|
definitionMap.set('deps', compileDependencies(meta.deps));
|
|
@@ -34486,7 +34924,7 @@ function compileDeclareInjectableFromMetadata(meta) {
|
|
|
34486
34924
|
function createInjectableDefinitionMap(meta) {
|
|
34487
34925
|
const definitionMap = new DefinitionMap();
|
|
34488
34926
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$3));
|
|
34489
|
-
definitionMap.set('version', literal('17.2.0-
|
|
34927
|
+
definitionMap.set('version', literal('17.2.0-rc.0'));
|
|
34490
34928
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
34491
34929
|
definitionMap.set('type', meta.type.value);
|
|
34492
34930
|
// Only generate providedIn property if it has a non-null value
|
|
@@ -34537,7 +34975,7 @@ function compileDeclareInjectorFromMetadata(meta) {
|
|
|
34537
34975
|
function createInjectorDefinitionMap(meta) {
|
|
34538
34976
|
const definitionMap = new DefinitionMap();
|
|
34539
34977
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$2));
|
|
34540
|
-
definitionMap.set('version', literal('17.2.0-
|
|
34978
|
+
definitionMap.set('version', literal('17.2.0-rc.0'));
|
|
34541
34979
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
34542
34980
|
definitionMap.set('type', meta.type.value);
|
|
34543
34981
|
definitionMap.set('providers', meta.providers);
|
|
@@ -34570,7 +35008,7 @@ function createNgModuleDefinitionMap(meta) {
|
|
|
34570
35008
|
throw new Error('Invalid path! Local compilation mode should not get into the partial compilation path');
|
|
34571
35009
|
}
|
|
34572
35010
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION$1));
|
|
34573
|
-
definitionMap.set('version', literal('17.2.0-
|
|
35011
|
+
definitionMap.set('version', literal('17.2.0-rc.0'));
|
|
34574
35012
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
34575
35013
|
definitionMap.set('type', meta.type.value);
|
|
34576
35014
|
// We only generate the keys in the metadata if the arrays contain values.
|
|
@@ -34621,7 +35059,7 @@ function compileDeclarePipeFromMetadata(meta) {
|
|
|
34621
35059
|
function createPipeDefinitionMap(meta) {
|
|
34622
35060
|
const definitionMap = new DefinitionMap();
|
|
34623
35061
|
definitionMap.set('minVersion', literal(MINIMUM_PARTIAL_LINKER_VERSION));
|
|
34624
|
-
definitionMap.set('version', literal('17.2.0-
|
|
35062
|
+
definitionMap.set('version', literal('17.2.0-rc.0'));
|
|
34625
35063
|
definitionMap.set('ngImport', importExpr(Identifiers.core));
|
|
34626
35064
|
// e.g. `type: MyPipe`
|
|
34627
35065
|
definitionMap.set('type', meta.type.value);
|
|
@@ -34654,5 +35092,5 @@ publishFacade(_global);
|
|
|
34654
35092
|
|
|
34655
35093
|
// This file is not used to build this module. It is only used during editing
|
|
34656
35094
|
|
|
34657
|
-
export { AST, ASTWithName, ASTWithSource, AbsoluteSourceSpan, ArrayType, ArrowFunctionExpr, AstMemoryEfficientTransformer, AstTransformer, Attribute, Binary, BinaryOperator, BinaryOperatorExpr, BindingPipe, Block, BlockParameter, BoundElementProperty, BuiltinType, BuiltinTypeName, CUSTOM_ELEMENTS_SCHEMA, Call, Chain, ChangeDetectionStrategy, CommaExpr, Comment, CompilerConfig, Conditional, ConditionalExpr, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DYNAMIC_TYPE, DeclareFunctionStmt, DeclareVarStmt, DomElementSchemaRegistry, DynamicImportExpr, EOF, Element, ElementSchemaRegistry, EmitterVisitorContext, EmptyExpr$1 as EmptyExpr, Expansion, ExpansionCase, Expression, ExpressionBinding, ExpressionStatement, ExpressionType, ExternalExpr, ExternalReference, FactoryTarget$1 as FactoryTarget, FunctionExpr, HtmlParser, HtmlTagDefinition, I18NHtmlParser, IfStmt, ImplicitReceiver, InstantiateExpr, Interpolation$1 as Interpolation, InterpolationConfig, InvokeFunctionExpr, JSDocComment, JitEvaluator, KeyedRead, KeyedWrite, LeadingComment, Lexer, LiteralArray, LiteralArrayExpr, LiteralExpr, LiteralMap, LiteralMapExpr, LiteralPrimitive, LocalizedString, MapType, MessageBundle, NONE_TYPE, NO_ERRORS_SCHEMA, NodeWithI18n, NonNullAssert, NotExpr, ParseError, ParseErrorLevel, ParseLocation, ParseSourceFile, ParseSourceSpan, ParseSpan, ParseTreeResult, ParsedEvent, ParsedProperty, ParsedPropertyType, ParsedVariable, Parser$1 as Parser, ParserError, PrefixNot, PropertyRead, PropertyWrite, R3BoundTarget, Identifiers as R3Identifiers, R3NgModuleMetadataKind, R3SelectorScopeMode, R3TargetBinder, R3TemplateDependencyKind, ReadKeyExpr, ReadPropExpr, ReadVarExpr, RecursiveAstVisitor, RecursiveVisitor, ResourceLoader, ReturnStatement, STRING_TYPE, SafeCall, SafeKeyedRead, SafePropertyRead, SelectorContext, SelectorListContext, SelectorMatcher, Serializer, SplitInterpolation, Statement, StmtModifier, TagContentType, TaggedTemplateExpr, TemplateBindingParseResult, TemplateLiteral, TemplateLiteralElement, Text, ThisReceiver, BoundAttribute as TmplAstBoundAttribute, BoundDeferredTrigger as TmplAstBoundDeferredTrigger, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, DeferredBlock as TmplAstDeferredBlock, DeferredBlockError as TmplAstDeferredBlockError, DeferredBlockLoading as TmplAstDeferredBlockLoading, DeferredBlockPlaceholder as TmplAstDeferredBlockPlaceholder, DeferredTrigger as TmplAstDeferredTrigger, Element$1 as TmplAstElement, ForLoopBlock as TmplAstForLoopBlock, ForLoopBlockEmpty as TmplAstForLoopBlockEmpty, HoverDeferredTrigger as TmplAstHoverDeferredTrigger, Icu$1 as TmplAstIcu, IdleDeferredTrigger as TmplAstIdleDeferredTrigger, IfBlock as TmplAstIfBlock, IfBlockBranch as TmplAstIfBlockBranch, ImmediateDeferredTrigger as TmplAstImmediateDeferredTrigger, InteractionDeferredTrigger as TmplAstInteractionDeferredTrigger, RecursiveVisitor$1 as TmplAstRecursiveVisitor, Reference as TmplAstReference, SwitchBlock as TmplAstSwitchBlock, SwitchBlockCase as TmplAstSwitchBlockCase, Template as TmplAstTemplate, Text$3 as TmplAstText, TextAttribute as TmplAstTextAttribute, TimerDeferredTrigger as TmplAstTimerDeferredTrigger, UnknownBlock as TmplAstUnknownBlock, Variable as TmplAstVariable, ViewportDeferredTrigger as TmplAstViewportDeferredTrigger, Token, TokenType, TransplantedType, TreeError, Type, TypeModifier, TypeofExpr, Unary, UnaryOperator, UnaryOperatorExpr, VERSION, VariableBinding, Version, ViewEncapsulation, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, Xliff, Xliff2, Xmb, XmlParser, Xtb,
|
|
35095
|
+
export { AST, ASTWithName, ASTWithSource, AbsoluteSourceSpan, ArrayType, ArrowFunctionExpr, AstMemoryEfficientTransformer, AstTransformer, Attribute, Binary, BinaryOperator, BinaryOperatorExpr, BindingPipe, Block, BlockParameter, BoundElementProperty, BuiltinType, BuiltinTypeName, CUSTOM_ELEMENTS_SCHEMA, Call, Chain, ChangeDetectionStrategy, CommaExpr, Comment, CompilerConfig, Conditional, ConditionalExpr, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DYNAMIC_TYPE, DeclareFunctionStmt, DeclareVarStmt, DomElementSchemaRegistry, DynamicImportExpr, EOF, Element, ElementSchemaRegistry, EmitterVisitorContext, EmptyExpr$1 as EmptyExpr, Expansion, ExpansionCase, Expression, ExpressionBinding, ExpressionStatement, ExpressionType, ExternalExpr, ExternalReference, FactoryTarget$1 as FactoryTarget, FunctionExpr, HtmlParser, HtmlTagDefinition, I18NHtmlParser, IfStmt, ImplicitReceiver, InstantiateExpr, Interpolation$1 as Interpolation, InterpolationConfig, InvokeFunctionExpr, JSDocComment, JitEvaluator, KeyedRead, KeyedWrite, LeadingComment, Lexer, LiteralArray, LiteralArrayExpr, LiteralExpr, LiteralMap, LiteralMapExpr, LiteralPrimitive, LocalizedString, MapType, MessageBundle, NONE_TYPE, NO_ERRORS_SCHEMA, NodeWithI18n, NonNullAssert, NotExpr, ParseError, ParseErrorLevel, ParseLocation, ParseSourceFile, ParseSourceSpan, ParseSpan, ParseTreeResult, ParsedEvent, ParsedProperty, ParsedPropertyType, ParsedVariable, Parser$1 as Parser, ParserError, PrefixNot, PropertyRead, PropertyWrite, R3BoundTarget, Identifiers as R3Identifiers, R3NgModuleMetadataKind, R3SelectorScopeMode, R3TargetBinder, R3TemplateDependencyKind, ReadKeyExpr, ReadPropExpr, ReadVarExpr, RecursiveAstVisitor, RecursiveVisitor, ResourceLoader, ReturnStatement, STRING_TYPE, SafeCall, SafeKeyedRead, SafePropertyRead, SelectorContext, SelectorListContext, SelectorMatcher, Serializer, SplitInterpolation, Statement, StmtModifier, TagContentType, TaggedTemplateExpr, TemplateBindingParseResult, TemplateLiteral, TemplateLiteralElement, Text, ThisReceiver, BoundAttribute as TmplAstBoundAttribute, BoundDeferredTrigger as TmplAstBoundDeferredTrigger, BoundEvent as TmplAstBoundEvent, BoundText as TmplAstBoundText, Content as TmplAstContent, DeferredBlock as TmplAstDeferredBlock, DeferredBlockError as TmplAstDeferredBlockError, DeferredBlockLoading as TmplAstDeferredBlockLoading, DeferredBlockPlaceholder as TmplAstDeferredBlockPlaceholder, DeferredTrigger as TmplAstDeferredTrigger, Element$1 as TmplAstElement, ForLoopBlock as TmplAstForLoopBlock, ForLoopBlockEmpty as TmplAstForLoopBlockEmpty, HoverDeferredTrigger as TmplAstHoverDeferredTrigger, Icu$1 as TmplAstIcu, IdleDeferredTrigger as TmplAstIdleDeferredTrigger, IfBlock as TmplAstIfBlock, IfBlockBranch as TmplAstIfBlockBranch, ImmediateDeferredTrigger as TmplAstImmediateDeferredTrigger, InteractionDeferredTrigger as TmplAstInteractionDeferredTrigger, RecursiveVisitor$1 as TmplAstRecursiveVisitor, Reference as TmplAstReference, SwitchBlock as TmplAstSwitchBlock, SwitchBlockCase as TmplAstSwitchBlockCase, Template as TmplAstTemplate, Text$3 as TmplAstText, TextAttribute as TmplAstTextAttribute, TimerDeferredTrigger as TmplAstTimerDeferredTrigger, UnknownBlock as TmplAstUnknownBlock, Variable as TmplAstVariable, ViewportDeferredTrigger as TmplAstViewportDeferredTrigger, Token, TokenType, TransplantedType, TreeError, Type, TypeModifier, TypeofExpr, Unary, UnaryOperator, UnaryOperatorExpr, VERSION, VariableBinding, Version, ViewEncapsulation, WrappedNodeExpr, WriteKeyExpr, WritePropExpr, WriteVarExpr, Xliff, Xliff2, Xmb, XmlParser, Xtb, compileClassDebugInfo, compileClassMetadata, compileComponentClassMetadata, compileComponentFromMetadata, compileDeclareClassMetadata, compileDeclareComponentFromMetadata, compileDeclareDirectiveFromMetadata, compileDeclareFactoryFunction, compileDeclareInjectableFromMetadata, compileDeclareInjectorFromMetadata, compileDeclareNgModuleFromMetadata, compileDeclarePipeFromMetadata, compileDirectiveFromMetadata, compileFactoryFunction, compileInjectable, compileInjector, compileNgModule, compilePipeFromMetadata, computeMsgId, core, createCssSelectorFromNode, createInjectableType, createMayBeForwardRefExpression, devOnlyGuardedExpression, emitDistinctChangesOnlyDefaultValue, encapsulateStyle, getHtmlTagDefinition, getNsPrefix, getSafePropertyAccessString, identifierName, isIdentifier, isNgContainer, isNgContent, isNgTemplate, jsDocComment, leadingComment, literal, literalMap, makeBindingParser, mergeNsAndName, output_ast as outputAst, parseHostBindings, parseTemplate, preserveWhitespacesDefault, publishFacade, r3JitTypeSourceSpan, sanitizeIdentifier, splitNsName, verifyHostBindings, visitAll };
|
|
34658
35096
|
//# sourceMappingURL=compiler.mjs.map
|