@lwc/template-compiler 7.0.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.
@@ -69,7 +69,7 @@ export default class CodeGen {
69
69
  genScopedId(id: string | t.Expression): t.CallExpression;
70
70
  genScopedFragId(id: string | t.Expression): t.CallExpression;
71
71
  genClassExpression(value: Expression): import("estree").Expression;
72
- genNormalizeClassName(className: t.Expression): t.CallExpression;
72
+ genNormalizeClassName(className: t.Expression): t.CallExpression | t.ConditionalExpression;
73
73
  /**
74
74
  * Generates childs vnodes when slot content is static.
75
75
  * @param slotName
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,14 +11201,19 @@ class TemplateHtmlTokenizer extends Tokenizer {
11069
11201
  }
11070
11202
  }
11071
11203
  }
11072
- function isTemplateExpressionTextNodeValue(value) {
11073
- return value?.[0] === '{';
11204
+ function isTemplateElement(node) {
11205
+ return node.nodeName === 'template';
11074
11206
  }
11075
11207
  function isTextNode(node) {
11076
11208
  return node?.nodeName === '#text';
11077
11209
  }
11078
- function isTemplateExpressionTextNode(node) {
11079
- 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));
11080
11217
  }
11081
11218
  /**
11082
11219
  * This class extends `parse5`'s internal parser. The heavy lifting is
@@ -11098,11 +11235,12 @@ class TemplateHtmlParser extends Parser {
11098
11235
  _insertCharacters(token) {
11099
11236
  const parentNode = this.openElements.current;
11100
11237
  const previousPeer = parentNode.childNodes.at(-1);
11238
+ const html = this.tokenizer.preprocessor.html;
11101
11239
  if (
11102
11240
  // If we're not dealing with a template expression...
11103
- !isTemplateExpressionTextNodeValue(token.chars) &&
11241
+ !isTemplateExpressionTextNodeValue(token.chars, token.location.startOffset, html) &&
11104
11242
  // ... and the previous node wasn't a text-node-template-expression...
11105
- !isTemplateExpressionTextNode(previousPeer)) {
11243
+ !isTemplateExpressionTextNode(previousPeer, html)) {
11106
11244
  // ... concatenate the provided characters with the previous token's characters.
11107
11245
  return super._insertCharacters(token);
11108
11246
  }
@@ -11112,7 +11250,12 @@ class TemplateHtmlParser extends Parser {
11112
11250
  sourceCodeLocation: token.location ? { ...token.location } : null,
11113
11251
  parentNode,
11114
11252
  };
11115
- parentNode.childNodes.push(textNode);
11253
+ if (isTemplateElement(parentNode)) {
11254
+ parentNode.content.childNodes.push(textNode);
11255
+ }
11256
+ else {
11257
+ parentNode.childNodes.push(textNode);
11258
+ }
11116
11259
  }
11117
11260
  }
11118
11261
  /**
@@ -11217,138 +11360,6 @@ function decodeTextContent(source) {
11217
11360
  return he__namespace.decode(source);
11218
11361
  }
11219
11362
 
11220
- /*
11221
- * Copyright (c) 2018, salesforce.com, inc.
11222
- * All rights reserved.
11223
- * SPDX-License-Identifier: MIT
11224
- * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
11225
- */
11226
- // https://262.ecma-international.org/12.0/#sec-keywords-and-reserved-words
11227
- // prettier-ignore
11228
- const REVERSED_KEYWORDS = new Set([
11229
- // Reserved keywords
11230
- 'await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', 'default', 'delete',
11231
- 'do', 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 'function', 'if', 'import',
11232
- 'in', 'instanceof', 'new', 'null', 'return', 'super', 'switch', 'this', 'throw', 'true', 'try',
11233
- 'typeof', 'var', 'void', 'while', 'with', 'yield',
11234
- // Strict mode only reserved keywords
11235
- 'let', 'static', 'implements', 'interface', 'package', 'private', 'protected', 'public'
11236
- ]);
11237
- function isReservedES6Keyword(str) {
11238
- return REVERSED_KEYWORDS.has(str);
11239
- }
11240
-
11241
- /*
11242
- * Copyright (c) 2018, salesforce.com, inc.
11243
- * All rights reserved.
11244
- * SPDX-License-Identifier: MIT
11245
- * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
11246
- */
11247
- const EXPRESSION_SYMBOL_START = '{';
11248
- const EXPRESSION_SYMBOL_END = '}';
11249
- const POTENTIAL_EXPRESSION_RE = /^.?{.+}.*$/;
11250
- const WHITESPACES_RE = /\s/;
11251
- function isExpression(source) {
11252
- // Issue #3418: Legacy behavior, previous regex treated "{}" attribute value as non expression
11253
- return source[0] === '{' && source.slice(-1) === '}' && source.length > 2;
11254
- }
11255
- function isPotentialExpression(source) {
11256
- return !!source.match(POTENTIAL_EXPRESSION_RE);
11257
- }
11258
- function validateExpression(node, config) {
11259
- // TODO [#3370]: remove experimental template expression flag
11260
- if (config.experimentalComplexExpressions) {
11261
- return validateExpressionAst(node);
11262
- }
11263
- const isValidNode = isIdentifier(node) || isMemberExpression(node);
11264
- errors.invariant(isValidNode, errors.ParserDiagnostics.INVALID_NODE, [node.type]);
11265
- if (isMemberExpression(node)) {
11266
- errors.invariant(config.experimentalComputedMemberExpression || !node.computed, errors.ParserDiagnostics.COMPUTED_PROPERTY_ACCESS_NOT_ALLOWED);
11267
- const { object, property } = node;
11268
- if (!isIdentifier(object)) {
11269
- validateExpression(object, config);
11270
- }
11271
- if (!isIdentifier(property)) {
11272
- validateExpression(property, config);
11273
- }
11274
- }
11275
- }
11276
- function validateSourceIsParsedExpression(source, parsedExpression) {
11277
- if (parsedExpression.end === source.length - 1) {
11278
- return;
11279
- }
11280
- let unclosedParenthesisCount = 0;
11281
- for (let i = 0, n = parsedExpression.start; i < n; i++) {
11282
- if (source[i] === '(') {
11283
- unclosedParenthesisCount++;
11284
- }
11285
- }
11286
- // source[source.length - 1] === '}', n = source.length - 1 is to avoid processing '}'.
11287
- for (let i = parsedExpression.end, n = source.length - 1; i < n; i++) {
11288
- const character = source[i];
11289
- if (character === ')') {
11290
- unclosedParenthesisCount--;
11291
- }
11292
- else if (character === ';') {
11293
- // acorn parseExpressionAt will stop at the first ";", it may be that the expression is not
11294
- // a multiple expression ({foo;}), but this is a case that we explicitly do not want to support.
11295
- // in such case, let's fail with the same error as if it were a multiple expression.
11296
- errors.invariant(false, errors.ParserDiagnostics.MULTIPLE_EXPRESSIONS);
11297
- }
11298
- else {
11299
- errors.invariant(WHITESPACES_RE.test(character), errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, ['Unexpected end of expression']);
11300
- }
11301
- }
11302
- errors.invariant(unclosedParenthesisCount === 0, errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, [
11303
- 'Unexpected end of expression',
11304
- ]);
11305
- }
11306
- function validatePreparsedJsExpressions(ctx) {
11307
- ctx.preparsedJsExpressions?.forEach(({ parsedExpression, rawText }) => {
11308
- const acornLoc = parsedExpression.loc;
11309
- const parse5Loc = {
11310
- startLine: acornLoc.start.line,
11311
- startCol: acornLoc.start.column,
11312
- startOffset: parsedExpression.start,
11313
- endLine: acornLoc.end.line,
11314
- endCol: acornLoc.end.column,
11315
- endOffset: parsedExpression.end,
11316
- };
11317
- ctx.withErrorWrapping(() => {
11318
- validateExpressionAst(parsedExpression);
11319
- }, errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, sourceLocation(parse5Loc), (err) => `Invalid expression ${rawText} - ${err.message}`);
11320
- });
11321
- }
11322
- function parseExpression(ctx, source, location) {
11323
- const { ecmaVersion } = ctx;
11324
- return ctx.withErrorWrapping(() => {
11325
- const parsed = acorn.parseExpressionAt(source, 1, {
11326
- ecmaVersion,
11327
- // TODO [#3370]: remove experimental template expression flag
11328
- allowAwaitOutsideFunction: ctx.config.experimentalComplexExpressions,
11329
- });
11330
- validateSourceIsParsedExpression(source, parsed);
11331
- validateExpression(parsed, ctx.config);
11332
- return { ...parsed, location };
11333
- }, errors.ParserDiagnostics.TEMPLATE_EXPRESSION_PARSING_ERROR, location, (err) => `Invalid expression ${source} - ${err.message}`);
11334
- }
11335
- function parseIdentifier(ctx, source, location) {
11336
- let isValid = true;
11337
- isValid = acorn.isIdentifierStart(source.charCodeAt(0));
11338
- for (let i = 1; i < source.length && isValid; i++) {
11339
- isValid = acorn.isIdentifierChar(source.charCodeAt(i));
11340
- }
11341
- if (isValid && !isReservedES6Keyword(source)) {
11342
- return {
11343
- ...identifier(source),
11344
- location,
11345
- };
11346
- }
11347
- else {
11348
- ctx.throwAtLocation(errors.ParserDiagnostics.INVALID_IDENTIFIER, location, [source]);
11349
- }
11350
- }
11351
-
11352
11363
  /*
11353
11364
  * Copyright (c) 2018, salesforce.com, inc.
11354
11365
  * All rights reserved.
@@ -13001,7 +13012,6 @@ function isStaticNode(node, apiVersion) {
13001
13012
  name !== 'slot' &&
13002
13013
  // check for ScopedId
13003
13014
  name !== 'id' &&
13004
- name !== 'spellcheck' && // spellcheck is specially handled by the vnodes.
13005
13015
  !isIdReferencingAttribute(name) &&
13006
13016
  // svg href needs sanitization.
13007
13017
  !isSvgUseHref(nodeName, name, namespace) &&
@@ -13163,6 +13173,14 @@ function serializeAttrs(element, codeGen) {
13163
13173
  v += '${0}';
13164
13174
  }
13165
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
+ }
13166
13184
  if (typeof v === 'string') {
13167
13185
  // Inject a placeholder where the staticPartId will go when an expression occurs.
13168
13186
  // This is only needed for SSR to inject the expression value during serialization.
@@ -13593,14 +13611,14 @@ class CodeGen {
13593
13611
  }
13594
13612
  genClassExpression(value) {
13595
13613
  let classExpression = this.bindExpression(value);
13596
- 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);
13597
13615
  if (isClassNameObjectBindingEnabled) {
13598
13616
  classExpression = this.genNormalizeClassName(classExpression);
13599
13617
  }
13600
13618
  return classExpression;
13601
13619
  }
13602
13620
  genNormalizeClassName(className) {
13603
- return this._renderApiCall(RENDER_APIS.normalizeClassName, [className]);
13621
+ return this._renderApiCall(RENDER_APIS.normalizeClassName, [className], className);
13604
13622
  }
13605
13623
  /**
13606
13624
  * Generates childs vnodes when slot content is static.
@@ -13698,13 +13716,19 @@ class CodeGen {
13698
13716
  // usage of the lwc:inner-html, and in an iteration, usages are dynamically generated.
13699
13717
  return conditionalExpression(binaryExpression('!==', memberExpression(identifier(TEMPLATE_PARAMS.CONTEXT), identifier(`_rawHtml$${instance}`)), assignmentExpression('=', memberExpression(identifier(TEMPLATE_PARAMS.CONTEXT), identifier(`_rawHtml$${instance}`)), expr)), assignmentExpression('=', memberExpression(identifier(TEMPLATE_PARAMS.CONTEXT), identifier(`_sanitizedHtml$${instance}`)), this.genSanitizeHtmlContent(expr)), memberExpression(identifier(TEMPLATE_PARAMS.CONTEXT), identifier(`_sanitizedHtml$${instance}`)));
13700
13718
  }
13701
- _renderApiCall(primitive, params) {
13719
+ _renderApiCall(primitive, params, fallbackForCompilerEngineVersionMismatch) {
13702
13720
  const { name, alias } = primitive;
13703
13721
  let identifier$1 = this.usedApis[name];
13704
13722
  if (!identifier$1) {
13705
13723
  identifier$1 = this.usedApis[name] = identifier(alias);
13706
13724
  }
13707
- return callExpression(identifier$1, params);
13725
+ const callExpression$1 = callExpression(identifier$1, params);
13726
+ // Check if the property actually exists before calling, to allow
13727
+ // for older engines to work with newer compilers
13728
+ // TODO [#4313]: remove temporary logic to support v7 compiler + v6 engine
13729
+ return fallbackForCompilerEngineVersionMismatch
13730
+ ? conditionalExpression(identifier$1, callExpression$1, fallbackForCompilerEngineVersionMismatch)
13731
+ : callExpression$1;
13708
13732
  }
13709
13733
  beginScope() {
13710
13734
  this.scope = this.createScope(this.scope);
@@ -14389,6 +14413,7 @@ function transform(codeGen) {
14389
14413
  if (attrName === 'id') {
14390
14414
  return codeGen.genScopedId(attrValue.value);
14391
14415
  }
14416
+ // `spellcheck` string values are specially handled to massage them into booleans.
14392
14417
  if (attrName === 'spellcheck') {
14393
14418
  return literal$1(attrValue.value.toLowerCase() !== 'false');
14394
14419
  }
@@ -14655,5 +14680,5 @@ exports.default = compile;
14655
14680
  exports.kebabcaseToCamelcase = kebabcaseToCamelcase;
14656
14681
  exports.parse = parse;
14657
14682
  exports.toPropertyName = toPropertyName;
14658
- /** version: 7.0.0 */
14683
+ /** version: 7.0.1 */
14659
14684
  //# sourceMappingURL=index.cjs.js.map