@lwc/template-compiler 7.0.3 → 7.0.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/index.cjs.js CHANGED
@@ -12996,7 +12996,7 @@ const STATIC_ELEMENT_WITH_DYNAMIC_TEXT_SET = new WeakSet();
12996
12996
  const STATIC_ELEMENT_TO_DYNAMIC_TEXT_CHILDREN_CACHE = new WeakMap();
12997
12997
  function isStaticNode(node, apiVersion) {
12998
12998
  let result = true;
12999
- const { name: nodeName, namespace = '', attributes, directives, properties } = node;
12999
+ const { namespace = '', attributes, directives, properties } = node;
13000
13000
  // SVG is excluded from static content optimization in older API versions due to issues with case sensitivity
13001
13001
  // in CSS scope tokens. See https://github.com/salesforce/lwc/issues/3313
13002
13002
  if (!shared.isAPIFeatureEnabled(0 /* APIFeature.LOWERCASE_SCOPE_TOKENS */, apiVersion) &&
@@ -13007,20 +13007,10 @@ function isStaticNode(node, apiVersion) {
13007
13007
  result &&= isElement(node);
13008
13008
  // all attrs are static-safe
13009
13009
  // the criteria to determine safety can be found in computeAttrValue
13010
- result &&= attributes.every(({ name, value }) => {
13011
- const isStaticSafeLiteral = isLiteral(value) &&
13012
- name !== 'slot' &&
13013
- // svg href needs sanitization.
13014
- !isSvgUseHref(nodeName, name, namespace) &&
13015
- // Check for ScopedFragId
13016
- !(isAllowedFragOnlyUrlsXHTML(nodeName, name, namespace) &&
13017
- isFragmentOnlyUrl(value.value));
13018
- const isStaticSafeExpression = isExpression$1(value) &&
13019
- name !== 'slot' &&
13020
- // TODO [#3624]: Revisit whether svgs can be included in static content optimization
13021
- // svg href needs sanitization.
13022
- !isSvgUseHref(nodeName, name, namespace);
13023
- return isStaticSafeLiteral || isStaticSafeExpression;
13010
+ result &&= attributes.every(({ name }) => {
13011
+ // Slots are not safe because the VDOM handles them specially in synthetic shadow and light DOM mode
13012
+ // TODO [#4351]: `disableSyntheticShadowSupport` should allow slots to be static-optimized
13013
+ return name !== 'slot';
13024
13014
  });
13025
13015
  // all directives are static-safe
13026
13016
  result &&= !directives.some((directive) => !STATIC_SAFE_DIRECTIVES.has(directive.name));
@@ -13158,7 +13148,7 @@ function serializeAttrs(element, codeGen) {
13158
13148
  */
13159
13149
  const attrs = [];
13160
13150
  let hasClassAttr = false;
13161
- const collector = ({ name, value, hasExpression, isIdOrIdRef, }) => {
13151
+ const collector = ({ name, value, hasExpression, hasIdOrIdRef, hasSvgUseHref, hasScopedFragmentRef, }) => {
13162
13152
  let v = typeof value === 'string' ? templateStringEscape(value) : value;
13163
13153
  if (name === 'class') {
13164
13154
  hasClassAttr = true;
@@ -13184,7 +13174,7 @@ function serializeAttrs(element, codeGen) {
13184
13174
  // Note that, to maintain backwards compatibility with the non-static output, we treat the valueless
13185
13175
  // "boolean" format (e.g. `<div id>`) as the empty string, which is semantically equivalent.
13186
13176
  // TODO [#3658]: `disableSyntheticShadowSupport` should also disable this dynamic behavior
13187
- const needsPlaceholder = hasExpression || isIdOrIdRef;
13177
+ const needsPlaceholder = hasExpression || hasIdOrIdRef || hasSvgUseHref || hasScopedFragmentRef;
13188
13178
  // Inject a placeholder where the staticPartId will go when an expression occurs.
13189
13179
  // This is only needed for SSR to inject the expression value during serialization.
13190
13180
  attrs.push(needsPlaceholder ? `\${"${v}"}` : ` ${name}="${shared.htmlEscape(v, true)}"`);
@@ -13200,13 +13190,22 @@ function serializeAttrs(element, codeGen) {
13200
13190
  // IDs/IDRefs must be handled dynamically at runtime due to synthetic shadow scoping.
13201
13191
  // Note that for backwards compat we only consider non-booleans to be dynamic IDs/IDRefs
13202
13192
  // TODO [#3658]: `disableSyntheticShadowSupport` should also disable this dynamic behavior
13203
- const isIdOrIdRef = (name === 'id' || isIdReferencingAttribute(name)) &&
13204
- (isExpression$1(value) || isStringLiteral(value));
13193
+ const hasIdOrIdRef = (name === 'id' || isIdReferencingAttribute(name)) && !isBooleanLiteral(value);
13194
+ // For boolean literals (e.g. `<use xlink:href>`), there is no reason to sanitize since it's empty
13195
+ const hasSvgUseHref = isSvgUseHref(element.name, name, element.namespace) && !isBooleanLiteral(value);
13196
+ // `<a href="#foo">` and `<area href="#foo">` must be dynamic due to synthetic shadow scoping
13197
+ // Note this only applies if there is an `id` attribute somewhere in the template
13198
+ const hasScopedFragmentRef = codeGen.scopeFragmentId &&
13199
+ isStringLiteral(value) &&
13200
+ isAllowedFragOnlyUrlsXHTML(element.name, name, element.namespace) &&
13201
+ isFragmentOnlyUrl(value.value);
13205
13202
  return {
13206
13203
  hasExpression,
13207
- isIdOrIdRef,
13204
+ hasIdOrIdRef,
13205
+ hasSvgUseHref,
13206
+ hasScopedFragmentRef,
13208
13207
  name,
13209
- value: hasExpression || isIdOrIdRef
13208
+ value: hasExpression || hasIdOrIdRef || hasSvgUseHref || hasScopedFragmentRef
13210
13209
  ? codeGen.getStaticExpressionToken(attr)
13211
13210
  : value.value,
13212
13211
  };
@@ -13462,16 +13461,21 @@ function bindAttributeExpression(attr, element, codeGen, addLegacySanitizationHo
13462
13461
  return codeGen.genScopedFragId(expression);
13463
13462
  }
13464
13463
  if (isSvgUseHref(elmName, attrName, namespace)) {
13464
+ // Apply the fragment id scoping transformation if necessary.
13465
+ // This scoping can be skipped if the value is a string literal that doesn't start with a "#"
13466
+ const value = isStringLiteral(attrValue) && !isFragmentOnlyUrl(attrValue.value)
13467
+ ? literal$1(attrValue.value)
13468
+ : codeGen.genScopedFragId(expression);
13465
13469
  if (addLegacySanitizationHook) {
13466
13470
  codeGen.usedLwcApis.add('sanitizeAttribute');
13467
13471
  return callExpression(identifier('sanitizeAttribute'), [
13468
13472
  literal$1(elmName),
13469
13473
  literal$1(namespace),
13470
13474
  literal$1(attrName),
13471
- codeGen.genScopedFragId(expression),
13475
+ value,
13472
13476
  ]);
13473
13477
  }
13474
- return codeGen.genScopedFragId(expression);
13478
+ return value;
13475
13479
  }
13476
13480
  return expression;
13477
13481
  }
@@ -13877,13 +13881,13 @@ class CodeGen {
13877
13881
  };
13878
13882
  // Depth-first traversal. We assign a partId to each element, which is an integer based on traversal order.
13879
13883
  while (stack.length > 0) {
13880
- const current = stack.shift();
13884
+ const currentNode = stack.shift();
13881
13885
  // Skip comment nodes in parts count, as they will be stripped in production, unless when `lwc:preserve-comments` is enabled
13882
- if (isContiguousText(current) || !isComment(current) || this.preserveComments) {
13886
+ if (isContiguousText(currentNode) || !isComment(currentNode) || this.preserveComments) {
13883
13887
  partId++;
13884
13888
  }
13885
- if (isContiguousText(current)) {
13886
- const textNodes = current;
13889
+ if (isContiguousText(currentNode)) {
13890
+ const textNodes = currentNode;
13887
13891
  if (hasDynamicText(textNodes)) {
13888
13892
  const partToken = `${"t" /* STATIC_PART_TOKEN_ID.TEXT */}${partId}`;
13889
13893
  // Use the first text node as the key.
@@ -13893,30 +13897,38 @@ class CodeGen {
13893
13897
  setPartIdText(concatenatedText);
13894
13898
  }
13895
13899
  }
13896
- else if (isElement(current)) {
13897
- const elm = current;
13900
+ else if (isElement(currentNode)) {
13898
13901
  const databag = [];
13899
13902
  // has event listeners
13900
- if (elm.listeners.length) {
13901
- databag.push(this.genEventListeners(elm.listeners));
13903
+ if (currentNode.listeners.length) {
13904
+ databag.push(this.genEventListeners(currentNode.listeners));
13902
13905
  }
13903
13906
  // See STATIC_SAFE_DIRECTIVES for what's allowed here.
13904
13907
  // Also note that we don't generate the 'key' here, because we only support it at the top level
13905
13908
  // directly passed into the `api_static_fragment` function, not as a part.
13906
- for (const directive of elm.directives) {
13909
+ for (const directive of currentNode.directives) {
13907
13910
  if (directive.name === 'Ref') {
13908
13911
  databag.push(this.genRef(directive));
13909
13912
  }
13910
13913
  }
13911
13914
  const attributeExpressions = [];
13912
- for (const attribute of elm.attributes) {
13915
+ for (const attribute of currentNode.attributes) {
13913
13916
  const { name, value } = attribute;
13914
13917
  // IDs/IDRefs must be handled dynamically at runtime due to synthetic shadow scoping.
13915
13918
  // Note that for backwards compat we only consider non-booleans to be dynamic IDs/IDRefs
13916
13919
  // TODO [#3658]: `disableSyntheticShadowSupport` should also disable this dynamic behavior
13917
13920
  const isIdOrIdRef = (name === 'id' || isIdReferencingAttribute(name)) &&
13918
- (isExpression$1(value) || isStringLiteral(value));
13919
- if (isExpression$1(value) || isIdOrIdRef) {
13921
+ !isBooleanLiteral(value);
13922
+ // For boolean literals (e.g. `<use xlink:href>`), there is no reason to sanitize since it's empty
13923
+ const isSvgHref = isSvgUseHref(currentNode.name, name, currentNode.namespace) &&
13924
+ !isBooleanLiteral(value);
13925
+ // `<a href="#foo">` and `<area href="#foo">` must be dynamic due to synthetic shadow scoping
13926
+ // Note this only applies if there is an `id` attribute somewhere in the template
13927
+ const isScopedFragmentRef = this.scopeFragmentId &&
13928
+ isStringLiteral(value) &&
13929
+ isAllowedFragOnlyUrlsXHTML(currentNode.name, name, currentNode.namespace) &&
13930
+ isFragmentOnlyUrl(value.value);
13931
+ if (isExpression$1(value) || isIdOrIdRef || isSvgHref || isScopedFragmentRef) {
13920
13932
  let partToken = '';
13921
13933
  if (name === 'style') {
13922
13934
  partToken = `${"s" /* STATIC_PART_TOKEN_ID.STYLE */}${partId}`;
@@ -13927,9 +13939,12 @@ class CodeGen {
13927
13939
  databag.push(property$1(identifier('className'), this.genClassExpression(value)));
13928
13940
  }
13929
13941
  else {
13930
- // non-class, non-style (i.e. generic attribute or ID/IDRef)
13942
+ // non-class, non-style (i.e. generic attribute or ID/IDRef or svg use href)
13931
13943
  partToken = `${"a" /* STATIC_PART_TOKEN_ID.ATTRIBUTE */}${partId}:${name}`;
13932
- attributeExpressions.push(property$1(literal$1(name), bindAttributeExpression(attribute, elm, this, false)));
13944
+ attributeExpressions.push(property$1(literal$1(name), bindAttributeExpression(attribute, currentNode, this,
13945
+ // `addLegacySanitizationHook` is true because `isCustomRendererHookRequired`
13946
+ // being false is a precondition for static nodes.
13947
+ true)));
13933
13948
  }
13934
13949
  this.staticExpressionMap.set(attribute, partToken);
13935
13950
  }
@@ -13943,7 +13958,7 @@ class CodeGen {
13943
13958
  // For depth-first traversal, children must be prepended in order, so that they are processed before
13944
13959
  // siblings. Note that this is consistent with the order used in the diffing algo as well as
13945
13960
  // `traverseAndSetElements` in @lwc/engine-core.
13946
- stack.unshift(...transformStaticChildren(elm, this.preserveComments));
13961
+ stack.unshift(...transformStaticChildren(currentNode, this.preserveComments));
13947
13962
  }
13948
13963
  }
13949
13964
  if (partIdsToArgs.size === 0) {
@@ -14447,7 +14462,8 @@ function transform(codeGen) {
14447
14462
  return codeGen.genScopedFragId(attrValue.value);
14448
14463
  }
14449
14464
  if (isSvgUseHref(elmName, attrName, namespace)) {
14450
- // apply the fragment id tranformation if necessary
14465
+ // Apply the fragment id scoping transformation if necessary.
14466
+ // This scoping can be skipped if the value is a string literal that doesn't start with a "#"
14451
14467
  const value = isFragmentOnlyUrl(attrValue.value)
14452
14468
  ? codeGen.genScopedFragId(attrValue.value)
14453
14469
  : literal$1(attrValue.value);
@@ -14696,5 +14712,5 @@ exports.default = compile;
14696
14712
  exports.kebabcaseToCamelcase = kebabcaseToCamelcase;
14697
14713
  exports.parse = parse;
14698
14714
  exports.toPropertyName = toPropertyName;
14699
- /** version: 7.0.3 */
14715
+ /** version: 7.0.4 */
14700
14716
  //# sourceMappingURL=index.cjs.js.map