@lwc/ssr-compiler 8.12.1 → 8.12.2

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.
@@ -0,0 +1,4 @@
1
+ import { type LWCErrorInfo } from '@lwc/errors';
2
+ type ExtractArguments<T extends string, Numbers extends number = never, Args extends string[] = []> = T extends `${string}{${infer N extends number}}${infer R}` ? N extends Numbers ? ExtractArguments<R, Numbers, Args> : ExtractArguments<R, N | Numbers, [string, ...Args]> : Args;
3
+ export declare function generateError<const T extends LWCErrorInfo>(error: T, ...args: ExtractArguments<T['message']>): Error;
4
+ export {};
@@ -1,4 +1,4 @@
1
- import type { ImportDeclaration } from 'estree';
1
+ import type { ImportDeclaration, ExportNamedDeclaration, ExportAllDeclaration } from 'estree';
2
2
  import type { NodePath } from 'estree-toolkit';
3
3
  import type { ComponentMetaState } from './types';
4
4
  /**
@@ -8,3 +8,11 @@ import type { ComponentMetaState } from './types';
8
8
  * 2. it makes note of the local var name associated with the `LightningElement` import
9
9
  */
10
10
  export declare function replaceLwcImport(path: NodePath<ImportDeclaration>, state: ComponentMetaState): void;
11
+ /**
12
+ * This handles lwc barrel exports by replacing "lwc" with "@lwc/ssr-runtime"
13
+ */
14
+ export declare function replaceNamedLwcExport(path: NodePath<ExportNamedDeclaration>): void;
15
+ /**
16
+ * This handles all lwc barrel exports by replacing "lwc" with "@lwc/ssr-runtime"
17
+ */
18
+ export declare function replaceAllLwcExport(path: NodePath<ExportAllDeclaration>): void;
@@ -12,7 +12,6 @@ export interface ComponentMetaState {
12
12
  isLWC: boolean;
13
13
  hasConstructor: boolean;
14
14
  hasConnectedCallback: boolean;
15
- hasRenderMethod: boolean;
16
15
  hadRenderedCallback: boolean;
17
16
  hadDisconnectedCallback: boolean;
18
17
  hadErrorCallback: boolean;
@@ -1,7 +1,7 @@
1
1
  import type { ChildNode as IrChildNode, Node as IrNode } from '@lwc/template-compiler';
2
2
  import type { Statement as EsStatement } from 'estree';
3
3
  import type { TemplateOpts, TransformerContext } from './types';
4
- export declare function irChildrenToEs(children: IrChildNode[], cxt: TransformerContext): EsStatement[];
4
+ export declare function irChildrenToEs(children: IrChildNode[], cxt: TransformerContext, cb?: (child: IrChildNode) => (() => void) | void): EsStatement[];
5
5
  export declare function irToEs<T extends IrNode>(node: T, cxt: TransformerContext): EsStatement[];
6
6
  export declare function templateIrToEsTree(node: IrNode, contextOpts: TemplateOpts): {
7
7
  addImport: (imports: string | string[] | Record<string, string | undefined>, source?: string | undefined) => void;
@@ -0,0 +1,3 @@
1
+ import type { If as IrIf } from '@lwc/template-compiler';
2
+ import type { Transformer } from '../types';
3
+ export declare const LegacyIf: Transformer<IrIf>;
@@ -0,0 +1,3 @@
1
+ import type { ElseifBlock as IrElseifBlock, IfBlock as IrIfBlock } from '@lwc/template-compiler';
2
+ import type { Transformer } from '../types';
3
+ export declare const IfBlock: Transformer<IrIfBlock | IrElseifBlock>;
@@ -1,23 +1,5 @@
1
- import type { CallExpression, Identifier, MemberExpression } from 'estree';
2
1
  import type { Checker } from 'estree-toolkit/dist/generated/is-type';
3
2
  import type { Node } from 'estree-toolkit/dist/helpers';
4
- /** Node representing an identifier named "render". */
5
- type RenderIdentifier = Identifier & {
6
- name: 'render';
7
- };
8
- /** Node representing a member expression `<something>.render`. */
9
- type RenderMemberExpression = MemberExpression & {
10
- property: RenderIdentifier;
11
- };
12
- /** Node representing a method call `<something>.render()`. */
13
- type RenderCall = CallExpression & {
14
- callee: RenderMemberExpression;
15
- };
16
- /** Returns `true` if the node is an identifier or `<something>.render()`. */
17
- export declare const isIdentOrRenderCall: {
18
- (node: Node | null | undefined): node is Identifier | RenderCall;
19
- __debugName: string;
20
- };
21
3
  /** A validator that returns `true` if the node is `null`. */
22
4
  type NullableChecker<T extends Node> = (node: Node | null | undefined) => node is T | null;
23
5
  /** Extends a validator to return `true` if the node is `null`. */
package/dist/index.cjs.js CHANGED
@@ -9,11 +9,11 @@ var shared = require('@lwc/shared');
9
9
  var astring = require('astring');
10
10
  var estreeToolkit = require('estree-toolkit');
11
11
  var meriyah = require('meriyah');
12
+ var errors = require('@lwc/errors');
12
13
  var immer = require('immer');
13
14
  var node_path = require('node:path');
14
15
  var acorn = require('acorn');
15
16
  var templateCompiler = require('@lwc/template-compiler');
16
- var errors = require('@lwc/errors');
17
17
  var builders = require('estree-toolkit/dist/builders');
18
18
  var types = require('@babel/types');
19
19
  var util = require('util');
@@ -288,7 +288,7 @@ _ImportManager_map = new WeakMap();
288
288
  * 2. it makes note of the local var name associated with the `LightningElement` import
289
289
  */
290
290
  function replaceLwcImport(path, state) {
291
- if (!path.node || path.node.source.value !== 'lwc') {
291
+ if (!path.node || !isLwcSource(path)) {
292
292
  return;
293
293
  }
294
294
  for (const specifier of path.node.specifiers) {
@@ -299,7 +299,31 @@ function replaceLwcImport(path, state) {
299
299
  break;
300
300
  }
301
301
  }
302
- path.replaceWith(estreeToolkit.builders.importDeclaration(path.node.specifiers, estreeToolkit.builders.literal('@lwc/ssr-runtime')));
302
+ path.replaceWith(estreeToolkit.builders.importDeclaration(structuredClone(path.node.specifiers), estreeToolkit.builders.literal('@lwc/ssr-runtime')));
303
+ }
304
+ /**
305
+ * This handles lwc barrel exports by replacing "lwc" with "@lwc/ssr-runtime"
306
+ */
307
+ function replaceNamedLwcExport(path) {
308
+ if (!path.node || !isLwcSource(path)) {
309
+ return;
310
+ }
311
+ path.replaceWith(estreeToolkit.builders.exportNamedDeclaration(structuredClone(path.node.declaration), structuredClone(path.node.specifiers), estreeToolkit.builders.literal('@lwc/ssr-runtime')));
312
+ }
313
+ /**
314
+ * This handles all lwc barrel exports by replacing "lwc" with "@lwc/ssr-runtime"
315
+ */
316
+ function replaceAllLwcExport(path) {
317
+ if (!path.node || !isLwcSource(path)) {
318
+ return;
319
+ }
320
+ path.replaceWith(estreeToolkit.builders.exportAllDeclaration(estreeToolkit.builders.literal('@lwc/ssr-runtime'), structuredClone(path.node.exported)));
321
+ }
322
+ /**
323
+ * Utility to determine if a node source is 'lwc'
324
+ */
325
+ function isLwcSource(path) {
326
+ return path.node?.source?.value === 'lwc';
303
327
  }
304
328
 
305
329
  /*
@@ -518,36 +542,13 @@ function esTemplateWithYield(javascriptSegments, ...validators) {
518
542
  }
519
543
 
520
544
  /*
521
- * Copyright (c) 2024, salesforce.com, inc.
545
+ * Copyright (c) 2024, Salesforce, Inc.
522
546
  * All rights reserved.
523
547
  * SPDX-License-Identifier: MIT
524
548
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
525
549
  */
526
- /** Returns `true` if the node is an identifier or `<something>.render()`. */
527
- const isIdentOrRenderCall = (node) => {
528
- return (estreeToolkit.is.identifier(node) ||
529
- (estreeToolkit.is.callExpression(node) &&
530
- estreeToolkit.is.memberExpression(node.callee) &&
531
- estreeToolkit.is.identifier(node.callee.property) &&
532
- node.callee.property.name === 'render'));
533
- };
534
- isIdentOrRenderCall.__debugName = 'identifier or .render() call';
535
- /** Extends a validator to return `true` if the node is `null`. */
536
- function isNullableOf(validator) {
537
- const nullableValidator = (node) => {
538
- return node === null || validator(node);
539
- };
540
- if (process.env.NODE_ENV !== 'production') {
541
- nullableValidator.__debugName = `nullable(${validator.__debugName || validator.name || 'unknown validator'})`;
542
- }
543
- return nullableValidator;
544
- }
545
- isNullableOf.__debugName = 'isNullableOf';
546
- if (process.env.NODE_ENV !== 'production') {
547
- // Modifying another package's exports is a code smell!
548
- for (const [key, val] of shared.entries(estreeToolkit.is)) {
549
- val.__debugName = key;
550
- }
550
+ function generateError(error, ...args) {
551
+ return new Error(errors.generateErrorMessage(error, args));
551
552
  }
552
553
 
553
554
  /*
@@ -567,8 +568,7 @@ function bMemberExpressionChain(props) {
567
568
  function getWireParams(node) {
568
569
  const { decorators } = node;
569
570
  if (decorators.length > 1) {
570
- // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler`
571
- throw new Error('todo - multiple decorators at once');
571
+ throw generateError(errors.DecoratorErrors.ONE_WIRE_DECORATOR_ALLOWED);
572
572
  }
573
573
  // validate the parameters
574
574
  const wireDecorator = decorators[0].expression;
@@ -611,8 +611,7 @@ function validateWireId(id, path) {
611
611
  }
612
612
  // This is not the exact same validation done in @lwc/babel-plugin-component but it accomplishes the same thing
613
613
  if (path.scope?.getBinding(wireAdapterVar)?.kind !== 'module') {
614
- // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler`
615
- throw new Error('todo - WIRE_ADAPTER_SHOULD_BE_IMPORTED');
614
+ throw generateError(errors.DecoratorErrors.COMPUTED_PROPERTY_MUST_BE_CONSTANT_OR_LITERAL);
616
615
  }
617
616
  }
618
617
  function validateWireConfig(config, path) {
@@ -648,8 +647,10 @@ function validateWireConfig(config, path) {
648
647
  continue;
649
648
  }
650
649
  }
651
- // TODO [#5032]: Harmonize errors thrown in `@lwc/ssr-compiler`
652
- throw new Error('todo - COMPUTED_PROPERTY_MUST_BE_CONSTANT_OR_LITERAL');
650
+ else if (estreeToolkit.is.templateLiteral(key)) {
651
+ throw generateError(errors.DecoratorErrors.COMPUTED_PROPERTY_CANNOT_BE_TEMPLATE_LITERAL);
652
+ }
653
+ throw generateError(errors.DecoratorErrors.COMPUTED_PROPERTY_MUST_BE_CONSTANT_OR_LITERAL);
653
654
  }
654
655
  }
655
656
  function catalogWireAdapters(path, state) {
@@ -754,7 +755,10 @@ const bGenerateMarkup = (esTemplate `
754
755
  instance.connectedCallback();
755
756
  __mutationTracker.disable(instance);
756
757
  }
757
- const tmplFn = ${isIdentOrRenderCall} ?? ${ /*component class*/3}[__SYMBOL__DEFAULT_TEMPLATE] ?? __fallbackTmpl;
758
+ // If a render() function is defined on the class or any of its superclasses, then that takes priority.
759
+ // Next, if the class or any of its superclasses has an implicitly-associated template, then that takes
760
+ // second priority (e.g. a foo.html file alongside a foo.js file). Finally, there is a fallback empty template.
761
+ const tmplFn = instance.render?.() ?? ${ /*component class*/3}[__SYMBOL__DEFAULT_TEMPLATE] ?? __fallbackTmpl;
758
762
  yield \`<\${tagName}\`;
759
763
 
760
764
  const hostHasScopedStylesheets =
@@ -794,20 +798,17 @@ const bExposeTemplate = (esTemplate `
794
798
  * - deferring to the template function for yielding child content
795
799
  */
796
800
  function addGenerateMarkupFunction(program, state, tagName, filename) {
797
- const { hasRenderMethod, privateFields, publicFields, tmplExplicitImports } = state;
801
+ const { privateFields, publicFields, tmplExplicitImports } = state;
798
802
  // The default tag name represents the component name that's passed to the transformer.
799
803
  // This is needed to generate markup for dynamic components which are invoked through
800
804
  // the generateMarkup function on the constructor.
801
805
  // At the time of generation, the invoker does not have reference to its tag name to pass as an argument.
802
806
  const defaultTagName = estreeToolkit.builders.literal(tagName);
803
807
  const classIdentifier = estreeToolkit.builders.identifier(state.lwcClassName);
804
- const tmplVar = estreeToolkit.builders.identifier('tmpl');
805
- const renderCall = hasRenderMethod
806
- ? estreeToolkit.builders.callExpression(estreeToolkit.builders.memberExpression(estreeToolkit.builders.identifier('instance'), estreeToolkit.builders.identifier('render')), [])
807
- : tmplVar;
808
808
  let exposeTemplateBlock = null;
809
809
  if (!tmplExplicitImports) {
810
810
  const defaultTmplPath = `./${node_path.parse(filename).name}.html`;
811
+ const tmplVar = estreeToolkit.builders.identifier('tmpl');
811
812
  program.body.unshift(bImportDeclaration({ default: tmplVar.name }, defaultTmplPath));
812
813
  program.body.unshift(bImportDeclaration({ SYMBOL__DEFAULT_TEMPLATE: '__SYMBOL__DEFAULT_TEMPLATE' }));
813
814
  exposeTemplateBlock = bExposeTemplate(tmplVar, classIdentifier);
@@ -828,7 +829,7 @@ function addGenerateMarkupFunction(program, state, tagName, filename) {
828
829
  SYMBOL__SET_INTERNALS: '__SYMBOL__SET_INTERNALS',
829
830
  establishContextfulRelationship: '__establishContextfulRelationship',
830
831
  }));
831
- program.body.push(...bGenerateMarkup(defaultTagName, estreeToolkit.builders.arrayExpression(publicFields.map(estreeToolkit.builders.literal)), estreeToolkit.builders.arrayExpression(privateFields.map(estreeToolkit.builders.literal)), classIdentifier, connectWireAdapterCode, renderCall));
832
+ program.body.push(...bGenerateMarkup(defaultTagName, estreeToolkit.builders.arrayExpression(publicFields.map(estreeToolkit.builders.literal)), estreeToolkit.builders.arrayExpression(privateFields.map(estreeToolkit.builders.literal)), classIdentifier, connectWireAdapterCode));
832
833
  if (exposeTemplateBlock) {
833
834
  program.body.push(exposeTemplateBlock);
834
835
  }
@@ -861,6 +862,12 @@ function removeDecoratorImport(path) {
861
862
  */
862
863
  const visitors = {
863
864
  $: { scope: true },
865
+ ExportNamedDeclaration(path) {
866
+ replaceNamedLwcExport(path);
867
+ },
868
+ ExportAllDeclaration(path) {
869
+ replaceAllLwcExport(path);
870
+ },
864
871
  ImportDeclaration(path, state) {
865
872
  if (!path.node || !path.node.source.value || typeof path.node.source.value !== 'string') {
866
873
  return;
@@ -919,6 +926,7 @@ const visitors = {
919
926
  return;
920
927
  }
921
928
  const { decorators } = node;
929
+ validateUniqueDecorator(decorators);
922
930
  const decoratedExpression = decorators?.[0]?.expression;
923
931
  if (estreeToolkit.is.identifier(decoratedExpression) && decoratedExpression.name === 'api') {
924
932
  state.publicFields.push(node.key.name);
@@ -950,6 +958,7 @@ const visitors = {
950
958
  return;
951
959
  }
952
960
  const { decorators } = node;
961
+ validateUniqueDecorator(decorators);
953
962
  // The real type is a subset of `Expression`, which doesn't work with the `is` validators
954
963
  const decoratedExpression = decorators?.[0]?.expression;
955
964
  if (estreeToolkit.is.callExpression(decoratedExpression) &&
@@ -977,9 +986,6 @@ const visitors = {
977
986
  case 'connectedCallback':
978
987
  state.hasConnectedCallback = true;
979
988
  break;
980
- case 'render':
981
- state.hasRenderMethod = true;
982
- break;
983
989
  case 'renderedCallback':
984
990
  state.hadRenderedCallback = true;
985
991
  path.remove();
@@ -1014,6 +1020,21 @@ const visitors = {
1014
1020
  },
1015
1021
  },
1016
1022
  };
1023
+ function validateUniqueDecorator(decorators) {
1024
+ if (decorators.length < 2) {
1025
+ return;
1026
+ }
1027
+ const expressions = decorators.map(({ expression }) => expression);
1028
+ const hasWire = expressions.some((expr) => estreeToolkit.is.callExpression(expr) && estreeToolkit.is.identifier(expr.callee, { name: 'wire' }));
1029
+ const hasApi = expressions.some((expr) => estreeToolkit.is.identifier(expr, { name: 'api' }));
1030
+ if (hasWire && hasApi) {
1031
+ throw generateError(errors.DecoratorErrors.CONFLICT_WITH_ANOTHER_DECORATOR, 'api');
1032
+ }
1033
+ const hasTrack = expressions.some((expr) => estreeToolkit.is.identifier(expr, { name: 'track' }));
1034
+ if ((hasWire || hasApi) && hasTrack) {
1035
+ throw generateError(errors.DecoratorErrors.CONFLICT_WITH_ANOTHER_DECORATOR, 'track');
1036
+ }
1037
+ }
1017
1038
  function compileJS(src, filename, tagName, options, compilationMode) {
1018
1039
  let ast = meriyah.parseModule(src, {
1019
1040
  module: true,
@@ -1023,7 +1044,6 @@ function compileJS(src, filename, tagName, options, compilationMode) {
1023
1044
  isLWC: false,
1024
1045
  hasConstructor: false,
1025
1046
  hasConnectedCallback: false,
1026
- hasRenderMethod: false,
1027
1047
  hadRenderedCallback: false,
1028
1048
  hadDisconnectedCallback: false,
1029
1049
  hadErrorCallback: false,
@@ -1321,6 +1341,30 @@ const Comment = function Comment(node, cxt) {
1321
1341
  }
1322
1342
  };
1323
1343
 
1344
+ /*
1345
+ * Copyright (c) 2024, salesforce.com, inc.
1346
+ * All rights reserved.
1347
+ * SPDX-License-Identifier: MIT
1348
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
1349
+ */
1350
+ /** Extends a validator to return `true` if the node is `null`. */
1351
+ function isNullableOf(validator) {
1352
+ const nullableValidator = (node) => {
1353
+ return node === null || validator(node);
1354
+ };
1355
+ if (process.env.NODE_ENV !== 'production') {
1356
+ nullableValidator.__debugName = `nullable(${validator.__debugName || validator.name || 'unknown validator'})`;
1357
+ }
1358
+ return nullableValidator;
1359
+ }
1360
+ isNullableOf.__debugName = 'isNullableOf';
1361
+ if (process.env.NODE_ENV !== 'production') {
1362
+ // Modifying another package's exports is a code smell!
1363
+ for (const [key, val] of shared.entries(estreeToolkit.is)) {
1364
+ val.__debugName = key;
1365
+ }
1366
+ }
1367
+
1324
1368
  /*
1325
1369
  * Copyright (c) 2024, salesforce.com, inc.
1326
1370
  * All rights reserved.
@@ -1366,6 +1410,18 @@ const bAddLightContent = (esTemplate `
1366
1410
  ${ /* slot content */false}
1367
1411
  });
1368
1412
  `);
1413
+ function getShadowSlottedContent(slottableChildren, cxt) {
1414
+ return optimizeAdjacentYieldStmts(irChildrenToEs(slottableChildren, cxt, (child) => {
1415
+ const { isSlotted } = cxt;
1416
+ if (child.type === 'ExternalComponent' || child.type === 'Element') {
1417
+ cxt.isSlotted = false;
1418
+ }
1419
+ // cleanup function
1420
+ return () => {
1421
+ cxt.isSlotted = isSlotted;
1422
+ };
1423
+ }));
1424
+ }
1369
1425
  // Light DOM slots are a bit complex because of needing to handle slots _not_ at the top level
1370
1426
  // At the non-top level, it matters what the ancestors are. These are relevant to slots:
1371
1427
  // - If (`if:true`, `if:false`)
@@ -1414,7 +1470,10 @@ function getLightSlottedContent(rootNodes, cxt) {
1414
1470
  leaf.attributes = leaf.attributes.filter((attr) => attr.name !== 'slot');
1415
1471
  }
1416
1472
  });
1473
+ const { isSlotted: originalIsSlotted } = cxt;
1474
+ cxt.isSlotted = ancestorIndices.length > 1 || clone.type === 'Slot';
1417
1475
  const slotContent = irToEs(clone, cxt);
1476
+ cxt.isSlotted = originalIsSlotted;
1418
1477
  results.push(estreeToolkit.builders.expressionStatement(bAddLightContent(slotName, null, slotContent)));
1419
1478
  };
1420
1479
  const traverse = (nodes, ancestorIndices) => {
@@ -1452,7 +1511,7 @@ function getSlottedContent(node, cxt) {
1452
1511
  // Anything inside the slotted content is a normal slotted content except for `<template lwc:slot-data>` which is a scoped slot.
1453
1512
  const slottableChildren = node.children.filter((child) => child.type !== 'ScopedSlotFragment');
1454
1513
  const scopedSlottableChildren = node.children.filter((child) => child.type === 'ScopedSlotFragment');
1455
- const shadowSlotContent = optimizeAdjacentYieldStmts(irChildrenToEs(slottableChildren, cxt));
1514
+ const shadowSlotContent = getShadowSlottedContent(slottableChildren, cxt);
1456
1515
  const lightSlotContent = getLightSlottedContent(slottableChildren, cxt);
1457
1516
  const scopedSlotContent = scopedSlottableChildren.map((child) => {
1458
1517
  const boundVariableName = child.slotData.value.name;
@@ -1604,6 +1663,12 @@ const bYieldDynamicValue = (esTemplateWithYield `
1604
1663
  attrValue = shouldNormalize ? 0 : attrValue;
1605
1664
  }
1606
1665
 
1666
+ // Backwards compatibility with historical patchStyleAttribute() behavior:
1667
+ // https://github.com/salesforce/lwc/blob/59e2c6c/packages/%40lwc/engine-core/src/framework/modules/computed-style-attr.ts#L40
1668
+ if (attrName === 'style' && (typeof attrValue !== 'string' || attrValue === '')) {
1669
+ attrValue = undefined;
1670
+ }
1671
+
1607
1672
  if (attrValue !== undefined && attrValue !== null) {
1608
1673
  yield ' ' + attrName;
1609
1674
 
@@ -1834,36 +1899,14 @@ const ForOf = function ForEach(node, cxt) {
1834
1899
  * SPDX-License-Identifier: MIT
1835
1900
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
1836
1901
  */
1837
- function bYieldComment(text = '') {
1838
- return estreeToolkit.builders.expressionStatement(estreeToolkit.builders.yieldExpression(estreeToolkit.builders.literal(`<!--${text}-->`)));
1839
- }
1840
- function bBlockStatement(childNodes, cxt, insertComments) {
1841
- let statements = irChildrenToEs(childNodes, cxt);
1842
- if (insertComments)
1843
- statements = [bYieldComment(), ...statements, bYieldComment()];
1844
- return estreeToolkit.builders.blockStatement(optimizeAdjacentYieldStmts(statements));
1845
- }
1846
- const If = function If(node, cxt) {
1902
+ const LegacyIf = function If(node, cxt) {
1847
1903
  const { modifier: trueOrFalseAsStr, condition, children } = node;
1848
1904
  const trueOrFalse = trueOrFalseAsStr === 'true';
1905
+ // FIXME: Does engine-server actually do triple-equals here?
1849
1906
  const comparison = estreeToolkit.builders.binaryExpression('===', estreeToolkit.builders.literal(trueOrFalse), expressionIrToEs(condition, cxt));
1850
- return [estreeToolkit.builders.ifStatement(comparison, bBlockStatement(children, cxt, false))];
1851
- };
1852
- function bIfStatement(ifElseIfNode, cxt) {
1853
- const { children, condition, else: elseNode } = ifElseIfNode;
1854
- let elseBlock = null;
1855
- if (elseNode) {
1856
- if (elseNode.type === 'ElseBlock') {
1857
- elseBlock = bBlockStatement(elseNode.children, cxt, true);
1858
- }
1859
- else {
1860
- elseBlock = bIfStatement(elseNode, cxt);
1861
- }
1862
- }
1863
- return estreeToolkit.builders.ifStatement(expressionIrToEs(condition, cxt), bBlockStatement(children, cxt, !cxt.isSlotted), elseBlock);
1864
- }
1865
- const IfBlock = function IfBlock(node, cxt) {
1866
- return [bIfStatement(node, cxt)];
1907
+ const childStatements = irChildrenToEs(children, cxt);
1908
+ const block = estreeToolkit.builders.blockStatement(optimizeAdjacentYieldStmts(childStatements));
1909
+ return [estreeToolkit.builders.ifStatement(comparison, block)];
1867
1910
  };
1868
1911
 
1869
1912
  /*
@@ -1984,6 +2027,46 @@ function createNewContext(templateOptions) {
1984
2027
  };
1985
2028
  }
1986
2029
 
2030
+ /*
2031
+ * Copyright (c) 2024, salesforce.com, inc.
2032
+ * All rights reserved.
2033
+ * SPDX-License-Identifier: MIT
2034
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
2035
+ */
2036
+ // lwc:if/lwc:elseif/lwc:else use bookend comments due to VFragment vdom node using them
2037
+ // The bookends should surround the entire if/elseif/else series
2038
+ // Note: these should only be rendered if _something_ is rendered by a series of if/elseif/else's
2039
+ function bYieldBookendComment() {
2040
+ return estreeToolkit.builders.expressionStatement(estreeToolkit.builders.yieldExpression(estreeToolkit.builders.literal(`<!---->`)));
2041
+ }
2042
+ function bBlockStatement(childNodes, cxt) {
2043
+ const childStatements = irChildrenToEs(childNodes, cxt);
2044
+ // Due to `flattenFragmentsInChildren`, we have to remove bookends for all _top-level_ slotted
2045
+ // content. This applies to both light DOM and shadow DOM slots, although light DOM slots have
2046
+ // the additional wrinkle that they themselves are VFragments with their own bookends.
2047
+ // https://github.com/salesforce/lwc/blob/a33b390/packages/%40lwc/engine-core/src/framework/rendering.ts#L718-L753
2048
+ const statements = cxt.isSlotted
2049
+ ? childStatements
2050
+ : [bYieldBookendComment(), ...childStatements, bYieldBookendComment()];
2051
+ return estreeToolkit.builders.blockStatement(optimizeAdjacentYieldStmts(statements));
2052
+ }
2053
+ function bIfStatement(ifElseIfNode, cxt) {
2054
+ const { children, condition, else: elseNode } = ifElseIfNode;
2055
+ let elseBlock = null;
2056
+ if (elseNode) {
2057
+ if (elseNode.type === 'ElseBlock') {
2058
+ elseBlock = bBlockStatement(elseNode.children, cxt);
2059
+ }
2060
+ else {
2061
+ elseBlock = bIfStatement(elseNode, cxt);
2062
+ }
2063
+ }
2064
+ return estreeToolkit.builders.ifStatement(expressionIrToEs(condition, cxt), bBlockStatement(children, cxt), elseBlock);
2065
+ }
2066
+ const IfBlock = function IfBlock(node, cxt) {
2067
+ return [bIfStatement(node, cxt)];
2068
+ };
2069
+
1987
2070
  /*
1988
2071
  * Copyright (c) 2024, salesforce.com, inc.
1989
2072
  * All rights reserved.
@@ -2006,7 +2089,7 @@ const transformers = {
2006
2089
  ExternalComponent: Element,
2007
2090
  ForEach,
2008
2091
  ForOf,
2009
- If,
2092
+ If: LegacyIf,
2010
2093
  IfBlock,
2011
2094
  Root,
2012
2095
  Text,
@@ -2018,12 +2101,15 @@ const transformers = {
2018
2101
  Slot,
2019
2102
  Lwc: LwcComponent,
2020
2103
  };
2021
- function irChildrenToEs(children, cxt) {
2022
- const result = children.flatMap((child, idx) => {
2023
- cxt.prevSibling = children[idx - 1];
2024
- cxt.nextSibling = children[idx + 1];
2025
- return irToEs(child, cxt);
2026
- });
2104
+ function irChildrenToEs(children, cxt, cb) {
2105
+ const result = [];
2106
+ for (let i = 0; i < children.length; i++) {
2107
+ cxt.prevSibling = children[i - 1];
2108
+ cxt.nextSibling = children[i + 1];
2109
+ const cleanUp = cb?.(children[i]);
2110
+ result.push(...irToEs(children[i], cxt));
2111
+ cleanUp?.();
2112
+ }
2027
2113
  cxt.prevSibling = undefined;
2028
2114
  cxt.nextSibling = undefined;
2029
2115
  return result;
@@ -2173,5 +2259,5 @@ function compileTemplateForSSR(src, filename, options, mode = shared.DEFAULT_SSR
2173
2259
 
2174
2260
  exports.compileComponentForSSR = compileComponentForSSR;
2175
2261
  exports.compileTemplateForSSR = compileTemplateForSSR;
2176
- /** version: 8.12.1 */
2262
+ /** version: 8.12.2 */
2177
2263
  //# sourceMappingURL=index.cjs.js.map