@lwc/template-compiler 7.0.1-alpha.0 → 7.0.1

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 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) && isTemplateExpressionTextNodeValue(node.value);
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.
@@ -13009,7 +13012,6 @@ function isStaticNode(node, apiVersion) {
13009
13012
  name !== 'slot' &&
13010
13013
  // check for ScopedId
13011
13014
  name !== 'id' &&
13012
- name !== 'spellcheck' && // spellcheck is specially handled by the vnodes.
13013
13015
  !isIdReferencingAttribute(name) &&
13014
13016
  // svg href needs sanitization.
13015
13017
  !isSvgUseHref(nodeName, name, namespace) &&
@@ -13171,6 +13173,14 @@ function serializeAttrs(element, codeGen) {
13171
13173
  v += '${0}';
13172
13174
  }
13173
13175
  }
13176
+ // `spellcheck` string values are specially handled to massage them into booleans.
13177
+ // For backwards compat with non-static-optimized templates, we also treat any non-`"false"`
13178
+ // value other than the valueless format (e.g. `<div spellcheck>`) as `"true"`,
13179
+ // even though per MDN, the empty string and `"true"` are equivalent:
13180
+ // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/spellcheck
13181
+ if (name === 'spellcheck' && typeof v === 'string' && !hasExpression) {
13182
+ v = String(v.toLowerCase() !== 'false');
13183
+ }
13174
13184
  if (typeof v === 'string') {
13175
13185
  // Inject a placeholder where the staticPartId will go when an expression occurs.
13176
13186
  // This is only needed for SSR to inject the expression value during serialization.
@@ -13601,7 +13611,7 @@ class CodeGen {
13601
13611
  }
13602
13612
  genClassExpression(value) {
13603
13613
  let classExpression = this.bindExpression(value);
13604
- const isClassNameObjectBindingEnabled = shared.isAPIFeatureEnabled(11 /* APIFeature.TEMPLATE_CLASS_NAME_OBJECT_BINDING */, this.state.config.apiVersion);
13614
+ const isClassNameObjectBindingEnabled = shared.isAPIFeatureEnabled(10 /* APIFeature.TEMPLATE_CLASS_NAME_OBJECT_BINDING */, this.state.config.apiVersion);
13605
13615
  if (isClassNameObjectBindingEnabled) {
13606
13616
  classExpression = this.genNormalizeClassName(classExpression);
13607
13617
  }
@@ -13715,6 +13725,7 @@ class CodeGen {
13715
13725
  const callExpression$1 = callExpression(identifier$1, params);
13716
13726
  // Check if the property actually exists before calling, to allow
13717
13727
  // for older engines to work with newer compilers
13728
+ // TODO [#4313]: remove temporary logic to support v7 compiler + v6 engine
13718
13729
  return fallbackForCompilerEngineVersionMismatch
13719
13730
  ? conditionalExpression(identifier$1, callExpression$1, fallbackForCompilerEngineVersionMismatch)
13720
13731
  : callExpression$1;
@@ -14402,6 +14413,7 @@ function transform(codeGen) {
14402
14413
  if (attrName === 'id') {
14403
14414
  return codeGen.genScopedId(attrValue.value);
14404
14415
  }
14416
+ // `spellcheck` string values are specially handled to massage them into booleans.
14405
14417
  if (attrName === 'spellcheck') {
14406
14418
  return literal$1(attrValue.value.toLowerCase() !== 'false');
14407
14419
  }
@@ -14668,5 +14680,5 @@ exports.default = compile;
14668
14680
  exports.kebabcaseToCamelcase = kebabcaseToCamelcase;
14669
14681
  exports.parse = parse;
14670
14682
  exports.toPropertyName = toPropertyName;
14671
- /** version: 7.0.0 */
14683
+ /** version: 7.0.1 */
14672
14684
  //# sourceMappingURL=index.cjs.js.map