@lwc/template-compiler 7.0.1-alpha.0 → 7.0.2
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/index.cjs.js +183 -155
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.js +183 -155
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs.js
CHANGED
|
@@ -10912,6 +10912,138 @@ function validateExpressionAst(rootNode) {
|
|
|
10912
10912
|
});
|
|
10913
10913
|
}
|
|
10914
10914
|
|
|
10915
|
+
/*
|
|
10916
|
+
* Copyright (c) 2018, salesforce.com, inc.
|
|
10917
|
+
* All rights reserved.
|
|
10918
|
+
* SPDX-License-Identifier: MIT
|
|
10919
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
10920
|
+
*/
|
|
10921
|
+
// https://262.ecma-international.org/12.0/#sec-keywords-and-reserved-words
|
|
10922
|
+
// prettier-ignore
|
|
10923
|
+
const REVERSED_KEYWORDS = new Set([
|
|
10924
|
+
// Reserved keywords
|
|
10925
|
+
'await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 'delete',
|
|
10926
|
+
'do', 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'import',
|
|
10927
|
+
'in', 'instanceof', 'new', 'null', 'return', 'super', 'switch', 'this', 'throw', 'true', 'try',
|
|
10928
|
+
'typeof', 'var', 'void', 'while', 'with', 'yield',
|
|
10929
|
+
// Strict mode only reserved keywords
|
|
10930
|
+
'let', 'static', 'implements', 'interface', 'package', 'private', 'protected', 'public'
|
|
10931
|
+
]);
|
|
10932
|
+
function isReservedES6Keyword(str) {
|
|
10933
|
+
return REVERSED_KEYWORDS.has(str);
|
|
10934
|
+
}
|
|
10935
|
+
|
|
10936
|
+
/*
|
|
10937
|
+
* Copyright (c) 2018, salesforce.com, inc.
|
|
10938
|
+
* All rights reserved.
|
|
10939
|
+
* SPDX-License-Identifier: MIT
|
|
10940
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
10941
|
+
*/
|
|
10942
|
+
const EXPRESSION_SYMBOL_START = '{';
|
|
10943
|
+
const EXPRESSION_SYMBOL_END = '}';
|
|
10944
|
+
const POTENTIAL_EXPRESSION_RE = /^.?{.+}.*$/;
|
|
10945
|
+
const WHITESPACES_RE = /\s/;
|
|
10946
|
+
function isExpression(source) {
|
|
10947
|
+
// Issue #3418: Legacy behavior, previous regex treated "{}" attribute value as non expression
|
|
10948
|
+
return source[0] === '{' && source.slice(-1) === '}' && source.length > 2;
|
|
10949
|
+
}
|
|
10950
|
+
function isPotentialExpression(source) {
|
|
10951
|
+
return !!source.match(POTENTIAL_EXPRESSION_RE);
|
|
10952
|
+
}
|
|
10953
|
+
function validateExpression(node, config) {
|
|
10954
|
+
// TODO [#3370]: remove experimental template expression flag
|
|
10955
|
+
if (config.experimentalComplexExpressions) {
|
|
10956
|
+
return validateExpressionAst(node);
|
|
10957
|
+
}
|
|
10958
|
+
const isValidNode = isIdentifier(node) || isMemberExpression(node);
|
|
10959
|
+
errors.invariant(isValidNode, errors.ParserDiagnostics.INVALID_NODE, [node.type]);
|
|
10960
|
+
if (isMemberExpression(node)) {
|
|
10961
|
+
errors.invariant(config.experimentalComputedMemberExpression || !node.computed, errors.ParserDiagnostics.COMPUTED_PROPERTY_ACCESS_NOT_ALLOWED);
|
|
10962
|
+
const { object, property } = node;
|
|
10963
|
+
if (!isIdentifier(object)) {
|
|
10964
|
+
validateExpression(object, config);
|
|
10965
|
+
}
|
|
10966
|
+
if (!isIdentifier(property)) {
|
|
10967
|
+
validateExpression(property, config);
|
|
10968
|
+
}
|
|
10969
|
+
}
|
|
10970
|
+
}
|
|
10971
|
+
function validateSourceIsParsedExpression(source, parsedExpression) {
|
|
10972
|
+
if (parsedExpression.end === source.length - 1) {
|
|
10973
|
+
return;
|
|
10974
|
+
}
|
|
10975
|
+
let unclosedParenthesisCount = 0;
|
|
10976
|
+
for (let i = 0, n = parsedExpression.start; i < n; i++) {
|
|
10977
|
+
if (source[i] === '(') {
|
|
10978
|
+
unclosedParenthesisCount++;
|
|
10979
|
+
}
|
|
10980
|
+
}
|
|
10981
|
+
// source[source.length - 1] === '}', n = source.length - 1 is to avoid processing '}'.
|
|
10982
|
+
for (let i = parsedExpression.end, n = source.length - 1; i < n; i++) {
|
|
10983
|
+
const character = source[i];
|
|
10984
|
+
if (character === ')') {
|
|
10985
|
+
unclosedParenthesisCount--;
|
|
10986
|
+
}
|
|
10987
|
+
else if (character === ';') {
|
|
10988
|
+
// acorn parseExpressionAt will stop at the first ";", it may be that the expression is not
|
|
10989
|
+
// a multiple expression ({foo;}), but this is a case that we explicitly do not want to support.
|
|
10990
|
+
// in such case, let's fail with the same error as if it were a multiple expression.
|
|
10991
|
+
errors.invariant(false, errors.ParserDiagnostics.MULTIPLE_EXPRESSIONS);
|
|
10992
|
+
}
|
|
10993
|
+
else {
|
|
10994
|
+
errors.invariant(WHITESPACES_RE.test(character), errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, ['Unexpected end of expression']);
|
|
10995
|
+
}
|
|
10996
|
+
}
|
|
10997
|
+
errors.invariant(unclosedParenthesisCount === 0, errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, [
|
|
10998
|
+
'Unexpected end of expression',
|
|
10999
|
+
]);
|
|
11000
|
+
}
|
|
11001
|
+
function validatePreparsedJsExpressions(ctx) {
|
|
11002
|
+
ctx.preparsedJsExpressions?.forEach(({ parsedExpression, rawText }) => {
|
|
11003
|
+
const acornLoc = parsedExpression.loc;
|
|
11004
|
+
const parse5Loc = {
|
|
11005
|
+
startLine: acornLoc.start.line,
|
|
11006
|
+
startCol: acornLoc.start.column,
|
|
11007
|
+
startOffset: parsedExpression.start,
|
|
11008
|
+
endLine: acornLoc.end.line,
|
|
11009
|
+
endCol: acornLoc.end.column,
|
|
11010
|
+
endOffset: parsedExpression.end,
|
|
11011
|
+
};
|
|
11012
|
+
ctx.withErrorWrapping(() => {
|
|
11013
|
+
validateExpressionAst(parsedExpression);
|
|
11014
|
+
}, errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, sourceLocation(parse5Loc), (err) => `Invalid expression ${rawText} - ${err.message}`);
|
|
11015
|
+
});
|
|
11016
|
+
}
|
|
11017
|
+
function parseExpression(ctx, source, location) {
|
|
11018
|
+
const { ecmaVersion } = ctx;
|
|
11019
|
+
return ctx.withErrorWrapping(() => {
|
|
11020
|
+
const parsed = acorn.parseExpressionAt(source, 1, {
|
|
11021
|
+
ecmaVersion,
|
|
11022
|
+
// TODO [#3370]: remove experimental template expression flag
|
|
11023
|
+
allowAwaitOutsideFunction: ctx.config.experimentalComplexExpressions,
|
|
11024
|
+
});
|
|
11025
|
+
validateSourceIsParsedExpression(source, parsed);
|
|
11026
|
+
validateExpression(parsed, ctx.config);
|
|
11027
|
+
return { ...parsed, location };
|
|
11028
|
+
}, errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, location, (err) => `Invalid expression ${source} - ${err.message}`);
|
|
11029
|
+
}
|
|
11030
|
+
function parseIdentifier(ctx, source, location) {
|
|
11031
|
+
let isValid = true;
|
|
11032
|
+
isValid = acorn.isIdentifierStart(source.charCodeAt(0));
|
|
11033
|
+
for (let i = 1; i < source.length && isValid; i++) {
|
|
11034
|
+
isValid = acorn.isIdentifierChar(source.charCodeAt(i));
|
|
11035
|
+
}
|
|
11036
|
+
if (isValid && !isReservedES6Keyword(source)) {
|
|
11037
|
+
return {
|
|
11038
|
+
...identifier(source),
|
|
11039
|
+
location,
|
|
11040
|
+
};
|
|
11041
|
+
}
|
|
11042
|
+
else {
|
|
11043
|
+
ctx.throwAtLocation(errors.ParserDiagnostics.INVALID_IDENTIFIER, location, [source]);
|
|
11044
|
+
}
|
|
11045
|
+
}
|
|
11046
|
+
|
|
10915
11047
|
/*
|
|
10916
11048
|
* Copyright (c) 2023, salesforce.com, inc.
|
|
10917
11049
|
* All rights reserved.
|
|
@@ -11069,17 +11201,19 @@ class TemplateHtmlTokenizer extends Tokenizer {
|
|
|
11069
11201
|
}
|
|
11070
11202
|
}
|
|
11071
11203
|
}
|
|
11072
|
-
function isTemplateExpressionTextNodeValue(value) {
|
|
11073
|
-
return value?.[0] === '{';
|
|
11074
|
-
}
|
|
11075
11204
|
function isTemplateElement(node) {
|
|
11076
11205
|
return node.nodeName === 'template';
|
|
11077
11206
|
}
|
|
11078
11207
|
function isTextNode(node) {
|
|
11079
11208
|
return node?.nodeName === '#text';
|
|
11080
11209
|
}
|
|
11081
|
-
function isTemplateExpressionTextNode(node) {
|
|
11082
|
-
return isTextNode(node) &&
|
|
11210
|
+
function isTemplateExpressionTextNode(node, html) {
|
|
11211
|
+
return (isTextNode(node) &&
|
|
11212
|
+
isTemplateExpressionTextNodeValue(node.value, node.sourceCodeLocation.startOffset, html));
|
|
11213
|
+
}
|
|
11214
|
+
function isTemplateExpressionTextNodeValue(value, startOffset, html) {
|
|
11215
|
+
return (value.startsWith(EXPRESSION_SYMBOL_START) &&
|
|
11216
|
+
html.startsWith(EXPRESSION_SYMBOL_START, startOffset));
|
|
11083
11217
|
}
|
|
11084
11218
|
/**
|
|
11085
11219
|
* This class extends `parse5`'s internal parser. The heavy lifting is
|
|
@@ -11101,11 +11235,12 @@ class TemplateHtmlParser extends Parser {
|
|
|
11101
11235
|
_insertCharacters(token) {
|
|
11102
11236
|
const parentNode = this.openElements.current;
|
|
11103
11237
|
const previousPeer = parentNode.childNodes.at(-1);
|
|
11238
|
+
const html = this.tokenizer.preprocessor.html;
|
|
11104
11239
|
if (
|
|
11105
11240
|
// If we're not dealing with a template expression...
|
|
11106
|
-
!isTemplateExpressionTextNodeValue(token.chars) &&
|
|
11241
|
+
!isTemplateExpressionTextNodeValue(token.chars, token.location.startOffset, html) &&
|
|
11107
11242
|
// ... and the previous node wasn't a text-node-template-expression...
|
|
11108
|
-
!isTemplateExpressionTextNode(previousPeer)) {
|
|
11243
|
+
!isTemplateExpressionTextNode(previousPeer, html)) {
|
|
11109
11244
|
// ... concatenate the provided characters with the previous token's characters.
|
|
11110
11245
|
return super._insertCharacters(token);
|
|
11111
11246
|
}
|
|
@@ -11225,138 +11360,6 @@ function decodeTextContent(source) {
|
|
|
11225
11360
|
return he__namespace.decode(source);
|
|
11226
11361
|
}
|
|
11227
11362
|
|
|
11228
|
-
/*
|
|
11229
|
-
* Copyright (c) 2018, salesforce.com, inc.
|
|
11230
|
-
* All rights reserved.
|
|
11231
|
-
* SPDX-License-Identifier: MIT
|
|
11232
|
-
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
11233
|
-
*/
|
|
11234
|
-
// https://262.ecma-international.org/12.0/#sec-keywords-and-reserved-words
|
|
11235
|
-
// prettier-ignore
|
|
11236
|
-
const REVERSED_KEYWORDS = new Set([
|
|
11237
|
-
// Reserved keywords
|
|
11238
|
-
'await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 'delete',
|
|
11239
|
-
'do', 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'import',
|
|
11240
|
-
'in', 'instanceof', 'new', 'null', 'return', 'super', 'switch', 'this', 'throw', 'true', 'try',
|
|
11241
|
-
'typeof', 'var', 'void', 'while', 'with', 'yield',
|
|
11242
|
-
// Strict mode only reserved keywords
|
|
11243
|
-
'let', 'static', 'implements', 'interface', 'package', 'private', 'protected', 'public'
|
|
11244
|
-
]);
|
|
11245
|
-
function isReservedES6Keyword(str) {
|
|
11246
|
-
return REVERSED_KEYWORDS.has(str);
|
|
11247
|
-
}
|
|
11248
|
-
|
|
11249
|
-
/*
|
|
11250
|
-
* Copyright (c) 2018, salesforce.com, inc.
|
|
11251
|
-
* All rights reserved.
|
|
11252
|
-
* SPDX-License-Identifier: MIT
|
|
11253
|
-
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
11254
|
-
*/
|
|
11255
|
-
const EXPRESSION_SYMBOL_START = '{';
|
|
11256
|
-
const EXPRESSION_SYMBOL_END = '}';
|
|
11257
|
-
const POTENTIAL_EXPRESSION_RE = /^.?{.+}.*$/;
|
|
11258
|
-
const WHITESPACES_RE = /\s/;
|
|
11259
|
-
function isExpression(source) {
|
|
11260
|
-
// Issue #3418: Legacy behavior, previous regex treated "{}" attribute value as non expression
|
|
11261
|
-
return source[0] === '{' && source.slice(-1) === '}' && source.length > 2;
|
|
11262
|
-
}
|
|
11263
|
-
function isPotentialExpression(source) {
|
|
11264
|
-
return !!source.match(POTENTIAL_EXPRESSION_RE);
|
|
11265
|
-
}
|
|
11266
|
-
function validateExpression(node, config) {
|
|
11267
|
-
// TODO [#3370]: remove experimental template expression flag
|
|
11268
|
-
if (config.experimentalComplexExpressions) {
|
|
11269
|
-
return validateExpressionAst(node);
|
|
11270
|
-
}
|
|
11271
|
-
const isValidNode = isIdentifier(node) || isMemberExpression(node);
|
|
11272
|
-
errors.invariant(isValidNode, errors.ParserDiagnostics.INVALID_NODE, [node.type]);
|
|
11273
|
-
if (isMemberExpression(node)) {
|
|
11274
|
-
errors.invariant(config.experimentalComputedMemberExpression || !node.computed, errors.ParserDiagnostics.COMPUTED_PROPERTY_ACCESS_NOT_ALLOWED);
|
|
11275
|
-
const { object, property } = node;
|
|
11276
|
-
if (!isIdentifier(object)) {
|
|
11277
|
-
validateExpression(object, config);
|
|
11278
|
-
}
|
|
11279
|
-
if (!isIdentifier(property)) {
|
|
11280
|
-
validateExpression(property, config);
|
|
11281
|
-
}
|
|
11282
|
-
}
|
|
11283
|
-
}
|
|
11284
|
-
function validateSourceIsParsedExpression(source, parsedExpression) {
|
|
11285
|
-
if (parsedExpression.end === source.length - 1) {
|
|
11286
|
-
return;
|
|
11287
|
-
}
|
|
11288
|
-
let unclosedParenthesisCount = 0;
|
|
11289
|
-
for (let i = 0, n = parsedExpression.start; i < n; i++) {
|
|
11290
|
-
if (source[i] === '(') {
|
|
11291
|
-
unclosedParenthesisCount++;
|
|
11292
|
-
}
|
|
11293
|
-
}
|
|
11294
|
-
// source[source.length - 1] === '}', n = source.length - 1 is to avoid processing '}'.
|
|
11295
|
-
for (let i = parsedExpression.end, n = source.length - 1; i < n; i++) {
|
|
11296
|
-
const character = source[i];
|
|
11297
|
-
if (character === ')') {
|
|
11298
|
-
unclosedParenthesisCount--;
|
|
11299
|
-
}
|
|
11300
|
-
else if (character === ';') {
|
|
11301
|
-
// acorn parseExpressionAt will stop at the first ";", it may be that the expression is not
|
|
11302
|
-
// a multiple expression ({foo;}), but this is a case that we explicitly do not want to support.
|
|
11303
|
-
// in such case, let's fail with the same error as if it were a multiple expression.
|
|
11304
|
-
errors.invariant(false, errors.ParserDiagnostics.MULTIPLE_EXPRESSIONS);
|
|
11305
|
-
}
|
|
11306
|
-
else {
|
|
11307
|
-
errors.invariant(WHITESPACES_RE.test(character), errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, ['Unexpected end of expression']);
|
|
11308
|
-
}
|
|
11309
|
-
}
|
|
11310
|
-
errors.invariant(unclosedParenthesisCount === 0, errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, [
|
|
11311
|
-
'Unexpected end of expression',
|
|
11312
|
-
]);
|
|
11313
|
-
}
|
|
11314
|
-
function validatePreparsedJsExpressions(ctx) {
|
|
11315
|
-
ctx.preparsedJsExpressions?.forEach(({ parsedExpression, rawText }) => {
|
|
11316
|
-
const acornLoc = parsedExpression.loc;
|
|
11317
|
-
const parse5Loc = {
|
|
11318
|
-
startLine: acornLoc.start.line,
|
|
11319
|
-
startCol: acornLoc.start.column,
|
|
11320
|
-
startOffset: parsedExpression.start,
|
|
11321
|
-
endLine: acornLoc.end.line,
|
|
11322
|
-
endCol: acornLoc.end.column,
|
|
11323
|
-
endOffset: parsedExpression.end,
|
|
11324
|
-
};
|
|
11325
|
-
ctx.withErrorWrapping(() => {
|
|
11326
|
-
validateExpressionAst(parsedExpression);
|
|
11327
|
-
}, errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, sourceLocation(parse5Loc), (err) => `Invalid expression ${rawText} - ${err.message}`);
|
|
11328
|
-
});
|
|
11329
|
-
}
|
|
11330
|
-
function parseExpression(ctx, source, location) {
|
|
11331
|
-
const { ecmaVersion } = ctx;
|
|
11332
|
-
return ctx.withErrorWrapping(() => {
|
|
11333
|
-
const parsed = acorn.parseExpressionAt(source, 1, {
|
|
11334
|
-
ecmaVersion,
|
|
11335
|
-
// TODO [#3370]: remove experimental template expression flag
|
|
11336
|
-
allowAwaitOutsideFunction: ctx.config.experimentalComplexExpressions,
|
|
11337
|
-
});
|
|
11338
|
-
validateSourceIsParsedExpression(source, parsed);
|
|
11339
|
-
validateExpression(parsed, ctx.config);
|
|
11340
|
-
return { ...parsed, location };
|
|
11341
|
-
}, errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, location, (err) => `Invalid expression ${source} - ${err.message}`);
|
|
11342
|
-
}
|
|
11343
|
-
function parseIdentifier(ctx, source, location) {
|
|
11344
|
-
let isValid = true;
|
|
11345
|
-
isValid = acorn.isIdentifierStart(source.charCodeAt(0));
|
|
11346
|
-
for (let i = 1; i < source.length && isValid; i++) {
|
|
11347
|
-
isValid = acorn.isIdentifierChar(source.charCodeAt(i));
|
|
11348
|
-
}
|
|
11349
|
-
if (isValid && !isReservedES6Keyword(source)) {
|
|
11350
|
-
return {
|
|
11351
|
-
...identifier(source),
|
|
11352
|
-
location,
|
|
11353
|
-
};
|
|
11354
|
-
}
|
|
11355
|
-
else {
|
|
11356
|
-
ctx.throwAtLocation(errors.ParserDiagnostics.INVALID_IDENTIFIER, location, [source]);
|
|
11357
|
-
}
|
|
11358
|
-
}
|
|
11359
|
-
|
|
11360
11363
|
/*
|
|
11361
11364
|
* Copyright (c) 2018, salesforce.com, inc.
|
|
11362
11365
|
* All rights reserved.
|
|
@@ -12958,15 +12961,15 @@ function parseStyleText(cssText) {
|
|
|
12958
12961
|
}
|
|
12959
12962
|
return styleMap;
|
|
12960
12963
|
}
|
|
12964
|
+
const IMPORTANT_FLAG = /\s*!\s*important\s*$/i;
|
|
12961
12965
|
// Given a map of CSS property keys to values, return an array AST like:
|
|
12962
12966
|
// ['color', 'blue', false] // { color: 'blue' }
|
|
12963
12967
|
// ['background', 'red', true] // { background: 'red !important' }
|
|
12964
12968
|
function styleMapToStyleDeclsAST(styleMap) {
|
|
12965
12969
|
const styles = Object.entries(styleMap).map(([key, value]) => {
|
|
12966
|
-
const important =
|
|
12970
|
+
const important = IMPORTANT_FLAG.test(value);
|
|
12967
12971
|
if (important) {
|
|
12968
|
-
|
|
12969
|
-
value = value.substring(0, value.length - 10).trim();
|
|
12972
|
+
value = value.replace(IMPORTANT_FLAG, '').trim();
|
|
12970
12973
|
}
|
|
12971
12974
|
return [key, value, important];
|
|
12972
12975
|
});
|
|
@@ -13007,10 +13010,6 @@ function isStaticNode(node, apiVersion) {
|
|
|
13007
13010
|
result &&= attributes.every(({ name, value }) => {
|
|
13008
13011
|
const isStaticSafeLiteral = isLiteral(value) &&
|
|
13009
13012
|
name !== 'slot' &&
|
|
13010
|
-
// check for ScopedId
|
|
13011
|
-
name !== 'id' &&
|
|
13012
|
-
name !== 'spellcheck' && // spellcheck is specially handled by the vnodes.
|
|
13013
|
-
!isIdReferencingAttribute(name) &&
|
|
13014
13013
|
// svg href needs sanitization.
|
|
13015
13014
|
!isSvgUseHref(nodeName, name, namespace) &&
|
|
13016
13015
|
// Check for ScopedFragId
|
|
@@ -13159,7 +13158,7 @@ function serializeAttrs(element, codeGen) {
|
|
|
13159
13158
|
*/
|
|
13160
13159
|
const attrs = [];
|
|
13161
13160
|
let hasClassAttr = false;
|
|
13162
|
-
const collector = ({ name, value, hasExpression, }) => {
|
|
13161
|
+
const collector = ({ name, value, hasExpression, isIdOrIdRef, }) => {
|
|
13163
13162
|
let v = typeof value === 'string' ? templateStringEscape(value) : value;
|
|
13164
13163
|
if (name === 'class') {
|
|
13165
13164
|
hasClassAttr = true;
|
|
@@ -13171,10 +13170,24 @@ function serializeAttrs(element, codeGen) {
|
|
|
13171
13170
|
v += '${0}';
|
|
13172
13171
|
}
|
|
13173
13172
|
}
|
|
13173
|
+
// `spellcheck` string values are specially handled to massage them into booleans.
|
|
13174
|
+
// For backwards compat with non-static-optimized templates, we also treat any non-`"false"`
|
|
13175
|
+
// value other than the valueless format (e.g. `<div spellcheck>`) as `"true"`,
|
|
13176
|
+
// even though per MDN, the empty string and `"true"` are equivalent:
|
|
13177
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/spellcheck
|
|
13178
|
+
if (name === 'spellcheck' && typeof v === 'string' && !hasExpression) {
|
|
13179
|
+
v = String(v.toLowerCase() !== 'false');
|
|
13180
|
+
}
|
|
13174
13181
|
if (typeof v === 'string') {
|
|
13182
|
+
// IDs/IDRefs must be handled dynamically at runtime due to synthetic shadow scoping.
|
|
13183
|
+
// Skip serializing here and handle it as if it were a dynamic attribute instead.
|
|
13184
|
+
// Note that, to maintain backwards compatibility with the non-static output, we treat the valueless
|
|
13185
|
+
// "boolean" format (e.g. `<div id>`) as the empty string, which is semantically equivalent.
|
|
13186
|
+
// TODO [#3658]: `disableSyntheticShadowSupport` should also disable this dynamic behavior
|
|
13187
|
+
const needsPlaceholder = hasExpression || isIdOrIdRef;
|
|
13175
13188
|
// Inject a placeholder where the staticPartId will go when an expression occurs.
|
|
13176
13189
|
// This is only needed for SSR to inject the expression value during serialization.
|
|
13177
|
-
attrs.push(
|
|
13190
|
+
attrs.push(needsPlaceholder ? `\${"${v}"}` : ` ${name}="${shared.htmlEscape(v, true)}"`);
|
|
13178
13191
|
}
|
|
13179
13192
|
else {
|
|
13180
13193
|
attrs.push(` ${name}`);
|
|
@@ -13182,13 +13195,20 @@ function serializeAttrs(element, codeGen) {
|
|
|
13182
13195
|
};
|
|
13183
13196
|
element.attributes
|
|
13184
13197
|
.map((attr) => {
|
|
13185
|
-
const
|
|
13198
|
+
const { name, value } = attr;
|
|
13199
|
+
const hasExpression = isExpression$1(value);
|
|
13200
|
+
// IDs/IDRefs must be handled dynamically at runtime due to synthetic shadow scoping.
|
|
13201
|
+
// Note that for backwards compat we only consider non-booleans to be dynamic IDs/IDRefs
|
|
13202
|
+
// TODO [#3658]: `disableSyntheticShadowSupport` should also disable this dynamic behavior
|
|
13203
|
+
const isIdOrIdRef = (name === 'id' || isIdReferencingAttribute(name)) &&
|
|
13204
|
+
(isExpression$1(value) || isStringLiteral(value));
|
|
13186
13205
|
return {
|
|
13187
13206
|
hasExpression,
|
|
13188
|
-
|
|
13189
|
-
|
|
13207
|
+
isIdOrIdRef,
|
|
13208
|
+
name,
|
|
13209
|
+
value: hasExpression || isIdOrIdRef
|
|
13190
13210
|
? codeGen.getStaticExpressionToken(attr)
|
|
13191
|
-
:
|
|
13211
|
+
: value.value,
|
|
13192
13212
|
};
|
|
13193
13213
|
})
|
|
13194
13214
|
.forEach(collector);
|
|
@@ -13601,7 +13621,7 @@ class CodeGen {
|
|
|
13601
13621
|
}
|
|
13602
13622
|
genClassExpression(value) {
|
|
13603
13623
|
let classExpression = this.bindExpression(value);
|
|
13604
|
-
const isClassNameObjectBindingEnabled = shared.isAPIFeatureEnabled(
|
|
13624
|
+
const isClassNameObjectBindingEnabled = shared.isAPIFeatureEnabled(10 /* APIFeature.TEMPLATE_CLASS_NAME_OBJECT_BINDING */, this.state.config.apiVersion);
|
|
13605
13625
|
if (isClassNameObjectBindingEnabled) {
|
|
13606
13626
|
classExpression = this.genNormalizeClassName(classExpression);
|
|
13607
13627
|
}
|
|
@@ -13715,6 +13735,7 @@ class CodeGen {
|
|
|
13715
13735
|
const callExpression$1 = callExpression(identifier$1, params);
|
|
13716
13736
|
// Check if the property actually exists before calling, to allow
|
|
13717
13737
|
// for older engines to work with newer compilers
|
|
13738
|
+
// TODO [#4313]: remove temporary logic to support v7 compiler + v6 engine
|
|
13718
13739
|
return fallbackForCompilerEngineVersionMismatch
|
|
13719
13740
|
? conditionalExpression(identifier$1, callExpression$1, fallbackForCompilerEngineVersionMismatch)
|
|
13720
13741
|
: callExpression$1;
|
|
@@ -13890,7 +13911,12 @@ class CodeGen {
|
|
|
13890
13911
|
const attributeExpressions = [];
|
|
13891
13912
|
for (const attribute of elm.attributes) {
|
|
13892
13913
|
const { name, value } = attribute;
|
|
13893
|
-
|
|
13914
|
+
// IDs/IDRefs must be handled dynamically at runtime due to synthetic shadow scoping.
|
|
13915
|
+
// Note that for backwards compat we only consider non-booleans to be dynamic IDs/IDRefs
|
|
13916
|
+
// TODO [#3658]: `disableSyntheticShadowSupport` should also disable this dynamic behavior
|
|
13917
|
+
const isIdOrIdRef = (name === 'id' || isIdReferencingAttribute(name)) &&
|
|
13918
|
+
(isExpression$1(value) || isStringLiteral(value));
|
|
13919
|
+
if (isExpression$1(value) || isIdOrIdRef) {
|
|
13894
13920
|
let partToken = '';
|
|
13895
13921
|
if (name === 'style') {
|
|
13896
13922
|
partToken = `${"s" /* STATIC_PART_TOKEN_ID.STYLE */}${partId}`;
|
|
@@ -13901,6 +13927,7 @@ class CodeGen {
|
|
|
13901
13927
|
databag.push(property$1(identifier('className'), this.genClassExpression(value)));
|
|
13902
13928
|
}
|
|
13903
13929
|
else {
|
|
13930
|
+
// non-class, non-style (i.e. generic attribute or ID/IDRef)
|
|
13904
13931
|
partToken = `${"a" /* STATIC_PART_TOKEN_ID.ATTRIBUTE */}${partId}:${name}`;
|
|
13905
13932
|
attributeExpressions.push(property$1(literal$1(name), bindAttributeExpression(attribute, elm, this, false)));
|
|
13906
13933
|
}
|
|
@@ -14402,6 +14429,7 @@ function transform(codeGen) {
|
|
|
14402
14429
|
if (attrName === 'id') {
|
|
14403
14430
|
return codeGen.genScopedId(attrValue.value);
|
|
14404
14431
|
}
|
|
14432
|
+
// `spellcheck` string values are specially handled to massage them into booleans.
|
|
14405
14433
|
if (attrName === 'spellcheck') {
|
|
14406
14434
|
return literal$1(attrValue.value.toLowerCase() !== 'false');
|
|
14407
14435
|
}
|
|
@@ -14668,5 +14696,5 @@ exports.default = compile;
|
|
|
14668
14696
|
exports.kebabcaseToCamelcase = kebabcaseToCamelcase;
|
|
14669
14697
|
exports.parse = parse;
|
|
14670
14698
|
exports.toPropertyName = toPropertyName;
|
|
14671
|
-
/** version: 7.0.
|
|
14699
|
+
/** version: 7.0.2 */
|
|
14672
14700
|
//# sourceMappingURL=index.cjs.js.map
|