@lwc/template-compiler 8.22.1 → 8.22.4
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/dist/codegen/expression.d.ts +9 -2
- package/dist/index.cjs.js +169 -144
- package/dist/index.d.ts +1 -0
- package/dist/index.js +170 -146
- package/dist/parser/expression.d.ts +1 -1
- package/package.json +3 -3
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import * as t from '../shared/estree';
|
|
2
|
-
import type { Attribute, BaseElement, ComplexExpression, Property } from '../shared/types';
|
|
2
|
+
import type { Attribute, BaseElement, Expression, ComplexExpression, Property } from '../shared/types';
|
|
3
3
|
import type CodeGen from './codegen';
|
|
4
|
+
/**
|
|
5
|
+
* Bind the passed expression to the component instance. It applies the following transformation to the expression:
|
|
6
|
+
* - {value} --> {$cmp.value}
|
|
7
|
+
* - {value[index]} --> {$cmp.value[$cmp.index]}
|
|
8
|
+
* @param expression
|
|
9
|
+
*/
|
|
10
|
+
export declare function bindExpression(expression: Expression | t.Literal | ComplexExpression, isLocalIdentifier: (node: t.Identifier) => boolean, templateInstanceName: string, experimentalComplexExpressions: boolean): t.Expression;
|
|
4
11
|
/**
|
|
5
12
|
* Bind the passed expression to the component instance. It applies the following
|
|
6
13
|
* transformation to the expression:
|
|
@@ -22,6 +29,6 @@ import type CodeGen from './codegen';
|
|
|
22
29
|
* @param expression
|
|
23
30
|
* @param codeGen
|
|
24
31
|
*/
|
|
25
|
-
export declare function bindComplexExpression(expression: ComplexExpression,
|
|
32
|
+
export declare function bindComplexExpression(expression: ComplexExpression, isLocalIdentifier: (node: t.Identifier) => boolean, templateInstanceName: string): t.Expression;
|
|
26
33
|
export declare function bindAttributeExpression(attr: Attribute | Property, element: BaseElement, codeGen: CodeGen, addLegacySanitizationHook: boolean): import("estree").Identifier | import("estree").MemberExpression | import("estree").ArrayExpression | import("estree").ObjectExpression | import("estree").ArrowFunctionExpression | import("estree").UnaryExpression | import("estree").SimpleLiteral | import("estree").RegExpLiteral | import("estree").BigIntLiteral | import("estree").AssignmentExpression | import("estree").AwaitExpression | import("estree").BinaryExpression | import("estree").SimpleCallExpression | import("estree").NewExpression | import("estree").ChainExpression | import("estree").ClassExpression | import("estree").ConditionalExpression | import("estree").FunctionExpression | import("estree").ImportExpression | import("estree").LogicalExpression | import("estree").MetaProperty | import("estree").SequenceExpression | import("estree").TaggedTemplateExpression | import("estree").TemplateLiteral | import("estree").ThisExpression | import("estree").UpdateExpression | import("estree").YieldExpression;
|
|
27
34
|
//# sourceMappingURL=expression.d.ts.map
|
package/dist/index.cjs.js
CHANGED
|
@@ -10087,104 +10087,6 @@ function isReservedES6Keyword(str) {
|
|
|
10087
10087
|
return REVERSED_KEYWORDS.has(str);
|
|
10088
10088
|
}
|
|
10089
10089
|
|
|
10090
|
-
/*
|
|
10091
|
-
* Copyright (c) 2018, salesforce.com, inc.
|
|
10092
|
-
* All rights reserved.
|
|
10093
|
-
* SPDX-License-Identifier: MIT
|
|
10094
|
-
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
10095
|
-
*/
|
|
10096
|
-
const EXPRESSION_SYMBOL_START = '{';
|
|
10097
|
-
const EXPRESSION_SYMBOL_END = '}';
|
|
10098
|
-
const POTENTIAL_EXPRESSION_RE = /^.?{.+}.*$/;
|
|
10099
|
-
const WHITESPACES_RE = /\s/;
|
|
10100
|
-
function isExpression(source) {
|
|
10101
|
-
// Issue #3418: Legacy behavior, previous regex treated "{}" attribute value as non expression
|
|
10102
|
-
return source[0] === '{' && source.slice(-1) === '}' && source.length > 2;
|
|
10103
|
-
}
|
|
10104
|
-
function isPotentialExpression(source) {
|
|
10105
|
-
return !!source.match(POTENTIAL_EXPRESSION_RE);
|
|
10106
|
-
}
|
|
10107
|
-
function validateExpression(source, node, config) {
|
|
10108
|
-
const isValidNode = isIdentifier(node) || isMemberExpression(node);
|
|
10109
|
-
// INVALID_XYZ_COMPLEX provides additional context to the user if CTE is enabled.
|
|
10110
|
-
// The author may not have delimited the CTE with quotes, resulting in it being parsed
|
|
10111
|
-
// as a legacy expression.
|
|
10112
|
-
errors.invariant(isValidNode, config.experimentalComplexExpressions
|
|
10113
|
-
? errors.ParserDiagnostics.INVALID_NODE_COMPLEX
|
|
10114
|
-
: errors.ParserDiagnostics.INVALID_NODE, [node.type, source]);
|
|
10115
|
-
if (isMemberExpression(node)) {
|
|
10116
|
-
errors.invariant(config.experimentalComputedMemberExpression || !node.computed, config.experimentalComplexExpressions
|
|
10117
|
-
? errors.ParserDiagnostics.COMPUTED_PROPERTY_ACCESS_NOT_ALLOWED_COMPLEX
|
|
10118
|
-
: errors.ParserDiagnostics.COMPUTED_PROPERTY_ACCESS_NOT_ALLOWED, [source]);
|
|
10119
|
-
const { object, property } = node;
|
|
10120
|
-
if (!isIdentifier(object)) {
|
|
10121
|
-
validateExpression(source, object, config);
|
|
10122
|
-
}
|
|
10123
|
-
if (!isIdentifier(property)) {
|
|
10124
|
-
validateExpression(source, property, config);
|
|
10125
|
-
}
|
|
10126
|
-
}
|
|
10127
|
-
}
|
|
10128
|
-
function validateSourceIsParsedExpression(source, parsedExpression) {
|
|
10129
|
-
if (parsedExpression.end === source.length - 1) {
|
|
10130
|
-
return;
|
|
10131
|
-
}
|
|
10132
|
-
let unclosedParenthesisCount = 0;
|
|
10133
|
-
for (let i = 0, n = parsedExpression.start; i < n; i++) {
|
|
10134
|
-
if (source[i] === '(') {
|
|
10135
|
-
unclosedParenthesisCount++;
|
|
10136
|
-
}
|
|
10137
|
-
}
|
|
10138
|
-
// source[source.length - 1] === '}', n = source.length - 1 is to avoid processing '}'.
|
|
10139
|
-
for (let i = parsedExpression.end, n = source.length - 1; i < n; i++) {
|
|
10140
|
-
const character = source[i];
|
|
10141
|
-
if (character === ')') {
|
|
10142
|
-
unclosedParenthesisCount--;
|
|
10143
|
-
}
|
|
10144
|
-
else if (character === ';') {
|
|
10145
|
-
// acorn parseExpressionAt will stop at the first ";", it may be that the expression is not
|
|
10146
|
-
// a multiple expression ({foo;}), but this is a case that we explicitly do not want to support.
|
|
10147
|
-
// in such case, let's fail with the same error as if it were a multiple expression.
|
|
10148
|
-
errors.invariant(false, errors.ParserDiagnostics.MULTIPLE_EXPRESSIONS);
|
|
10149
|
-
}
|
|
10150
|
-
else {
|
|
10151
|
-
errors.invariant(WHITESPACES_RE.test(character), errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, ['Unexpected end of expression']);
|
|
10152
|
-
}
|
|
10153
|
-
}
|
|
10154
|
-
errors.invariant(unclosedParenthesisCount === 0, errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, [
|
|
10155
|
-
'Unexpected end of expression',
|
|
10156
|
-
]);
|
|
10157
|
-
}
|
|
10158
|
-
function parseExpression(ctx, source, location) {
|
|
10159
|
-
const { ecmaVersion } = ctx;
|
|
10160
|
-
return ctx.withErrorWrapping(() => {
|
|
10161
|
-
const parsed = acorn.parseExpressionAt(source, 1, {
|
|
10162
|
-
ecmaVersion,
|
|
10163
|
-
allowAwaitOutsideFunction: false,
|
|
10164
|
-
onComment: () => errors.invariant(false, errors.ParserDiagnostics.INVALID_EXPR_COMMENTS_DISALLOWED),
|
|
10165
|
-
});
|
|
10166
|
-
validateSourceIsParsedExpression(source, parsed);
|
|
10167
|
-
validateExpression(source, parsed, ctx.config);
|
|
10168
|
-
return { ...parsed, location };
|
|
10169
|
-
}, errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, location, (err) => `Invalid expression ${source} - ${err.message}`);
|
|
10170
|
-
}
|
|
10171
|
-
function parseIdentifier(ctx, source, location) {
|
|
10172
|
-
let isValid = true;
|
|
10173
|
-
isValid = acorn.isIdentifierStart(source.charCodeAt(0));
|
|
10174
|
-
for (let i = 1; i < source.length && isValid; i++) {
|
|
10175
|
-
isValid = acorn.isIdentifierChar(source.charCodeAt(i));
|
|
10176
|
-
}
|
|
10177
|
-
if (isValid && !isReservedES6Keyword(source)) {
|
|
10178
|
-
return {
|
|
10179
|
-
...identifier(source),
|
|
10180
|
-
location,
|
|
10181
|
-
};
|
|
10182
|
-
}
|
|
10183
|
-
else {
|
|
10184
|
-
ctx.throwAtLocation(errors.ParserDiagnostics.INVALID_IDENTIFIER, location, [source]);
|
|
10185
|
-
}
|
|
10186
|
-
}
|
|
10187
|
-
|
|
10188
10090
|
/**
|
|
10189
10091
|
* @typedef { import('estree').Node} Node
|
|
10190
10092
|
* @typedef {{
|
|
@@ -10616,6 +10518,120 @@ function parseComplexExpression(ctx, source, templateSource, location, expressio
|
|
|
10616
10518
|
}, errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, location, (err) => `Invalid expression ${source} - ${err.message}`);
|
|
10617
10519
|
}
|
|
10618
10520
|
|
|
10521
|
+
/*
|
|
10522
|
+
* Copyright (c) 2018, salesforce.com, inc.
|
|
10523
|
+
* All rights reserved.
|
|
10524
|
+
* SPDX-License-Identifier: MIT
|
|
10525
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
10526
|
+
*/
|
|
10527
|
+
const EXPRESSION_SYMBOL_START = '{';
|
|
10528
|
+
const EXPRESSION_SYMBOL_END = '}';
|
|
10529
|
+
const POTENTIAL_EXPRESSION_RE = /^.?{.+}.*$/;
|
|
10530
|
+
const WHITESPACES_RE = /\s/;
|
|
10531
|
+
function isExpression(source) {
|
|
10532
|
+
// Issue #3418: Legacy behavior, previous regex treated "{}" attribute value as non expression
|
|
10533
|
+
return source[0] === '{' && source.slice(-1) === '}' && source.length > 2;
|
|
10534
|
+
}
|
|
10535
|
+
function isPotentialExpression(source) {
|
|
10536
|
+
return !!source.match(POTENTIAL_EXPRESSION_RE);
|
|
10537
|
+
}
|
|
10538
|
+
const minCteApiVersion = shared.minApiVersion(11 /* APIFeature.ENABLE_COMPLEX_TEMPLATE_EXPRESSIONS */);
|
|
10539
|
+
function validateExpression(source, node, ctx, unquotedAttributeExpression) {
|
|
10540
|
+
const cteOnlyNode = !isIdentifier(node) && !isMemberExpression(node);
|
|
10541
|
+
// If this node is not an identifier or a member expression (the only two nodes allowed if complexTemplateExpressions are disabled),
|
|
10542
|
+
// then we throw if the following invariants do not hold true.
|
|
10543
|
+
if (cteOnlyNode) {
|
|
10544
|
+
// complexTemplateExpressions must be enabled if this is a cteOnlyNode.
|
|
10545
|
+
errors.invariant(ctx.config.experimentalComplexExpressions, errors.ParserDiagnostics.INVALID_NODE, [
|
|
10546
|
+
node.type,
|
|
10547
|
+
]);
|
|
10548
|
+
// complexTemplateExpressions must be enabled and the component API version must be sufficient.
|
|
10549
|
+
errors.invariant(isComplexTemplateExpressionEnabled(ctx), errors.ParserDiagnostics.INVALID_NODE_CTE_API_VERSION, [node.type, ctx.apiVersion, minCteApiVersion]);
|
|
10550
|
+
// complexTemplateExpressions must be enabled, the component API version must be sufficient and the expression should not be
|
|
10551
|
+
// an unquoted attribute expression.
|
|
10552
|
+
errors.invariant(isComplexTemplateExpressionEnabled(ctx) && !unquotedAttributeExpression, errors.ParserDiagnostics.INVALID_NODE_CTE_UNQUOTED, [node.type, source]);
|
|
10553
|
+
}
|
|
10554
|
+
if (isMemberExpression(node)) {
|
|
10555
|
+
// If this is a computed node and experimentalComputedMemberExpressions is not enabled,
|
|
10556
|
+
// then we throw if the following invariants do not hold true.
|
|
10557
|
+
if (!ctx.config.experimentalComputedMemberExpression && node.computed) {
|
|
10558
|
+
// complexTemplateExpressions must be enabled.
|
|
10559
|
+
errors.invariant(ctx.config.experimentalComplexExpressions, errors.ParserDiagnostics.COMPUTED_PROPERTY_ACCESS_NOT_ALLOWED, [source]);
|
|
10560
|
+
// complexTemplateExpressions must be enabled and the component API version must be sufficient.
|
|
10561
|
+
errors.invariant(isComplexTemplateExpressionEnabled(ctx), errors.ParserDiagnostics.COMPUTED_PROPERTY_ACCESS_NOT_ALLOWED_CTE_API_VERSION, [source, ctx.apiVersion, minCteApiVersion]);
|
|
10562
|
+
// complexTemplateExpressions must be enabled, the component API version must be sufficient and the expression
|
|
10563
|
+
// should not be an unquoted attribute expression.
|
|
10564
|
+
errors.invariant(isComplexTemplateExpressionEnabled(ctx) && !unquotedAttributeExpression, errors.ParserDiagnostics.COMPUTED_PROPERTY_ACCESS_NOT_ALLOWED_CTE_UNQUOTED, [source]);
|
|
10565
|
+
}
|
|
10566
|
+
const { object, property } = node;
|
|
10567
|
+
if (!isIdentifier(object)) {
|
|
10568
|
+
validateExpression(source, object, ctx, unquotedAttributeExpression);
|
|
10569
|
+
}
|
|
10570
|
+
if (!isIdentifier(property)) {
|
|
10571
|
+
validateExpression(source, property, ctx, unquotedAttributeExpression);
|
|
10572
|
+
}
|
|
10573
|
+
}
|
|
10574
|
+
}
|
|
10575
|
+
function validateSourceIsParsedExpression(source, parsedExpression) {
|
|
10576
|
+
if (parsedExpression.end === source.length - 1) {
|
|
10577
|
+
return;
|
|
10578
|
+
}
|
|
10579
|
+
let unclosedParenthesisCount = 0;
|
|
10580
|
+
for (let i = 0, n = parsedExpression.start; i < n; i++) {
|
|
10581
|
+
if (source[i] === '(') {
|
|
10582
|
+
unclosedParenthesisCount++;
|
|
10583
|
+
}
|
|
10584
|
+
}
|
|
10585
|
+
// source[source.length - 1] === '}', n = source.length - 1 is to avoid processing '}'.
|
|
10586
|
+
for (let i = parsedExpression.end, n = source.length - 1; i < n; i++) {
|
|
10587
|
+
const character = source[i];
|
|
10588
|
+
if (character === ')') {
|
|
10589
|
+
unclosedParenthesisCount--;
|
|
10590
|
+
}
|
|
10591
|
+
else if (character === ';') {
|
|
10592
|
+
// acorn parseExpressionAt will stop at the first ";", it may be that the expression is not
|
|
10593
|
+
// a multiple expression ({foo;}), but this is a case that we explicitly do not want to support.
|
|
10594
|
+
// in such case, let's fail with the same error as if it were a multiple expression.
|
|
10595
|
+
errors.invariant(false, errors.ParserDiagnostics.MULTIPLE_EXPRESSIONS);
|
|
10596
|
+
}
|
|
10597
|
+
else {
|
|
10598
|
+
errors.invariant(WHITESPACES_RE.test(character), errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, ['Unexpected end of expression']);
|
|
10599
|
+
}
|
|
10600
|
+
}
|
|
10601
|
+
errors.invariant(unclosedParenthesisCount === 0, errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, [
|
|
10602
|
+
'Unexpected end of expression',
|
|
10603
|
+
]);
|
|
10604
|
+
}
|
|
10605
|
+
function parseExpression(ctx, source, location, unquotedAttributeExpression) {
|
|
10606
|
+
const { ecmaVersion } = ctx;
|
|
10607
|
+
return ctx.withErrorWrapping(() => {
|
|
10608
|
+
const parsed = acorn.parseExpressionAt(source, 1, {
|
|
10609
|
+
ecmaVersion,
|
|
10610
|
+
allowAwaitOutsideFunction: false,
|
|
10611
|
+
onComment: () => errors.invariant(false, errors.ParserDiagnostics.INVALID_EXPR_COMMENTS_DISALLOWED),
|
|
10612
|
+
});
|
|
10613
|
+
validateSourceIsParsedExpression(source, parsed);
|
|
10614
|
+
validateExpression(source, parsed, ctx, unquotedAttributeExpression);
|
|
10615
|
+
return { ...parsed, location };
|
|
10616
|
+
}, errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, location, (err) => `Invalid expression ${source} - ${err.message}`);
|
|
10617
|
+
}
|
|
10618
|
+
function parseIdentifier(ctx, source, location) {
|
|
10619
|
+
let isValid = true;
|
|
10620
|
+
isValid = acorn.isIdentifierStart(source.charCodeAt(0));
|
|
10621
|
+
for (let i = 1; i < source.length && isValid; i++) {
|
|
10622
|
+
isValid = acorn.isIdentifierChar(source.charCodeAt(i));
|
|
10623
|
+
}
|
|
10624
|
+
if (isValid && !isReservedES6Keyword(source)) {
|
|
10625
|
+
return {
|
|
10626
|
+
...identifier(source),
|
|
10627
|
+
location,
|
|
10628
|
+
};
|
|
10629
|
+
}
|
|
10630
|
+
else {
|
|
10631
|
+
ctx.throwAtLocation(errors.ParserDiagnostics.INVALID_IDENTIFIER, location, [source]);
|
|
10632
|
+
}
|
|
10633
|
+
}
|
|
10634
|
+
|
|
10619
10635
|
/*
|
|
10620
10636
|
* Copyright (c) 2018, salesforce.com, inc.
|
|
10621
10637
|
* All rights reserved.
|
|
@@ -11081,7 +11097,9 @@ function normalizeAttributeValue(ctx, raw, tag, attr, location) {
|
|
|
11081
11097
|
const isQuoted = isQuotedAttribute(rawAttrVal);
|
|
11082
11098
|
const isEscaped = isEscapedAttribute(rawAttrVal);
|
|
11083
11099
|
if (!isEscaped && isExpression(value)) {
|
|
11084
|
-
if
|
|
11100
|
+
// Don't test for the API version here, just check if CTE is enabled.
|
|
11101
|
+
// We can provide more specific errors w.r.t API versions after the expression has been parsed and we know what it is.
|
|
11102
|
+
if (isQuoted && !ctx.config.experimentalComplexExpressions) {
|
|
11085
11103
|
// <input value="{myValue}" />
|
|
11086
11104
|
// -> ambiguity if the attribute value is a template identifier or a string literal.
|
|
11087
11105
|
const unquoted = raw.replace(/"/g, '');
|
|
@@ -11527,7 +11545,7 @@ function parseText(ctx, rawText, sourceLocation, location) {
|
|
|
11527
11545
|
}
|
|
11528
11546
|
let value;
|
|
11529
11547
|
if (isExpression(token)) {
|
|
11530
|
-
value = parseExpression(ctx, token, sourceLocation);
|
|
11548
|
+
value = parseExpression(ctx, token, sourceLocation, false);
|
|
11531
11549
|
}
|
|
11532
11550
|
else {
|
|
11533
11551
|
value = literal(decodeTextContent(token));
|
|
@@ -11579,7 +11597,7 @@ function parseTextNode(ctx, parse5Text) {
|
|
|
11579
11597
|
return [];
|
|
11580
11598
|
}
|
|
11581
11599
|
const sourceLocation$1 = sourceLocation(location);
|
|
11582
|
-
return ctx
|
|
11600
|
+
return isComplexTemplateExpressionEnabled(ctx)
|
|
11583
11601
|
? parseTextComplex(ctx, rawText, sourceLocation$1, location)
|
|
11584
11602
|
: parseText(ctx, rawText, sourceLocation$1, location);
|
|
11585
11603
|
}
|
|
@@ -12462,13 +12480,13 @@ function getTemplateAttribute(ctx, tag, attribute$1, attributeLocation) {
|
|
|
12462
12480
|
to be used.
|
|
12463
12481
|
*/
|
|
12464
12482
|
const isPotentialComplexExpression = quotedExpression && !escapedExpression && value.startsWith(EXPRESSION_SYMBOL_START);
|
|
12465
|
-
if (ctx
|
|
12483
|
+
if (isComplexTemplateExpressionEnabled(ctx) && isPotentialComplexExpression) {
|
|
12466
12484
|
const attributeNameOffset = attribute$1.name.length + 2; // The +2 accounts for the '="' in the attribute: attr="...
|
|
12467
12485
|
const templateSource = ctx.getSource(attributeLocation.startOffset + attributeNameOffset);
|
|
12468
12486
|
attrValue = parseComplexExpression(ctx, value, templateSource, location).expression;
|
|
12469
12487
|
}
|
|
12470
12488
|
else if (isExpression(value) && !escapedExpression) {
|
|
12471
|
-
attrValue = parseExpression(ctx, value, location);
|
|
12489
|
+
attrValue = parseExpression(ctx, value, location, !quotedExpression);
|
|
12472
12490
|
}
|
|
12473
12491
|
else if (isBooleanAttribute) {
|
|
12474
12492
|
attrValue = literal(true);
|
|
@@ -13067,6 +13085,48 @@ function serializeStaticElement(element, codeGen) {
|
|
|
13067
13085
|
* SPDX-License-Identifier: MIT
|
|
13068
13086
|
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
13069
13087
|
*/
|
|
13088
|
+
/**
|
|
13089
|
+
* Bind the passed expression to the component instance. It applies the following transformation to the expression:
|
|
13090
|
+
* - {value} --> {$cmp.value}
|
|
13091
|
+
* - {value[index]} --> {$cmp.value[$cmp.index]}
|
|
13092
|
+
* @param expression
|
|
13093
|
+
*/
|
|
13094
|
+
function bindExpression(expression, isLocalIdentifier, templateInstanceName, experimentalComplexExpressions) {
|
|
13095
|
+
if (isIdentifier(expression)) {
|
|
13096
|
+
if (!isLocalIdentifier(expression)) {
|
|
13097
|
+
return memberExpression(identifier(templateInstanceName), expression);
|
|
13098
|
+
}
|
|
13099
|
+
else {
|
|
13100
|
+
return expression;
|
|
13101
|
+
}
|
|
13102
|
+
}
|
|
13103
|
+
// TODO [#3370]: remove experimental template expression flag
|
|
13104
|
+
if (experimentalComplexExpressions) {
|
|
13105
|
+
// Cloning here is necessary because `this.replace()` is destructive, and we might use the
|
|
13106
|
+
// node later during static content optimization
|
|
13107
|
+
expression = structuredClone(expression);
|
|
13108
|
+
return bindComplexExpression(expression, isLocalIdentifier, templateInstanceName);
|
|
13109
|
+
}
|
|
13110
|
+
// Cloning here is necessary because `this.replace()` is destructive, and we might use the
|
|
13111
|
+
// node later during static content optimization
|
|
13112
|
+
expression = structuredClone(expression);
|
|
13113
|
+
// TODO [#3370]: when the template expression flag is removed, the
|
|
13114
|
+
// ComplexExpression type should be redefined as an ESTree Node. Doing
|
|
13115
|
+
// so when the flag is still in place results in a cascade of required
|
|
13116
|
+
// type changes across the codebase.
|
|
13117
|
+
walk(expression, {
|
|
13118
|
+
leave(node, parent) {
|
|
13119
|
+
if (parent !== null &&
|
|
13120
|
+
isIdentifier(node) &&
|
|
13121
|
+
isMemberExpression(parent) &&
|
|
13122
|
+
parent.object === node &&
|
|
13123
|
+
!isLocalIdentifier(node)) {
|
|
13124
|
+
this.replace(memberExpression(identifier(templateInstanceName), node));
|
|
13125
|
+
}
|
|
13126
|
+
},
|
|
13127
|
+
});
|
|
13128
|
+
return expression;
|
|
13129
|
+
}
|
|
13070
13130
|
/**
|
|
13071
13131
|
* Bind the passed expression to the component instance. It applies the following
|
|
13072
13132
|
* transformation to the expression:
|
|
@@ -13088,7 +13148,7 @@ function serializeStaticElement(element, codeGen) {
|
|
|
13088
13148
|
* @param expression
|
|
13089
13149
|
* @param codeGen
|
|
13090
13150
|
*/
|
|
13091
|
-
function bindComplexExpression(expression,
|
|
13151
|
+
function bindComplexExpression(expression, isLocalIdentifier, templateInstanceName) {
|
|
13092
13152
|
const expressionScopes = new ExpressionScopes();
|
|
13093
13153
|
// TODO [#3370]: when the template expression flag is removed, the
|
|
13094
13154
|
// ComplexExpression type should be redefined as an ESTree Node. Doing
|
|
@@ -13112,9 +13172,9 @@ function bindComplexExpression(expression, codeGen) {
|
|
|
13112
13172
|
isIdentifier$1 &&
|
|
13113
13173
|
!(isMemberExpression(parent) && parent.property === node && !parent.computed) &&
|
|
13114
13174
|
!(isProperty$1(parent) && parent.key === node) &&
|
|
13115
|
-
!
|
|
13175
|
+
!isLocalIdentifier(node) &&
|
|
13116
13176
|
!expressionScopes.isScopedToExpression(node)) {
|
|
13117
|
-
this.replace(memberExpression(identifier(
|
|
13177
|
+
this.replace(memberExpression(identifier(templateInstanceName), node));
|
|
13118
13178
|
}
|
|
13119
13179
|
},
|
|
13120
13180
|
});
|
|
@@ -13595,43 +13655,7 @@ class CodeGen {
|
|
|
13595
13655
|
* @param expression
|
|
13596
13656
|
*/
|
|
13597
13657
|
bindExpression(expression) {
|
|
13598
|
-
|
|
13599
|
-
if (!this.isLocalIdentifier(expression)) {
|
|
13600
|
-
return memberExpression(identifier(TEMPLATE_PARAMS.INSTANCE), expression);
|
|
13601
|
-
}
|
|
13602
|
-
else {
|
|
13603
|
-
return expression;
|
|
13604
|
-
}
|
|
13605
|
-
}
|
|
13606
|
-
// TODO [#3370]: remove experimental template expression flag
|
|
13607
|
-
if (this.state.config.experimentalComplexExpressions) {
|
|
13608
|
-
// Cloning here is necessary because `this.replace()` is destructive, and we might use the
|
|
13609
|
-
// node later during static content optimization
|
|
13610
|
-
expression = structuredClone(expression);
|
|
13611
|
-
return bindComplexExpression(expression, this);
|
|
13612
|
-
}
|
|
13613
|
-
// We need access to both this `this` and the walker's `this` in the walker
|
|
13614
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
13615
|
-
const scope = this;
|
|
13616
|
-
// Cloning here is necessary because `this.replace()` is destructive, and we might use the
|
|
13617
|
-
// node later during static content optimization
|
|
13618
|
-
expression = structuredClone(expression);
|
|
13619
|
-
// TODO [#3370]: when the template expression flag is removed, the
|
|
13620
|
-
// ComplexExpression type should be redefined as an ESTree Node. Doing
|
|
13621
|
-
// so when the flag is still in place results in a cascade of required
|
|
13622
|
-
// type changes across the codebase.
|
|
13623
|
-
walk(expression, {
|
|
13624
|
-
leave(node, parent) {
|
|
13625
|
-
if (parent !== null &&
|
|
13626
|
-
isIdentifier(node) &&
|
|
13627
|
-
isMemberExpression(parent) &&
|
|
13628
|
-
parent.object === node &&
|
|
13629
|
-
!scope.isLocalIdentifier(node)) {
|
|
13630
|
-
this.replace(memberExpression(identifier(TEMPLATE_PARAMS.INSTANCE), node));
|
|
13631
|
-
}
|
|
13632
|
-
},
|
|
13633
|
-
});
|
|
13634
|
-
return expression;
|
|
13658
|
+
return bindExpression(expression, this.isLocalIdentifier.bind(this), TEMPLATE_PARAMS.INSTANCE, this.state.config.experimentalComplexExpressions);
|
|
13635
13659
|
}
|
|
13636
13660
|
genStaticElement(element, slotParentName) {
|
|
13637
13661
|
const staticParts = this.genStaticParts(element);
|
|
@@ -14543,11 +14567,12 @@ function compile(source, filename, config) {
|
|
|
14543
14567
|
};
|
|
14544
14568
|
}
|
|
14545
14569
|
|
|
14570
|
+
exports.bindExpression = bindExpression;
|
|
14546
14571
|
exports.compile = compile;
|
|
14547
14572
|
exports.default = compile;
|
|
14548
14573
|
exports.generateScopeTokens = generateScopeTokens;
|
|
14549
14574
|
exports.kebabcaseToCamelcase = kebabcaseToCamelcase;
|
|
14550
14575
|
exports.parse = parse;
|
|
14551
14576
|
exports.toPropertyName = toPropertyName;
|
|
14552
|
-
/** version: 8.22.
|
|
14577
|
+
/** version: 8.22.4 */
|
|
14553
14578
|
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export { Config } from './config';
|
|
|
6
6
|
export { toPropertyName } from './shared/utils';
|
|
7
7
|
export { kebabcaseToCamelcase } from './shared/naming';
|
|
8
8
|
export { generateScopeTokens } from './scopeTokens';
|
|
9
|
+
export { bindExpression } from './codegen/expression';
|
|
9
10
|
/**
|
|
10
11
|
* Parses HTML markup into an AST
|
|
11
12
|
* @param source HTML markup to parse
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { invariant, TemplateErrors, generateCompilerError, CompilerError, normalizeToDiagnostic, generateCompilerDiagnostic, ParserDiagnostics, DiagnosticLevel, CompilerMetrics } from '@lwc/errors';
|
|
5
5
|
import path from 'path';
|
|
6
|
-
import { StringCharAt, hasOwnProperty, getAPIVersionFromNumber, HTML_NAMESPACE, AriaAttrNameToPropNameMap, fromEntries, ArrayFrom, SPECIAL_PROPERTY_ATTRIBUTE_MAPPING, isAPIFeatureEnabled, ID_REFERENCING_ATTRIBUTES_SET, SVG_NAMESPACE, isBooleanAttribute, isGlobalHtmlAttribute, isAriaAttribute, isVoidElement, MATHML_NAMESPACE, isNull, isUndefined, IMPORTANT_FLAG, isArray, ArrayEvery, ArraySome, normalizeStyleAttributeValue, htmlEscape, parseStyleText, LWC_VERSION_COMMENT } from '@lwc/shared';
|
|
6
|
+
import { StringCharAt, hasOwnProperty, getAPIVersionFromNumber, HTML_NAMESPACE, AriaAttrNameToPropNameMap, fromEntries, ArrayFrom, SPECIAL_PROPERTY_ATTRIBUTE_MAPPING, isAPIFeatureEnabled, minApiVersion, ID_REFERENCING_ATTRIBUTES_SET, SVG_NAMESPACE, isBooleanAttribute, isGlobalHtmlAttribute, isAriaAttribute, isVoidElement, MATHML_NAMESPACE, isNull, isUndefined, IMPORTANT_FLAG, isArray, ArrayEvery, ArraySome, normalizeStyleAttributeValue, htmlEscape, parseStyleText, LWC_VERSION_COMMENT } from '@lwc/shared';
|
|
7
7
|
import * as he from 'he';
|
|
8
8
|
import { parseExpressionAt, isIdentifierStart, isIdentifierChar } from 'acorn';
|
|
9
9
|
import * as astring from 'astring';
|
|
@@ -10063,104 +10063,6 @@ function isReservedES6Keyword(str) {
|
|
|
10063
10063
|
return REVERSED_KEYWORDS.has(str);
|
|
10064
10064
|
}
|
|
10065
10065
|
|
|
10066
|
-
/*
|
|
10067
|
-
* Copyright (c) 2018, salesforce.com, inc.
|
|
10068
|
-
* All rights reserved.
|
|
10069
|
-
* SPDX-License-Identifier: MIT
|
|
10070
|
-
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
10071
|
-
*/
|
|
10072
|
-
const EXPRESSION_SYMBOL_START = '{';
|
|
10073
|
-
const EXPRESSION_SYMBOL_END = '}';
|
|
10074
|
-
const POTENTIAL_EXPRESSION_RE = /^.?{.+}.*$/;
|
|
10075
|
-
const WHITESPACES_RE = /\s/;
|
|
10076
|
-
function isExpression(source) {
|
|
10077
|
-
// Issue #3418: Legacy behavior, previous regex treated "{}" attribute value as non expression
|
|
10078
|
-
return source[0] === '{' && source.slice(-1) === '}' && source.length > 2;
|
|
10079
|
-
}
|
|
10080
|
-
function isPotentialExpression(source) {
|
|
10081
|
-
return !!source.match(POTENTIAL_EXPRESSION_RE);
|
|
10082
|
-
}
|
|
10083
|
-
function validateExpression(source, node, config) {
|
|
10084
|
-
const isValidNode = isIdentifier(node) || isMemberExpression(node);
|
|
10085
|
-
// INVALID_XYZ_COMPLEX provides additional context to the user if CTE is enabled.
|
|
10086
|
-
// The author may not have delimited the CTE with quotes, resulting in it being parsed
|
|
10087
|
-
// as a legacy expression.
|
|
10088
|
-
invariant(isValidNode, config.experimentalComplexExpressions
|
|
10089
|
-
? ParserDiagnostics.INVALID_NODE_COMPLEX
|
|
10090
|
-
: ParserDiagnostics.INVALID_NODE, [node.type, source]);
|
|
10091
|
-
if (isMemberExpression(node)) {
|
|
10092
|
-
invariant(config.experimentalComputedMemberExpression || !node.computed, config.experimentalComplexExpressions
|
|
10093
|
-
? ParserDiagnostics.COMPUTED_PROPERTY_ACCESS_NOT_ALLOWED_COMPLEX
|
|
10094
|
-
: ParserDiagnostics.COMPUTED_PROPERTY_ACCESS_NOT_ALLOWED, [source]);
|
|
10095
|
-
const { object, property } = node;
|
|
10096
|
-
if (!isIdentifier(object)) {
|
|
10097
|
-
validateExpression(source, object, config);
|
|
10098
|
-
}
|
|
10099
|
-
if (!isIdentifier(property)) {
|
|
10100
|
-
validateExpression(source, property, config);
|
|
10101
|
-
}
|
|
10102
|
-
}
|
|
10103
|
-
}
|
|
10104
|
-
function validateSourceIsParsedExpression(source, parsedExpression) {
|
|
10105
|
-
if (parsedExpression.end === source.length - 1) {
|
|
10106
|
-
return;
|
|
10107
|
-
}
|
|
10108
|
-
let unclosedParenthesisCount = 0;
|
|
10109
|
-
for (let i = 0, n = parsedExpression.start; i < n; i++) {
|
|
10110
|
-
if (source[i] === '(') {
|
|
10111
|
-
unclosedParenthesisCount++;
|
|
10112
|
-
}
|
|
10113
|
-
}
|
|
10114
|
-
// source[source.length - 1] === '}', n = source.length - 1 is to avoid processing '}'.
|
|
10115
|
-
for (let i = parsedExpression.end, n = source.length - 1; i < n; i++) {
|
|
10116
|
-
const character = source[i];
|
|
10117
|
-
if (character === ')') {
|
|
10118
|
-
unclosedParenthesisCount--;
|
|
10119
|
-
}
|
|
10120
|
-
else if (character === ';') {
|
|
10121
|
-
// acorn parseExpressionAt will stop at the first ";", it may be that the expression is not
|
|
10122
|
-
// a multiple expression ({foo;}), but this is a case that we explicitly do not want to support.
|
|
10123
|
-
// in such case, let's fail with the same error as if it were a multiple expression.
|
|
10124
|
-
invariant(false, ParserDiagnostics.MULTIPLE_EXPRESSIONS);
|
|
10125
|
-
}
|
|
10126
|
-
else {
|
|
10127
|
-
invariant(WHITESPACES_RE.test(character), ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, ['Unexpected end of expression']);
|
|
10128
|
-
}
|
|
10129
|
-
}
|
|
10130
|
-
invariant(unclosedParenthesisCount === 0, ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, [
|
|
10131
|
-
'Unexpected end of expression',
|
|
10132
|
-
]);
|
|
10133
|
-
}
|
|
10134
|
-
function parseExpression(ctx, source, location) {
|
|
10135
|
-
const { ecmaVersion } = ctx;
|
|
10136
|
-
return ctx.withErrorWrapping(() => {
|
|
10137
|
-
const parsed = parseExpressionAt(source, 1, {
|
|
10138
|
-
ecmaVersion,
|
|
10139
|
-
allowAwaitOutsideFunction: false,
|
|
10140
|
-
onComment: () => invariant(false, ParserDiagnostics.INVALID_EXPR_COMMENTS_DISALLOWED),
|
|
10141
|
-
});
|
|
10142
|
-
validateSourceIsParsedExpression(source, parsed);
|
|
10143
|
-
validateExpression(source, parsed, ctx.config);
|
|
10144
|
-
return { ...parsed, location };
|
|
10145
|
-
}, ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, location, (err) => `Invalid expression ${source} - ${err.message}`);
|
|
10146
|
-
}
|
|
10147
|
-
function parseIdentifier(ctx, source, location) {
|
|
10148
|
-
let isValid = true;
|
|
10149
|
-
isValid = isIdentifierStart(source.charCodeAt(0));
|
|
10150
|
-
for (let i = 1; i < source.length && isValid; i++) {
|
|
10151
|
-
isValid = isIdentifierChar(source.charCodeAt(i));
|
|
10152
|
-
}
|
|
10153
|
-
if (isValid && !isReservedES6Keyword(source)) {
|
|
10154
|
-
return {
|
|
10155
|
-
...identifier(source),
|
|
10156
|
-
location,
|
|
10157
|
-
};
|
|
10158
|
-
}
|
|
10159
|
-
else {
|
|
10160
|
-
ctx.throwAtLocation(ParserDiagnostics.INVALID_IDENTIFIER, location, [source]);
|
|
10161
|
-
}
|
|
10162
|
-
}
|
|
10163
|
-
|
|
10164
10066
|
/**
|
|
10165
10067
|
* @typedef { import('estree').Node} Node
|
|
10166
10068
|
* @typedef {{
|
|
@@ -10592,6 +10494,120 @@ function parseComplexExpression(ctx, source, templateSource, location, expressio
|
|
|
10592
10494
|
}, ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, location, (err) => `Invalid expression ${source} - ${err.message}`);
|
|
10593
10495
|
}
|
|
10594
10496
|
|
|
10497
|
+
/*
|
|
10498
|
+
* Copyright (c) 2018, salesforce.com, inc.
|
|
10499
|
+
* All rights reserved.
|
|
10500
|
+
* SPDX-License-Identifier: MIT
|
|
10501
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
10502
|
+
*/
|
|
10503
|
+
const EXPRESSION_SYMBOL_START = '{';
|
|
10504
|
+
const EXPRESSION_SYMBOL_END = '}';
|
|
10505
|
+
const POTENTIAL_EXPRESSION_RE = /^.?{.+}.*$/;
|
|
10506
|
+
const WHITESPACES_RE = /\s/;
|
|
10507
|
+
function isExpression(source) {
|
|
10508
|
+
// Issue #3418: Legacy behavior, previous regex treated "{}" attribute value as non expression
|
|
10509
|
+
return source[0] === '{' && source.slice(-1) === '}' && source.length > 2;
|
|
10510
|
+
}
|
|
10511
|
+
function isPotentialExpression(source) {
|
|
10512
|
+
return !!source.match(POTENTIAL_EXPRESSION_RE);
|
|
10513
|
+
}
|
|
10514
|
+
const minCteApiVersion = minApiVersion(11 /* APIFeature.ENABLE_COMPLEX_TEMPLATE_EXPRESSIONS */);
|
|
10515
|
+
function validateExpression(source, node, ctx, unquotedAttributeExpression) {
|
|
10516
|
+
const cteOnlyNode = !isIdentifier(node) && !isMemberExpression(node);
|
|
10517
|
+
// If this node is not an identifier or a member expression (the only two nodes allowed if complexTemplateExpressions are disabled),
|
|
10518
|
+
// then we throw if the following invariants do not hold true.
|
|
10519
|
+
if (cteOnlyNode) {
|
|
10520
|
+
// complexTemplateExpressions must be enabled if this is a cteOnlyNode.
|
|
10521
|
+
invariant(ctx.config.experimentalComplexExpressions, ParserDiagnostics.INVALID_NODE, [
|
|
10522
|
+
node.type,
|
|
10523
|
+
]);
|
|
10524
|
+
// complexTemplateExpressions must be enabled and the component API version must be sufficient.
|
|
10525
|
+
invariant(isComplexTemplateExpressionEnabled(ctx), ParserDiagnostics.INVALID_NODE_CTE_API_VERSION, [node.type, ctx.apiVersion, minCteApiVersion]);
|
|
10526
|
+
// complexTemplateExpressions must be enabled, the component API version must be sufficient and the expression should not be
|
|
10527
|
+
// an unquoted attribute expression.
|
|
10528
|
+
invariant(isComplexTemplateExpressionEnabled(ctx) && !unquotedAttributeExpression, ParserDiagnostics.INVALID_NODE_CTE_UNQUOTED, [node.type, source]);
|
|
10529
|
+
}
|
|
10530
|
+
if (isMemberExpression(node)) {
|
|
10531
|
+
// If this is a computed node and experimentalComputedMemberExpressions is not enabled,
|
|
10532
|
+
// then we throw if the following invariants do not hold true.
|
|
10533
|
+
if (!ctx.config.experimentalComputedMemberExpression && node.computed) {
|
|
10534
|
+
// complexTemplateExpressions must be enabled.
|
|
10535
|
+
invariant(ctx.config.experimentalComplexExpressions, ParserDiagnostics.COMPUTED_PROPERTY_ACCESS_NOT_ALLOWED, [source]);
|
|
10536
|
+
// complexTemplateExpressions must be enabled and the component API version must be sufficient.
|
|
10537
|
+
invariant(isComplexTemplateExpressionEnabled(ctx), ParserDiagnostics.COMPUTED_PROPERTY_ACCESS_NOT_ALLOWED_CTE_API_VERSION, [source, ctx.apiVersion, minCteApiVersion]);
|
|
10538
|
+
// complexTemplateExpressions must be enabled, the component API version must be sufficient and the expression
|
|
10539
|
+
// should not be an unquoted attribute expression.
|
|
10540
|
+
invariant(isComplexTemplateExpressionEnabled(ctx) && !unquotedAttributeExpression, ParserDiagnostics.COMPUTED_PROPERTY_ACCESS_NOT_ALLOWED_CTE_UNQUOTED, [source]);
|
|
10541
|
+
}
|
|
10542
|
+
const { object, property } = node;
|
|
10543
|
+
if (!isIdentifier(object)) {
|
|
10544
|
+
validateExpression(source, object, ctx, unquotedAttributeExpression);
|
|
10545
|
+
}
|
|
10546
|
+
if (!isIdentifier(property)) {
|
|
10547
|
+
validateExpression(source, property, ctx, unquotedAttributeExpression);
|
|
10548
|
+
}
|
|
10549
|
+
}
|
|
10550
|
+
}
|
|
10551
|
+
function validateSourceIsParsedExpression(source, parsedExpression) {
|
|
10552
|
+
if (parsedExpression.end === source.length - 1) {
|
|
10553
|
+
return;
|
|
10554
|
+
}
|
|
10555
|
+
let unclosedParenthesisCount = 0;
|
|
10556
|
+
for (let i = 0, n = parsedExpression.start; i < n; i++) {
|
|
10557
|
+
if (source[i] === '(') {
|
|
10558
|
+
unclosedParenthesisCount++;
|
|
10559
|
+
}
|
|
10560
|
+
}
|
|
10561
|
+
// source[source.length - 1] === '}', n = source.length - 1 is to avoid processing '}'.
|
|
10562
|
+
for (let i = parsedExpression.end, n = source.length - 1; i < n; i++) {
|
|
10563
|
+
const character = source[i];
|
|
10564
|
+
if (character === ')') {
|
|
10565
|
+
unclosedParenthesisCount--;
|
|
10566
|
+
}
|
|
10567
|
+
else if (character === ';') {
|
|
10568
|
+
// acorn parseExpressionAt will stop at the first ";", it may be that the expression is not
|
|
10569
|
+
// a multiple expression ({foo;}), but this is a case that we explicitly do not want to support.
|
|
10570
|
+
// in such case, let's fail with the same error as if it were a multiple expression.
|
|
10571
|
+
invariant(false, ParserDiagnostics.MULTIPLE_EXPRESSIONS);
|
|
10572
|
+
}
|
|
10573
|
+
else {
|
|
10574
|
+
invariant(WHITESPACES_RE.test(character), ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, ['Unexpected end of expression']);
|
|
10575
|
+
}
|
|
10576
|
+
}
|
|
10577
|
+
invariant(unclosedParenthesisCount === 0, ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, [
|
|
10578
|
+
'Unexpected end of expression',
|
|
10579
|
+
]);
|
|
10580
|
+
}
|
|
10581
|
+
function parseExpression(ctx, source, location, unquotedAttributeExpression) {
|
|
10582
|
+
const { ecmaVersion } = ctx;
|
|
10583
|
+
return ctx.withErrorWrapping(() => {
|
|
10584
|
+
const parsed = parseExpressionAt(source, 1, {
|
|
10585
|
+
ecmaVersion,
|
|
10586
|
+
allowAwaitOutsideFunction: false,
|
|
10587
|
+
onComment: () => invariant(false, ParserDiagnostics.INVALID_EXPR_COMMENTS_DISALLOWED),
|
|
10588
|
+
});
|
|
10589
|
+
validateSourceIsParsedExpression(source, parsed);
|
|
10590
|
+
validateExpression(source, parsed, ctx, unquotedAttributeExpression);
|
|
10591
|
+
return { ...parsed, location };
|
|
10592
|
+
}, ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, location, (err) => `Invalid expression ${source} - ${err.message}`);
|
|
10593
|
+
}
|
|
10594
|
+
function parseIdentifier(ctx, source, location) {
|
|
10595
|
+
let isValid = true;
|
|
10596
|
+
isValid = isIdentifierStart(source.charCodeAt(0));
|
|
10597
|
+
for (let i = 1; i < source.length && isValid; i++) {
|
|
10598
|
+
isValid = isIdentifierChar(source.charCodeAt(i));
|
|
10599
|
+
}
|
|
10600
|
+
if (isValid && !isReservedES6Keyword(source)) {
|
|
10601
|
+
return {
|
|
10602
|
+
...identifier(source),
|
|
10603
|
+
location,
|
|
10604
|
+
};
|
|
10605
|
+
}
|
|
10606
|
+
else {
|
|
10607
|
+
ctx.throwAtLocation(ParserDiagnostics.INVALID_IDENTIFIER, location, [source]);
|
|
10608
|
+
}
|
|
10609
|
+
}
|
|
10610
|
+
|
|
10595
10611
|
/*
|
|
10596
10612
|
* Copyright (c) 2018, salesforce.com, inc.
|
|
10597
10613
|
* All rights reserved.
|
|
@@ -11057,7 +11073,9 @@ function normalizeAttributeValue(ctx, raw, tag, attr, location) {
|
|
|
11057
11073
|
const isQuoted = isQuotedAttribute(rawAttrVal);
|
|
11058
11074
|
const isEscaped = isEscapedAttribute(rawAttrVal);
|
|
11059
11075
|
if (!isEscaped && isExpression(value)) {
|
|
11060
|
-
if
|
|
11076
|
+
// Don't test for the API version here, just check if CTE is enabled.
|
|
11077
|
+
// We can provide more specific errors w.r.t API versions after the expression has been parsed and we know what it is.
|
|
11078
|
+
if (isQuoted && !ctx.config.experimentalComplexExpressions) {
|
|
11061
11079
|
// <input value="{myValue}" />
|
|
11062
11080
|
// -> ambiguity if the attribute value is a template identifier or a string literal.
|
|
11063
11081
|
const unquoted = raw.replace(/"/g, '');
|
|
@@ -11503,7 +11521,7 @@ function parseText(ctx, rawText, sourceLocation, location) {
|
|
|
11503
11521
|
}
|
|
11504
11522
|
let value;
|
|
11505
11523
|
if (isExpression(token)) {
|
|
11506
|
-
value = parseExpression(ctx, token, sourceLocation);
|
|
11524
|
+
value = parseExpression(ctx, token, sourceLocation, false);
|
|
11507
11525
|
}
|
|
11508
11526
|
else {
|
|
11509
11527
|
value = literal(decodeTextContent(token));
|
|
@@ -11555,7 +11573,7 @@ function parseTextNode(ctx, parse5Text) {
|
|
|
11555
11573
|
return [];
|
|
11556
11574
|
}
|
|
11557
11575
|
const sourceLocation$1 = sourceLocation(location);
|
|
11558
|
-
return ctx
|
|
11576
|
+
return isComplexTemplateExpressionEnabled(ctx)
|
|
11559
11577
|
? parseTextComplex(ctx, rawText, sourceLocation$1, location)
|
|
11560
11578
|
: parseText(ctx, rawText, sourceLocation$1, location);
|
|
11561
11579
|
}
|
|
@@ -12438,13 +12456,13 @@ function getTemplateAttribute(ctx, tag, attribute$1, attributeLocation) {
|
|
|
12438
12456
|
to be used.
|
|
12439
12457
|
*/
|
|
12440
12458
|
const isPotentialComplexExpression = quotedExpression && !escapedExpression && value.startsWith(EXPRESSION_SYMBOL_START);
|
|
12441
|
-
if (ctx
|
|
12459
|
+
if (isComplexTemplateExpressionEnabled(ctx) && isPotentialComplexExpression) {
|
|
12442
12460
|
const attributeNameOffset = attribute$1.name.length + 2; // The +2 accounts for the '="' in the attribute: attr="...
|
|
12443
12461
|
const templateSource = ctx.getSource(attributeLocation.startOffset + attributeNameOffset);
|
|
12444
12462
|
attrValue = parseComplexExpression(ctx, value, templateSource, location).expression;
|
|
12445
12463
|
}
|
|
12446
12464
|
else if (isExpression(value) && !escapedExpression) {
|
|
12447
|
-
attrValue = parseExpression(ctx, value, location);
|
|
12465
|
+
attrValue = parseExpression(ctx, value, location, !quotedExpression);
|
|
12448
12466
|
}
|
|
12449
12467
|
else if (isBooleanAttribute) {
|
|
12450
12468
|
attrValue = literal(true);
|
|
@@ -13043,6 +13061,48 @@ function serializeStaticElement(element, codeGen) {
|
|
|
13043
13061
|
* SPDX-License-Identifier: MIT
|
|
13044
13062
|
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
13045
13063
|
*/
|
|
13064
|
+
/**
|
|
13065
|
+
* Bind the passed expression to the component instance. It applies the following transformation to the expression:
|
|
13066
|
+
* - {value} --> {$cmp.value}
|
|
13067
|
+
* - {value[index]} --> {$cmp.value[$cmp.index]}
|
|
13068
|
+
* @param expression
|
|
13069
|
+
*/
|
|
13070
|
+
function bindExpression(expression, isLocalIdentifier, templateInstanceName, experimentalComplexExpressions) {
|
|
13071
|
+
if (isIdentifier(expression)) {
|
|
13072
|
+
if (!isLocalIdentifier(expression)) {
|
|
13073
|
+
return memberExpression(identifier(templateInstanceName), expression);
|
|
13074
|
+
}
|
|
13075
|
+
else {
|
|
13076
|
+
return expression;
|
|
13077
|
+
}
|
|
13078
|
+
}
|
|
13079
|
+
// TODO [#3370]: remove experimental template expression flag
|
|
13080
|
+
if (experimentalComplexExpressions) {
|
|
13081
|
+
// Cloning here is necessary because `this.replace()` is destructive, and we might use the
|
|
13082
|
+
// node later during static content optimization
|
|
13083
|
+
expression = structuredClone(expression);
|
|
13084
|
+
return bindComplexExpression(expression, isLocalIdentifier, templateInstanceName);
|
|
13085
|
+
}
|
|
13086
|
+
// Cloning here is necessary because `this.replace()` is destructive, and we might use the
|
|
13087
|
+
// node later during static content optimization
|
|
13088
|
+
expression = structuredClone(expression);
|
|
13089
|
+
// TODO [#3370]: when the template expression flag is removed, the
|
|
13090
|
+
// ComplexExpression type should be redefined as an ESTree Node. Doing
|
|
13091
|
+
// so when the flag is still in place results in a cascade of required
|
|
13092
|
+
// type changes across the codebase.
|
|
13093
|
+
walk(expression, {
|
|
13094
|
+
leave(node, parent) {
|
|
13095
|
+
if (parent !== null &&
|
|
13096
|
+
isIdentifier(node) &&
|
|
13097
|
+
isMemberExpression(parent) &&
|
|
13098
|
+
parent.object === node &&
|
|
13099
|
+
!isLocalIdentifier(node)) {
|
|
13100
|
+
this.replace(memberExpression(identifier(templateInstanceName), node));
|
|
13101
|
+
}
|
|
13102
|
+
},
|
|
13103
|
+
});
|
|
13104
|
+
return expression;
|
|
13105
|
+
}
|
|
13046
13106
|
/**
|
|
13047
13107
|
* Bind the passed expression to the component instance. It applies the following
|
|
13048
13108
|
* transformation to the expression:
|
|
@@ -13064,7 +13124,7 @@ function serializeStaticElement(element, codeGen) {
|
|
|
13064
13124
|
* @param expression
|
|
13065
13125
|
* @param codeGen
|
|
13066
13126
|
*/
|
|
13067
|
-
function bindComplexExpression(expression,
|
|
13127
|
+
function bindComplexExpression(expression, isLocalIdentifier, templateInstanceName) {
|
|
13068
13128
|
const expressionScopes = new ExpressionScopes();
|
|
13069
13129
|
// TODO [#3370]: when the template expression flag is removed, the
|
|
13070
13130
|
// ComplexExpression type should be redefined as an ESTree Node. Doing
|
|
@@ -13088,9 +13148,9 @@ function bindComplexExpression(expression, codeGen) {
|
|
|
13088
13148
|
isIdentifier$1 &&
|
|
13089
13149
|
!(isMemberExpression(parent) && parent.property === node && !parent.computed) &&
|
|
13090
13150
|
!(isProperty$1(parent) && parent.key === node) &&
|
|
13091
|
-
!
|
|
13151
|
+
!isLocalIdentifier(node) &&
|
|
13092
13152
|
!expressionScopes.isScopedToExpression(node)) {
|
|
13093
|
-
this.replace(memberExpression(identifier(
|
|
13153
|
+
this.replace(memberExpression(identifier(templateInstanceName), node));
|
|
13094
13154
|
}
|
|
13095
13155
|
},
|
|
13096
13156
|
});
|
|
@@ -13571,43 +13631,7 @@ class CodeGen {
|
|
|
13571
13631
|
* @param expression
|
|
13572
13632
|
*/
|
|
13573
13633
|
bindExpression(expression) {
|
|
13574
|
-
|
|
13575
|
-
if (!this.isLocalIdentifier(expression)) {
|
|
13576
|
-
return memberExpression(identifier(TEMPLATE_PARAMS.INSTANCE), expression);
|
|
13577
|
-
}
|
|
13578
|
-
else {
|
|
13579
|
-
return expression;
|
|
13580
|
-
}
|
|
13581
|
-
}
|
|
13582
|
-
// TODO [#3370]: remove experimental template expression flag
|
|
13583
|
-
if (this.state.config.experimentalComplexExpressions) {
|
|
13584
|
-
// Cloning here is necessary because `this.replace()` is destructive, and we might use the
|
|
13585
|
-
// node later during static content optimization
|
|
13586
|
-
expression = structuredClone(expression);
|
|
13587
|
-
return bindComplexExpression(expression, this);
|
|
13588
|
-
}
|
|
13589
|
-
// We need access to both this `this` and the walker's `this` in the walker
|
|
13590
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
13591
|
-
const scope = this;
|
|
13592
|
-
// Cloning here is necessary because `this.replace()` is destructive, and we might use the
|
|
13593
|
-
// node later during static content optimization
|
|
13594
|
-
expression = structuredClone(expression);
|
|
13595
|
-
// TODO [#3370]: when the template expression flag is removed, the
|
|
13596
|
-
// ComplexExpression type should be redefined as an ESTree Node. Doing
|
|
13597
|
-
// so when the flag is still in place results in a cascade of required
|
|
13598
|
-
// type changes across the codebase.
|
|
13599
|
-
walk(expression, {
|
|
13600
|
-
leave(node, parent) {
|
|
13601
|
-
if (parent !== null &&
|
|
13602
|
-
isIdentifier(node) &&
|
|
13603
|
-
isMemberExpression(parent) &&
|
|
13604
|
-
parent.object === node &&
|
|
13605
|
-
!scope.isLocalIdentifier(node)) {
|
|
13606
|
-
this.replace(memberExpression(identifier(TEMPLATE_PARAMS.INSTANCE), node));
|
|
13607
|
-
}
|
|
13608
|
-
},
|
|
13609
|
-
});
|
|
13610
|
-
return expression;
|
|
13634
|
+
return bindExpression(expression, this.isLocalIdentifier.bind(this), TEMPLATE_PARAMS.INSTANCE, this.state.config.experimentalComplexExpressions);
|
|
13611
13635
|
}
|
|
13612
13636
|
genStaticElement(element, slotParentName) {
|
|
13613
13637
|
const staticParts = this.genStaticParts(element);
|
|
@@ -14519,6 +14543,6 @@ function compile(source, filename, config) {
|
|
|
14519
14543
|
};
|
|
14520
14544
|
}
|
|
14521
14545
|
|
|
14522
|
-
export { ElementDirectiveName, LWCDirectiveDomMode, LWCDirectiveRenderMode, LwcTagName, RootDirectiveName, TemplateDirectiveName, compile, compile as default, generateScopeTokens, kebabcaseToCamelcase, parse, toPropertyName };
|
|
14523
|
-
/** version: 8.22.
|
|
14546
|
+
export { ElementDirectiveName, LWCDirectiveDomMode, LWCDirectiveRenderMode, LwcTagName, RootDirectiveName, TemplateDirectiveName, bindExpression, compile, compile as default, generateScopeTokens, kebabcaseToCamelcase, parse, toPropertyName };
|
|
14547
|
+
/** version: 8.22.4 */
|
|
14524
14548
|
//# sourceMappingURL=index.js.map
|
|
@@ -6,6 +6,6 @@ export declare const EXPRESSION_SYMBOL_END = "}";
|
|
|
6
6
|
export declare function isExpression(source: string): boolean;
|
|
7
7
|
export declare function isPotentialExpression(source: string): boolean;
|
|
8
8
|
export declare function validateSourceIsParsedExpression(source: string, parsedExpression: Node): void;
|
|
9
|
-
export declare function parseExpression(ctx: ParserCtx, source: string, location: SourceLocation): Expression;
|
|
9
|
+
export declare function parseExpression(ctx: ParserCtx, source: string, location: SourceLocation, unquotedAttributeExpression: boolean): Expression;
|
|
10
10
|
export declare function parseIdentifier(ctx: ParserCtx, source: string, location: SourceLocation): Identifier;
|
|
11
11
|
//# sourceMappingURL=expression.d.ts.map
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"You can safely modify dependencies, devDependencies, keywords, etc., but other props will be overwritten."
|
|
5
5
|
],
|
|
6
6
|
"name": "@lwc/template-compiler",
|
|
7
|
-
"version": "8.22.
|
|
7
|
+
"version": "8.22.4",
|
|
8
8
|
"description": "Template compiler package",
|
|
9
9
|
"keywords": [
|
|
10
10
|
"lwc"
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@lwc/errors": "8.22.
|
|
50
|
-
"@lwc/shared": "8.22.
|
|
49
|
+
"@lwc/errors": "8.22.4",
|
|
50
|
+
"@lwc/shared": "8.22.4",
|
|
51
51
|
"acorn": "~8.15.0",
|
|
52
52
|
"astring": "~1.9.0",
|
|
53
53
|
"he": "~1.2.0"
|