@lwc/template-compiler 7.2.0 → 7.2.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/README.md CHANGED
@@ -62,6 +62,7 @@ const { code, warnings } = compile(`<template><h1>Hello World!</h1></template>`,
62
62
  - `enableLwcSpread` (boolean, optional, `true` by default) - Deprecated. Ignored by template-compiler. `lwc:spread` is always enabled.
63
63
  - `customRendererConfig` (CustomRendererConfig, optional) - specifies a configuration to use to match elements. Matched elements will get a custom renderer hook in the generated template.
64
64
  - `instrumentation` (InstrumentationObject, optional) - instrumentation object to gather metrics and non-error logs for internal use. See the `@lwc/errors` package for details on the interface.
65
+ - `disableSyntheticShadowSupport` (type: `boolean`, default: `false`) - Set to true if synthetic shadow DOM support is not needed, which can result in smaller/faster output.
65
66
 
66
67
  - Example 1: Config to match `<use>` elements under the `svg` namespace and have `href` attribute set.
67
68
 
@@ -36,6 +36,11 @@ export default class CodeGen {
36
36
  * State maintains information about the current compilation configs.
37
37
  */
38
38
  readonly state: State;
39
+ /**
40
+ * True if this is a synthetic shadow template - otherwise, we may apply certain optimizations
41
+ * that only exist for native shadow and light DOM.
42
+ */
43
+ readonly isSyntheticShadow: boolean;
39
44
  currentId: number;
40
45
  currentKey: number;
41
46
  innerHtmlInstances: number;
@@ -66,8 +71,8 @@ export default class CodeGen {
66
71
  genIterator(iterable: t.Expression, callback: t.FunctionExpression): import("estree").CallExpression;
67
72
  genBind(handler: t.Expression): import("estree").CallExpression;
68
73
  genFlatten(children: t.Expression[]): import("estree").CallExpression;
69
- genScopedId(id: string | t.Expression): t.CallExpression;
70
- genScopedFragId(id: string | t.Expression): t.CallExpression;
74
+ genScopedId(id: string | t.Expression): t.Expression | t.Literal;
75
+ genScopedFragId(id: string | t.Expression): t.Expression | t.Literal;
71
76
  genClassExpression(value: Expression): import("estree").Expression;
72
77
  genNormalizeClassName(className: t.Expression): t.CallExpression;
73
78
  /**
package/dist/config.d.ts CHANGED
@@ -64,6 +64,8 @@ export interface Config {
64
64
  * The API version to associate with the compiled template
65
65
  */
66
66
  apiVersion?: number;
67
+ /** Set to true if synthetic shadow DOM support is not needed, which can result in smaller/faster output. */
68
+ disableSyntheticShadowSupport?: boolean;
67
69
  }
68
70
  type OptionalConfigNames = 'customRendererConfig' | 'instrumentation' | 'namespace' | 'name';
69
71
  type RequiredConfigOptions = Required<Omit<Config, OptionalConfigNames>>;
package/dist/index.cjs.js CHANGED
@@ -210,6 +210,7 @@ const AVAILABLE_OPTION_NAMES = new Set([
210
210
  'instrumentation',
211
211
  'namespace',
212
212
  'name',
213
+ 'disableSyntheticShadowSupport',
213
214
  ]);
214
215
  function normalizeCustomRendererConfig(config) {
215
216
  const tagNames = [];
@@ -261,6 +262,7 @@ function normalizeConfig(config) {
261
262
  enableDynamicComponents: false,
262
263
  enableStaticContentOptimization: true,
263
264
  enableLwcSpread: true,
265
+ disableSyntheticShadowSupport: false,
264
266
  ...config,
265
267
  apiVersion, // overrides the config apiVersion
266
268
  ...{ customRendererConfig },
@@ -13148,7 +13150,7 @@ function serializeAttrs(element, codeGen) {
13148
13150
  */
13149
13151
  const attrs = [];
13150
13152
  let hasClassAttr = false;
13151
- const collector = ({ name, value, hasExpression, hasIdOrIdRef, hasSvgUseHref, hasScopedFragmentRef, }) => {
13153
+ const collector = ({ name, value, hasExpression, hasSvgUseHref, needsScoping, }) => {
13152
13154
  let v = typeof value === 'string' ? templateStringEscape(value) : value;
13153
13155
  if (name === 'class') {
13154
13156
  hasClassAttr = true;
@@ -13173,8 +13175,7 @@ function serializeAttrs(element, codeGen) {
13173
13175
  // Skip serializing here and handle it as if it were a dynamic attribute instead.
13174
13176
  // Note that, to maintain backwards compatibility with the non-static output, we treat the valueless
13175
13177
  // "boolean" format (e.g. `<div id>`) as the empty string, which is semantically equivalent.
13176
- // TODO [#3658]: `disableSyntheticShadowSupport` should also disable this dynamic behavior
13177
- const needsPlaceholder = hasExpression || hasIdOrIdRef || hasSvgUseHref || hasScopedFragmentRef;
13178
+ const needsPlaceholder = hasExpression || hasSvgUseHref || needsScoping;
13178
13179
  // Inject a placeholder where the staticPartId will go when an expression occurs.
13179
13180
  // This is only needed for SSR to inject the expression value during serialization.
13180
13181
  attrs.push(needsPlaceholder ? `\${"${v}"}` : ` ${name}="${shared.htmlEscape(v, true)}"`);
@@ -13187,25 +13188,26 @@ function serializeAttrs(element, codeGen) {
13187
13188
  .map((attr) => {
13188
13189
  const { name, value } = attr;
13189
13190
  const hasExpression = isExpression$1(value);
13191
+ // For boolean literals (e.g. `<use xlink:href>`), there is no reason to sanitize since it's empty
13192
+ const hasSvgUseHref = isSvgUseHref(element.name, name, element.namespace) && !isBooleanLiteral(value);
13190
13193
  // IDs/IDRefs must be handled dynamically at runtime due to synthetic shadow scoping.
13191
13194
  // Note that for backwards compat we only consider non-booleans to be dynamic IDs/IDRefs
13192
- // TODO [#3658]: `disableSyntheticShadowSupport` should also disable this dynamic behavior
13193
13195
  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
13196
  // `<a href="#foo">` and `<area href="#foo">` must be dynamic due to synthetic shadow scoping
13197
13197
  // Note this only applies if there is an `id` attribute somewhere in the template
13198
13198
  const hasScopedFragmentRef = codeGen.scopeFragmentId &&
13199
13199
  isStringLiteral(value) &&
13200
13200
  isAllowedFragOnlyUrlsXHTML(element.name, name, element.namespace) &&
13201
13201
  isFragmentOnlyUrl(value.value);
13202
+ // If we're not running in synthetic shadow mode (light or shadow+disableSyntheticShadowSupport),
13203
+ // then static IDs/IDrefs/fragment refs will be rendered directly into HTML strings.
13204
+ const needsScoping = codeGen.isSyntheticShadow && (hasIdOrIdRef || hasScopedFragmentRef);
13202
13205
  return {
13203
13206
  hasExpression,
13204
- hasIdOrIdRef,
13205
13207
  hasSvgUseHref,
13206
- hasScopedFragmentRef,
13208
+ needsScoping,
13207
13209
  name,
13208
- value: hasExpression || hasIdOrIdRef || hasSvgUseHref || hasScopedFragmentRef
13210
+ value: hasExpression || hasSvgUseHref || needsScoping
13209
13211
  ? codeGen.getStaticExpressionToken(attr)
13210
13212
  : value.value,
13211
13213
  };
@@ -13544,6 +13546,9 @@ class CodeGen {
13544
13546
  this.scope = this.createScope();
13545
13547
  this.state = state;
13546
13548
  this.apiVersion = shared.getAPIVersionFromNumber(state.config.apiVersion);
13549
+ this.isSyntheticShadow =
13550
+ this.renderMode !== exports.LWCDirectiveRenderMode.light &&
13551
+ !this.state.config.disableSyntheticShadowSupport;
13547
13552
  }
13548
13553
  generateKey() {
13549
13554
  return this.currentKey++;
@@ -13612,16 +13617,14 @@ class CodeGen {
13612
13617
  return this._renderApiCall(RENDER_APIS.flatten, children);
13613
13618
  }
13614
13619
  genScopedId(id) {
13615
- if (typeof id === 'string') {
13616
- return this._renderApiCall(RENDER_APIS.scopedId, [literal$1(id)]);
13617
- }
13618
- return this._renderApiCall(RENDER_APIS.scopedId, [id]);
13620
+ const value = typeof id === 'string' ? literal$1(id) : id;
13621
+ return this.isSyntheticShadow ? this._renderApiCall(RENDER_APIS.scopedId, [value]) : value;
13619
13622
  }
13620
13623
  genScopedFragId(id) {
13621
- if (typeof id === 'string') {
13622
- return this._renderApiCall(RENDER_APIS.scopedFragId, [literal$1(id)]);
13623
- }
13624
- return this._renderApiCall(RENDER_APIS.scopedFragId, [id]);
13624
+ const value = typeof id === 'string' ? literal$1(id) : id;
13625
+ return this.isSyntheticShadow
13626
+ ? this._renderApiCall(RENDER_APIS.scopedFragId, [value])
13627
+ : value;
13625
13628
  }
13626
13629
  genClassExpression(value) {
13627
13630
  let classExpression = this.bindExpression(value);
@@ -13910,7 +13913,6 @@ class CodeGen {
13910
13913
  const { name, value } = attribute;
13911
13914
  // IDs/IDRefs must be handled dynamically at runtime due to synthetic shadow scoping.
13912
13915
  // Note that for backwards compat we only consider non-booleans to be dynamic IDs/IDRefs
13913
- // TODO [#3658]: `disableSyntheticShadowSupport` should also disable this dynamic behavior
13914
13916
  const isIdOrIdRef = (name === 'id' || isIdReferencingAttribute(name)) &&
13915
13917
  !isBooleanLiteral(value);
13916
13918
  // For boolean literals (e.g. `<use xlink:href>`), there is no reason to sanitize since it's empty
@@ -13922,7 +13924,10 @@ class CodeGen {
13922
13924
  isStringLiteral(value) &&
13923
13925
  isAllowedFragOnlyUrlsXHTML(currentNode.name, name, currentNode.namespace) &&
13924
13926
  isFragmentOnlyUrl(value.value);
13925
- if (isExpression$1(value) || isIdOrIdRef || isSvgHref || isScopedFragmentRef) {
13927
+ // If we're not running in synthetic shadow mode (light or shadow+disableSyntheticShadowSupport),
13928
+ // then static IDs/IDrefs/fragment refs will be rendered directly into HTML strings.
13929
+ const needsScoping = this.isSyntheticShadow && (isIdOrIdRef || isScopedFragmentRef);
13930
+ if (isExpression$1(value) || isSvgHref || needsScoping) {
13926
13931
  let partToken = '';
13927
13932
  if (name === 'style') {
13928
13933
  partToken = `${"s" /* STATIC_PART_TOKEN_ID.STYLE */}${partId}`;
@@ -14706,5 +14711,5 @@ exports.default = compile;
14706
14711
  exports.kebabcaseToCamelcase = kebabcaseToCamelcase;
14707
14712
  exports.parse = parse;
14708
14713
  exports.toPropertyName = toPropertyName;
14709
- /** version: 7.2.0 */
14714
+ /** version: 7.2.1 */
14710
14715
  //# sourceMappingURL=index.cjs.js.map