@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.js CHANGED
@@ -10888,6 +10888,138 @@ function validateExpressionAst(rootNode) {
10888
10888
  });
10889
10889
  }
10890
10890
 
10891
+ /*
10892
+ * Copyright (c) 2018, salesforce.com, inc.
10893
+ * All rights reserved.
10894
+ * SPDX-License-Identifier: MIT
10895
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
10896
+ */
10897
+ // https://262.ecma-international.org/12.0/#sec-keywords-and-reserved-words
10898
+ // prettier-ignore
10899
+ const REVERSED_KEYWORDS = new Set([
10900
+ // Reserved keywords
10901
+ 'await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 'delete',
10902
+ 'do', 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'import',
10903
+ 'in', 'instanceof', 'new', 'null', 'return', 'super', 'switch', 'this', 'throw', 'true', 'try',
10904
+ 'typeof', 'var', 'void', 'while', 'with', 'yield',
10905
+ // Strict mode only reserved keywords
10906
+ 'let', 'static', 'implements', 'interface', 'package', 'private', 'protected', 'public'
10907
+ ]);
10908
+ function isReservedES6Keyword(str) {
10909
+ return REVERSED_KEYWORDS.has(str);
10910
+ }
10911
+
10912
+ /*
10913
+ * Copyright (c) 2018, salesforce.com, inc.
10914
+ * All rights reserved.
10915
+ * SPDX-License-Identifier: MIT
10916
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
10917
+ */
10918
+ const EXPRESSION_SYMBOL_START = '{';
10919
+ const EXPRESSION_SYMBOL_END = '}';
10920
+ const POTENTIAL_EXPRESSION_RE = /^.?{.+}.*$/;
10921
+ const WHITESPACES_RE = /\s/;
10922
+ function isExpression(source) {
10923
+ // Issue #3418: Legacy behavior, previous regex treated "{}" attribute value as non expression
10924
+ return source[0] === '{' && source.slice(-1) === '}' && source.length > 2;
10925
+ }
10926
+ function isPotentialExpression(source) {
10927
+ return !!source.match(POTENTIAL_EXPRESSION_RE);
10928
+ }
10929
+ function validateExpression(node, config) {
10930
+ // TODO [#3370]: remove experimental template expression flag
10931
+ if (config.experimentalComplexExpressions) {
10932
+ return validateExpressionAst(node);
10933
+ }
10934
+ const isValidNode = isIdentifier(node) || isMemberExpression(node);
10935
+ invariant(isValidNode, ParserDiagnostics.INVALID_NODE, [node.type]);
10936
+ if (isMemberExpression(node)) {
10937
+ invariant(config.experimentalComputedMemberExpression || !node.computed, ParserDiagnostics.COMPUTED_PROPERTY_ACCESS_NOT_ALLOWED);
10938
+ const { object, property } = node;
10939
+ if (!isIdentifier(object)) {
10940
+ validateExpression(object, config);
10941
+ }
10942
+ if (!isIdentifier(property)) {
10943
+ validateExpression(property, config);
10944
+ }
10945
+ }
10946
+ }
10947
+ function validateSourceIsParsedExpression(source, parsedExpression) {
10948
+ if (parsedExpression.end === source.length - 1) {
10949
+ return;
10950
+ }
10951
+ let unclosedParenthesisCount = 0;
10952
+ for (let i = 0, n = parsedExpression.start; i < n; i++) {
10953
+ if (source[i] === '(') {
10954
+ unclosedParenthesisCount++;
10955
+ }
10956
+ }
10957
+ // source[source.length - 1] === '}', n = source.length - 1 is to avoid processing '}'.
10958
+ for (let i = parsedExpression.end, n = source.length - 1; i < n; i++) {
10959
+ const character = source[i];
10960
+ if (character === ')') {
10961
+ unclosedParenthesisCount--;
10962
+ }
10963
+ else if (character === ';') {
10964
+ // acorn parseExpressionAt will stop at the first ";", it may be that the expression is not
10965
+ // a multiple expression ({foo;}), but this is a case that we explicitly do not want to support.
10966
+ // in such case, let's fail with the same error as if it were a multiple expression.
10967
+ invariant(false, ParserDiagnostics.MULTIPLE_EXPRESSIONS);
10968
+ }
10969
+ else {
10970
+ invariant(WHITESPACES_RE.test(character), ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, ['Unexpected end of expression']);
10971
+ }
10972
+ }
10973
+ invariant(unclosedParenthesisCount === 0, ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, [
10974
+ 'Unexpected end of expression',
10975
+ ]);
10976
+ }
10977
+ function validatePreparsedJsExpressions(ctx) {
10978
+ ctx.preparsedJsExpressions?.forEach(({ parsedExpression, rawText }) => {
10979
+ const acornLoc = parsedExpression.loc;
10980
+ const parse5Loc = {
10981
+ startLine: acornLoc.start.line,
10982
+ startCol: acornLoc.start.column,
10983
+ startOffset: parsedExpression.start,
10984
+ endLine: acornLoc.end.line,
10985
+ endCol: acornLoc.end.column,
10986
+ endOffset: parsedExpression.end,
10987
+ };
10988
+ ctx.withErrorWrapping(() => {
10989
+ validateExpressionAst(parsedExpression);
10990
+ }, ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, sourceLocation(parse5Loc), (err) => `Invalid expression ${rawText} - ${err.message}`);
10991
+ });
10992
+ }
10993
+ function parseExpression(ctx, source, location) {
10994
+ const { ecmaVersion } = ctx;
10995
+ return ctx.withErrorWrapping(() => {
10996
+ const parsed = parseExpressionAt(source, 1, {
10997
+ ecmaVersion,
10998
+ // TODO [#3370]: remove experimental template expression flag
10999
+ allowAwaitOutsideFunction: ctx.config.experimentalComplexExpressions,
11000
+ });
11001
+ validateSourceIsParsedExpression(source, parsed);
11002
+ validateExpression(parsed, ctx.config);
11003
+ return { ...parsed, location };
11004
+ }, ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, location, (err) => `Invalid expression ${source} - ${err.message}`);
11005
+ }
11006
+ function parseIdentifier(ctx, source, location) {
11007
+ let isValid = true;
11008
+ isValid = isIdentifierStart(source.charCodeAt(0));
11009
+ for (let i = 1; i < source.length && isValid; i++) {
11010
+ isValid = isIdentifierChar(source.charCodeAt(i));
11011
+ }
11012
+ if (isValid && !isReservedES6Keyword(source)) {
11013
+ return {
11014
+ ...identifier(source),
11015
+ location,
11016
+ };
11017
+ }
11018
+ else {
11019
+ ctx.throwAtLocation(ParserDiagnostics.INVALID_IDENTIFIER, location, [source]);
11020
+ }
11021
+ }
11022
+
10891
11023
  /*
10892
11024
  * Copyright (c) 2023, salesforce.com, inc.
10893
11025
  * All rights reserved.
@@ -11045,17 +11177,19 @@ class TemplateHtmlTokenizer extends Tokenizer {
11045
11177
  }
11046
11178
  }
11047
11179
  }
11048
- function isTemplateExpressionTextNodeValue(value) {
11049
- return value?.[0] === '{';
11050
- }
11051
11180
  function isTemplateElement(node) {
11052
11181
  return node.nodeName === 'template';
11053
11182
  }
11054
11183
  function isTextNode(node) {
11055
11184
  return node?.nodeName === '#text';
11056
11185
  }
11057
- function isTemplateExpressionTextNode(node) {
11058
- return isTextNode(node) && isTemplateExpressionTextNodeValue(node.value);
11186
+ function isTemplateExpressionTextNode(node, html) {
11187
+ return (isTextNode(node) &&
11188
+ isTemplateExpressionTextNodeValue(node.value, node.sourceCodeLocation.startOffset, html));
11189
+ }
11190
+ function isTemplateExpressionTextNodeValue(value, startOffset, html) {
11191
+ return (value.startsWith(EXPRESSION_SYMBOL_START) &&
11192
+ html.startsWith(EXPRESSION_SYMBOL_START, startOffset));
11059
11193
  }
11060
11194
  /**
11061
11195
  * This class extends `parse5`'s internal parser. The heavy lifting is
@@ -11077,11 +11211,12 @@ class TemplateHtmlParser extends Parser {
11077
11211
  _insertCharacters(token) {
11078
11212
  const parentNode = this.openElements.current;
11079
11213
  const previousPeer = parentNode.childNodes.at(-1);
11214
+ const html = this.tokenizer.preprocessor.html;
11080
11215
  if (
11081
11216
  // If we're not dealing with a template expression...
11082
- !isTemplateExpressionTextNodeValue(token.chars) &&
11217
+ !isTemplateExpressionTextNodeValue(token.chars, token.location.startOffset, html) &&
11083
11218
  // ... and the previous node wasn't a text-node-template-expression...
11084
- !isTemplateExpressionTextNode(previousPeer)) {
11219
+ !isTemplateExpressionTextNode(previousPeer, html)) {
11085
11220
  // ... concatenate the provided characters with the previous token's characters.
11086
11221
  return super._insertCharacters(token);
11087
11222
  }
@@ -11201,138 +11336,6 @@ function decodeTextContent(source) {
11201
11336
  return he.decode(source);
11202
11337
  }
11203
11338
 
11204
- /*
11205
- * Copyright (c) 2018, salesforce.com, inc.
11206
- * All rights reserved.
11207
- * SPDX-License-Identifier: MIT
11208
- * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
11209
- */
11210
- // https://262.ecma-international.org/12.0/#sec-keywords-and-reserved-words
11211
- // prettier-ignore
11212
- const REVERSED_KEYWORDS = new Set([
11213
- // Reserved keywords
11214
- 'await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 'delete',
11215
- 'do', 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'import',
11216
- 'in', 'instanceof', 'new', 'null', 'return', 'super', 'switch', 'this', 'throw', 'true', 'try',
11217
- 'typeof', 'var', 'void', 'while', 'with', 'yield',
11218
- // Strict mode only reserved keywords
11219
- 'let', 'static', 'implements', 'interface', 'package', 'private', 'protected', 'public'
11220
- ]);
11221
- function isReservedES6Keyword(str) {
11222
- return REVERSED_KEYWORDS.has(str);
11223
- }
11224
-
11225
- /*
11226
- * Copyright (c) 2018, salesforce.com, inc.
11227
- * All rights reserved.
11228
- * SPDX-License-Identifier: MIT
11229
- * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
11230
- */
11231
- const EXPRESSION_SYMBOL_START = '{';
11232
- const EXPRESSION_SYMBOL_END = '}';
11233
- const POTENTIAL_EXPRESSION_RE = /^.?{.+}.*$/;
11234
- const WHITESPACES_RE = /\s/;
11235
- function isExpression(source) {
11236
- // Issue #3418: Legacy behavior, previous regex treated "{}" attribute value as non expression
11237
- return source[0] === '{' && source.slice(-1) === '}' && source.length > 2;
11238
- }
11239
- function isPotentialExpression(source) {
11240
- return !!source.match(POTENTIAL_EXPRESSION_RE);
11241
- }
11242
- function validateExpression(node, config) {
11243
- // TODO [#3370]: remove experimental template expression flag
11244
- if (config.experimentalComplexExpressions) {
11245
- return validateExpressionAst(node);
11246
- }
11247
- const isValidNode = isIdentifier(node) || isMemberExpression(node);
11248
- invariant(isValidNode, ParserDiagnostics.INVALID_NODE, [node.type]);
11249
- if (isMemberExpression(node)) {
11250
- invariant(config.experimentalComputedMemberExpression || !node.computed, ParserDiagnostics.COMPUTED_PROPERTY_ACCESS_NOT_ALLOWED);
11251
- const { object, property } = node;
11252
- if (!isIdentifier(object)) {
11253
- validateExpression(object, config);
11254
- }
11255
- if (!isIdentifier(property)) {
11256
- validateExpression(property, config);
11257
- }
11258
- }
11259
- }
11260
- function validateSourceIsParsedExpression(source, parsedExpression) {
11261
- if (parsedExpression.end === source.length - 1) {
11262
- return;
11263
- }
11264
- let unclosedParenthesisCount = 0;
11265
- for (let i = 0, n = parsedExpression.start; i < n; i++) {
11266
- if (source[i] === '(') {
11267
- unclosedParenthesisCount++;
11268
- }
11269
- }
11270
- // source[source.length - 1] === '}', n = source.length - 1 is to avoid processing '}'.
11271
- for (let i = parsedExpression.end, n = source.length - 1; i < n; i++) {
11272
- const character = source[i];
11273
- if (character === ')') {
11274
- unclosedParenthesisCount--;
11275
- }
11276
- else if (character === ';') {
11277
- // acorn parseExpressionAt will stop at the first ";", it may be that the expression is not
11278
- // a multiple expression ({foo;}), but this is a case that we explicitly do not want to support.
11279
- // in such case, let's fail with the same error as if it were a multiple expression.
11280
- invariant(false, ParserDiagnostics.MULTIPLE_EXPRESSIONS);
11281
- }
11282
- else {
11283
- invariant(WHITESPACES_RE.test(character), ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, ['Unexpected end of expression']);
11284
- }
11285
- }
11286
- invariant(unclosedParenthesisCount === 0, ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, [
11287
- 'Unexpected end of expression',
11288
- ]);
11289
- }
11290
- function validatePreparsedJsExpressions(ctx) {
11291
- ctx.preparsedJsExpressions?.forEach(({ parsedExpression, rawText }) => {
11292
- const acornLoc = parsedExpression.loc;
11293
- const parse5Loc = {
11294
- startLine: acornLoc.start.line,
11295
- startCol: acornLoc.start.column,
11296
- startOffset: parsedExpression.start,
11297
- endLine: acornLoc.end.line,
11298
- endCol: acornLoc.end.column,
11299
- endOffset: parsedExpression.end,
11300
- };
11301
- ctx.withErrorWrapping(() => {
11302
- validateExpressionAst(parsedExpression);
11303
- }, ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, sourceLocation(parse5Loc), (err) => `Invalid expression ${rawText} - ${err.message}`);
11304
- });
11305
- }
11306
- function parseExpression(ctx, source, location) {
11307
- const { ecmaVersion } = ctx;
11308
- return ctx.withErrorWrapping(() => {
11309
- const parsed = parseExpressionAt(source, 1, {
11310
- ecmaVersion,
11311
- // TODO [#3370]: remove experimental template expression flag
11312
- allowAwaitOutsideFunction: ctx.config.experimentalComplexExpressions,
11313
- });
11314
- validateSourceIsParsedExpression(source, parsed);
11315
- validateExpression(parsed, ctx.config);
11316
- return { ...parsed, location };
11317
- }, ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, location, (err) => `Invalid expression ${source} - ${err.message}`);
11318
- }
11319
- function parseIdentifier(ctx, source, location) {
11320
- let isValid = true;
11321
- isValid = isIdentifierStart(source.charCodeAt(0));
11322
- for (let i = 1; i < source.length && isValid; i++) {
11323
- isValid = isIdentifierChar(source.charCodeAt(i));
11324
- }
11325
- if (isValid && !isReservedES6Keyword(source)) {
11326
- return {
11327
- ...identifier(source),
11328
- location,
11329
- };
11330
- }
11331
- else {
11332
- ctx.throwAtLocation(ParserDiagnostics.INVALID_IDENTIFIER, location, [source]);
11333
- }
11334
- }
11335
-
11336
11339
  /*
11337
11340
  * Copyright (c) 2018, salesforce.com, inc.
11338
11341
  * All rights reserved.
@@ -12985,7 +12988,6 @@ function isStaticNode(node, apiVersion) {
12985
12988
  name !== 'slot' &&
12986
12989
  // check for ScopedId
12987
12990
  name !== 'id' &&
12988
- name !== 'spellcheck' && // spellcheck is specially handled by the vnodes.
12989
12991
  !isIdReferencingAttribute(name) &&
12990
12992
  // svg href needs sanitization.
12991
12993
  !isSvgUseHref(nodeName, name, namespace) &&
@@ -13147,6 +13149,14 @@ function serializeAttrs(element, codeGen) {
13147
13149
  v += '${0}';
13148
13150
  }
13149
13151
  }
13152
+ // `spellcheck` string values are specially handled to massage them into booleans.
13153
+ // For backwards compat with non-static-optimized templates, we also treat any non-`"false"`
13154
+ // value other than the valueless format (e.g. `<div spellcheck>`) as `"true"`,
13155
+ // even though per MDN, the empty string and `"true"` are equivalent:
13156
+ // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/spellcheck
13157
+ if (name === 'spellcheck' && typeof v === 'string' && !hasExpression) {
13158
+ v = String(v.toLowerCase() !== 'false');
13159
+ }
13150
13160
  if (typeof v === 'string') {
13151
13161
  // Inject a placeholder where the staticPartId will go when an expression occurs.
13152
13162
  // This is only needed for SSR to inject the expression value during serialization.
@@ -13577,7 +13587,7 @@ class CodeGen {
13577
13587
  }
13578
13588
  genClassExpression(value) {
13579
13589
  let classExpression = this.bindExpression(value);
13580
- const isClassNameObjectBindingEnabled = isAPIFeatureEnabled(11 /* APIFeature.TEMPLATE_CLASS_NAME_OBJECT_BINDING */, this.state.config.apiVersion);
13590
+ const isClassNameObjectBindingEnabled = isAPIFeatureEnabled(10 /* APIFeature.TEMPLATE_CLASS_NAME_OBJECT_BINDING */, this.state.config.apiVersion);
13581
13591
  if (isClassNameObjectBindingEnabled) {
13582
13592
  classExpression = this.genNormalizeClassName(classExpression);
13583
13593
  }
@@ -13691,6 +13701,7 @@ class CodeGen {
13691
13701
  const callExpression$1 = callExpression(identifier$1, params);
13692
13702
  // Check if the property actually exists before calling, to allow
13693
13703
  // for older engines to work with newer compilers
13704
+ // TODO [#4313]: remove temporary logic to support v7 compiler + v6 engine
13694
13705
  return fallbackForCompilerEngineVersionMismatch
13695
13706
  ? conditionalExpression(identifier$1, callExpression$1, fallbackForCompilerEngineVersionMismatch)
13696
13707
  : callExpression$1;
@@ -14378,6 +14389,7 @@ function transform(codeGen) {
14378
14389
  if (attrName === 'id') {
14379
14390
  return codeGen.genScopedId(attrValue.value);
14380
14391
  }
14392
+ // `spellcheck` string values are specially handled to massage them into booleans.
14381
14393
  if (attrName === 'spellcheck') {
14382
14394
  return literal$1(attrValue.value.toLowerCase() !== 'false');
14383
14395
  }
@@ -14640,5 +14652,5 @@ function compile(source, filename, config) {
14640
14652
  }
14641
14653
 
14642
14654
  export { ElementDirectiveName, LWCDirectiveDomMode, LWCDirectiveRenderMode, LwcTagName, RootDirectiveName, TemplateDirectiveName, compile, compile as default, kebabcaseToCamelcase, parse, toPropertyName };
14643
- /** version: 7.0.0 */
14655
+ /** version: 7.0.1 */
14644
14656
  //# sourceMappingURL=index.js.map