@lwc/ssr-compiler 8.3.0 → 8.4.0

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.
@@ -1,3 +1,4 @@
1
- export default function compileJS(src: string, filename: string): {
1
+ import type { CompilationMode } from '../shared';
2
+ export default function compileJS(src: string, filename: string, compilationMode: CompilationMode): {
2
3
  code: string;
3
4
  };
@@ -14,5 +14,4 @@ export interface ComponentMetaState {
14
14
  tmplExplicitImports: Map<string, string> | null;
15
15
  cssExplicitImports: Map<string, string> | null;
16
16
  staticStylesheetIds: Set<string> | null;
17
- reflectedPropsInPlay: Set<string>;
18
17
  }
@@ -1,4 +1,5 @@
1
1
  import { type Config as TemplateCompilerConfig } from '@lwc/template-compiler';
2
- export default function compileTemplate(src: string, filename: string, options: TemplateCompilerConfig): {
2
+ import type { CompilationMode } from '../shared';
3
+ export default function compileTemplate(src: string, filename: string, options: TemplateCompilerConfig, compilationMode: CompilationMode): {
3
4
  code: string;
4
5
  };
@@ -1,5 +1,16 @@
1
- import type { ImportDeclaration as EsImportDeclaration, Statement as EsStatement } from 'estree';
1
+ import { Node as IrNode } from '@lwc/template-compiler';
2
+ import { TransformerContext } from './types';
3
+ import type { ImportDeclaration as EsImportDeclaration, Statement as EsStatement, Expression as EsExpression } from 'estree';
2
4
  export declare const bImportHtmlEscape: () => EsImportDeclaration;
3
5
  export declare const importHtmlEscapeKey = "import:htmlEscape";
4
6
  export declare const isValidIdentifier: (str: string) => boolean;
5
7
  export declare function optimizeAdjacentYieldStmts(statements: EsStatement[]): EsStatement[];
8
+ export declare function bAttributeValue(node: IrNode, attrName: string): EsExpression;
9
+ /**
10
+ * Given an expression in a context, return an expression that may be scoped to that context.
11
+ * For example, for the expression `foo`, it will typically be `instance.foo`, but if we're
12
+ * inside a `for:each` block then the `foo` variable may refer to the scoped `foo`,
13
+ * e.g. `<template for:each={foos} for:item="foo">`
14
+ * @param expression
15
+ */
16
+ export declare function getScopedExpression(expression: EsExpression, cxt: TransformerContext): EsExpression;
package/dist/index.cjs.js CHANGED
@@ -8,14 +8,155 @@ Object.defineProperty(exports, '__esModule', { value: true });
8
8
  var astring = require('astring');
9
9
  var estreeToolkit = require('estree-toolkit');
10
10
  var meriyah = require('meriyah');
11
- var shared = require('@lwc/shared');
12
- var acorn = require('acorn');
13
11
  var immer = require('immer');
12
+ var acorn = require('acorn');
14
13
  var node_path = require('node:path');
15
14
  var templateCompiler = require('@lwc/template-compiler');
16
15
  var builders = require('estree-toolkit/dist/builders');
16
+ var shared = require('@lwc/shared');
17
17
  var util = require('util');
18
18
 
19
+ /*
20
+ * Copyright (c) 2024, Salesforce, Inc.
21
+ * All rights reserved.
22
+ * SPDX-License-Identifier: MIT
23
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
24
+ */
25
+ const EMIT_IDENT = estreeToolkit.builders.identifier('$$emit');
26
+ // Rollup may rename variables to prevent shadowing. When it does, it uses the format `foo$0`, `foo$1`, etc.
27
+ const TMPL_FN_PATTERN = /tmpl($\d+)?/;
28
+ const GEN_MARKUP_PATTERN = /generateMarkup($\d+)?/;
29
+ const isWithinFn = (pattern, nodePath) => {
30
+ const { node } = nodePath;
31
+ if (!node) {
32
+ return false;
33
+ }
34
+ if (node.type === 'FunctionDeclaration' && pattern.test(node.id.name)) {
35
+ return true;
36
+ }
37
+ if (nodePath.parentPath) {
38
+ return isWithinFn(pattern, nodePath.parentPath);
39
+ }
40
+ return false;
41
+ };
42
+ const visitors$2 = {
43
+ FunctionDeclaration(path, state) {
44
+ const { node } = path;
45
+ if (!node?.async || !node?.generator) {
46
+ return;
47
+ }
48
+ // Component authors might conceivably use async generator functions in their own code. Therefore,
49
+ // when traversing & transforming written+generated code, we need to disambiguate generated async
50
+ // generator functions from those that were written by the component author.
51
+ if (!isWithinFn(GEN_MARKUP_PATTERN, path) && !isWithinFn(TMPL_FN_PATTERN, path)) {
52
+ return;
53
+ }
54
+ node.generator = false;
55
+ node.async = state.mode === 'async';
56
+ node.params.unshift(EMIT_IDENT);
57
+ },
58
+ YieldExpression(path, state) {
59
+ const { node } = path;
60
+ if (!node) {
61
+ return;
62
+ }
63
+ // Component authors might conceivably use generator functions within their own code. Therefore,
64
+ // when traversing & transforming written+generated code, we need to disambiguate generated yield
65
+ // expressions from those that were written by the component author.
66
+ if (!isWithinFn(TMPL_FN_PATTERN, path) && !isWithinFn(GEN_MARKUP_PATTERN, path)) {
67
+ return;
68
+ }
69
+ if (node.delegate) {
70
+ // transform `yield* foo(arg)` into `foo($$emit, arg)` or `await foo($$emit, arg)`
71
+ if (node.argument?.type !== 'CallExpression') {
72
+ throw new Error('Implementation error: cannot transmogrify complex yield-from expressions');
73
+ }
74
+ const callExpr = node.argument;
75
+ callExpr.arguments.unshift(EMIT_IDENT);
76
+ path.replaceWith(state.mode === 'sync' ? callExpr : estreeToolkit.builders.awaitExpression(callExpr));
77
+ }
78
+ else {
79
+ // transform `yield foo` into `$$emit(foo)`
80
+ const emittedExpression = node.argument;
81
+ if (!emittedExpression) {
82
+ throw new Error('Implementation error: cannot transform a yield expression that yields nothing');
83
+ }
84
+ path.replaceWith(estreeToolkit.builders.callExpression(EMIT_IDENT, [emittedExpression]));
85
+ }
86
+ },
87
+ ImportSpecifier(path, _state) {
88
+ // @lwc/ssr-runtime has a couple of helper functions that need to conform to either the generator or
89
+ // no-generator compilation mode/paradigm. Since these are simple helper functions, we can maintain
90
+ // two implementations of each helper method:
91
+ //
92
+ // - renderAttrs vs renderAttrsNoYield
93
+ // - fallbackTmpl vs fallbackTmplNoYield
94
+ //
95
+ // If this becomes too burdensome to maintain, we can officially deprecate the generator-based approach
96
+ // and switch the @lwc/ssr-runtime implementation wholesale over to the no-generator paradigm.
97
+ const { node } = path;
98
+ if (!node || node.imported.type !== 'Identifier') {
99
+ throw new Error('Implementation error: unexpected missing identifier in import specifier');
100
+ }
101
+ if (path.parent?.type !== 'ImportDeclaration' ||
102
+ path.parent.source.value !== '@lwc/ssr-runtime') {
103
+ return;
104
+ }
105
+ if (node.imported.name === 'fallbackTmpl') {
106
+ node.imported.name = 'fallbackTmplNoYield';
107
+ }
108
+ else if (node.imported.name === 'renderAttrs') {
109
+ node.imported.name = 'renderAttrsNoYield';
110
+ }
111
+ },
112
+ };
113
+ /**
114
+ * Transforms async-generator code into either the async or synchronous alternatives that are
115
+ * ~semantically equivalent. For example, this template:
116
+ *
117
+ * <template>
118
+ * <div>foobar</div>
119
+ * <x-child></x-child>
120
+ * </template>
121
+ *
122
+ * Is compiled into the following JavaScript, intended for execution during SSR & stripped down
123
+ * for the purposes of this example:
124
+ *
125
+ * async function* tmpl(props, attrs, slottedContent, Cmp, instance) {
126
+ * yield '<div>foobar</div>';
127
+ * const childProps = {};
128
+ * const childAttrs = {};
129
+ * yield* generateChildMarkup("x-child", childProps, childAttrs, childSlottedContentGenerator);
130
+ * }
131
+ *
132
+ * When transmogrified in async-mode, the above generated template function becomes the following:
133
+ *
134
+ * async function tmpl($$emit, props, attrs, slottedContent, Cmp, instance) {
135
+ * $$emit('<div>foobar</div>');
136
+ * const childProps = {};
137
+ * const childAttrs = {};
138
+ * await generateChildMarkup($$emit, "x-child", childProps, childAttrs, childSlottedContentGenerator);
139
+ * }
140
+ *
141
+ * When transmogrified in sync-mode, the template function becomes the following:
142
+ *
143
+ * function tmpl($$emit, props, attrs, slottedContent, Cmp, instance) {
144
+ * $$emit('<div>foobar</div>');
145
+ * const childProps = {};
146
+ * const childAttrs = {};
147
+ * generateChildMarkup($$emit, "x-child", childProps, childAttrs, childSlottedContentGenerator);
148
+ * }
149
+ *
150
+ * There are tradeoffs for each of these modes. Notably, the async-yield variety is the easiest to transform
151
+ * into either of the other varieties and, for that reason, is the variety that is "authored" by the SSR compiler.
152
+ */
153
+ function transmogrify(compiledComponentAst, mode = 'sync') {
154
+ const state = {
155
+ mode,
156
+ };
157
+ return immer.produce(compiledComponentAst, (astDraft) => estreeToolkit.traverse(astDraft, visitors$2, state));
158
+ }
159
+
19
160
  /*
20
161
  * Copyright (c) 2024, salesforce.com, inc.
21
162
  * All rights reserved.
@@ -78,7 +219,7 @@ function catalogTmplImport(path, state) {
78
219
  /** Placeholder value to use to opt out of validation. */
79
220
  const NO_VALIDATION = false;
80
221
  const PLACEHOLDER_PREFIX = `__ESTEMPLATE_${Math.random().toString().slice(2)}_PLACEHOLDER__`;
81
- const getReplacementNode = (state, placeholderId, nodeType) => {
222
+ const getReplacementNode = (state, placeholderId) => {
82
223
  const key = Number(placeholderId.slice(PLACEHOLDER_PREFIX.length));
83
224
  const nodeCount = state.replacementNodes.length;
84
225
  if (key >= nodeCount) {
@@ -90,6 +231,9 @@ const getReplacementNode = (state, placeholderId, nodeType) => {
90
231
  !(Array.isArray(replacementNode)
91
232
  ? replacementNode.every(validateReplacement)
92
233
  : validateReplacement(replacementNode))) {
234
+ const nodeType = Array.isArray(replacementNode)
235
+ ? `[${replacementNode.map((n) => n.type)}.join(', ')]`
236
+ : replacementNode?.type;
93
237
  throw new Error(`Validation failed for templated node of type ${nodeType}`);
94
238
  }
95
239
  return replacementNode;
@@ -97,7 +241,7 @@ const getReplacementNode = (state, placeholderId, nodeType) => {
97
241
  const visitors$1 = {
98
242
  Identifier(path, state) {
99
243
  if (path.node?.name.startsWith(PLACEHOLDER_PREFIX)) {
100
- const replacementNode = getReplacementNode(state, path.node.name, path.node.type);
244
+ const replacementNode = getReplacementNode(state, path.node.name);
101
245
  if (replacementNode === null) {
102
246
  path.remove();
103
247
  }
@@ -123,7 +267,7 @@ const visitors$1 = {
123
267
  if (typeof path.node?.value === 'string' &&
124
268
  path.node.value.startsWith(PLACEHOLDER_PREFIX)) {
125
269
  // A literal can only be replaced with a single node
126
- const replacementNode = getReplacementNode(state, path.node.value, path.node.type);
270
+ const replacementNode = getReplacementNode(state, path.node.value);
127
271
  path.replaceWith(replacementNode);
128
272
  }
129
273
  },
@@ -267,10 +411,6 @@ const isIdentOrRenderCall = (node) => {
267
411
  estreeToolkit.is.identifier(node.callee.property) &&
268
412
  node.callee.property.name === 'render'));
269
413
  };
270
- /** Extends a validator to return `true` if the node is `null`. */
271
- function isNullableOf(validator) {
272
- return (node) => node === null || validator(node);
273
- }
274
414
 
275
415
  /*
276
416
  * Copyright (c) 2024, salesforce.com, inc.
@@ -291,11 +431,10 @@ const bImportDeclaration = (esTemplate `
291
431
  const bGenerateMarkup = (esTemplate `
292
432
  export async function* generateMarkup(tagName, props, attrs, slotted) {
293
433
  attrs = attrs ?? {};
294
- ${isNullableOf(estreeToolkit.is.expressionStatement)};
295
434
  const instance = new ${estreeToolkit.is.identifier}({
296
435
  tagName: tagName.toUpperCase(),
297
436
  });
298
- instance[__SYMBOL__SET_INTERNALS](props, __REFLECTED_PROPS__, attrs);
437
+ instance[__SYMBOL__SET_INTERNALS](props, attrs);
299
438
  instance.isConnected = true;
300
439
  if (instance.connectedCallback) {
301
440
  __mutationTracker.enable(instance);
@@ -307,52 +446,18 @@ const bGenerateMarkup = (esTemplate `
307
446
  yield tmplFn.stylesheetScopeTokenHostClass ?? '';
308
447
  yield* __renderAttrs(instance, attrs)
309
448
  yield '>';
310
- yield* tmplFn(props, attrs, slotted, ${1}, instance);
449
+ yield* tmplFn(props, attrs, slotted, ${0}, instance);
311
450
  yield \`</\${tagName}>\`;
312
451
  }
313
452
  `);
314
453
  const bInsertFallbackTmplImport = (esTemplate `
315
454
  import {
316
455
  fallbackTmpl as __fallbackTmpl,
317
- mutationTracker as __mutationTracker,
456
+ mutationTracker as __mutationTracker,
318
457
  renderAttrs as __renderAttrs,
319
458
  SYMBOL__SET_INTERNALS as __SYMBOL__SET_INTERNALS,
320
459
  } from '@lwc/ssr-runtime';
321
460
  `);
322
- const bCreateReflectedPropArr = (esTemplate `
323
- const __REFLECTED_PROPS__ = ${estreeToolkit.is.arrayExpression};
324
- `);
325
- function bReflectedAttrsObj(reflectedPropNames) {
326
- // This will build getter properties for each reflected property. It'll look
327
- // something like this:
328
- //
329
- // get ['aria-checked']() {
330
- // return props.ariaChecked;
331
- // }
332
- //
333
- // The props object will be kept up-to-date with any new values set on the corresponding
334
- // property name in the component instance.
335
- const reflectedAttrAccessors = [];
336
- for (const propName of reflectedPropNames) {
337
- reflectedAttrAccessors.push(estreeToolkit.builders.property('get', estreeToolkit.builders.literal(shared.AriaPropNameToAttrNameMap[propName]), estreeToolkit.builders.functionExpression(null, [], estreeToolkit.builders.blockStatement([
338
- estreeToolkit.builders.returnStatement(estreeToolkit.builders.callExpression(estreeToolkit.builders.identifier('String'), [
339
- estreeToolkit.builders.memberExpression(estreeToolkit.builders.identifier('props'), estreeToolkit.builders.identifier(propName)),
340
- ])),
341
- ]))), estreeToolkit.builders.property('set', estreeToolkit.builders.literal(shared.AriaPropNameToAttrNameMap[propName]), estreeToolkit.builders.functionExpression(null, [estreeToolkit.builders.identifier('val')], estreeToolkit.builders.blockStatement([
342
- estreeToolkit.builders.expressionStatement(estreeToolkit.builders.assignmentExpression('=', estreeToolkit.builders.memberExpression(estreeToolkit.builders.identifier('props'), estreeToolkit.builders.identifier(propName)), estreeToolkit.builders.identifier('val'))),
343
- ]))));
344
- }
345
- // This mutates the `attrs` object, adding the reflected aria attributes that have been
346
- // detected. Example:
347
- //
348
- // attrs = {
349
- // ...attrs,
350
- // get ['aria-checked']() {
351
- // return props.ariaChecked;
352
- // }
353
- // }
354
- return estreeToolkit.builders.expressionStatement(estreeToolkit.builders.assignmentExpression('=', estreeToolkit.builders.identifier('attrs'), estreeToolkit.builders.objectExpression([estreeToolkit.builders.spreadElement(estreeToolkit.builders.identifier('attrs')), ...reflectedAttrAccessors])));
355
- }
356
461
  /**
357
462
  * This builds a generator function `generateMarkup` and adds it to the component JS's
358
463
  * compilation output. `generateMarkup` acts as the glue between component JS and its
@@ -372,17 +477,11 @@ function addGenerateMarkupExport(program, state, filename) {
372
477
  ? estreeToolkit.builders.callExpression(estreeToolkit.builders.memberExpression(estreeToolkit.builders.identifier('instance'), estreeToolkit.builders.identifier('render')), [])
373
478
  : estreeToolkit.builders.identifier('tmpl');
374
479
  if (!tmplExplicitImports) {
375
- const defaultTmplPath = `./${node_path.basename(filename, 'js')}html`;
480
+ const defaultTmplPath = `./${node_path.parse(filename).name}.html`;
376
481
  program.body.unshift(bImportDeclaration(estreeToolkit.builders.identifier('tmpl'), estreeToolkit.builders.literal(defaultTmplPath)));
377
482
  }
378
- let attrsAugmentation = null;
379
- if (state.reflectedPropsInPlay.size) {
380
- attrsAugmentation = bReflectedAttrsObj([...state.reflectedPropsInPlay]);
381
- }
382
- const reflectedPropArr = estreeToolkit.builders.arrayExpression([...state.reflectedPropsInPlay].map((propName) => estreeToolkit.builders.literal(propName)));
383
483
  program.body.unshift(bInsertFallbackTmplImport());
384
- program.body.push(bCreateReflectedPropArr(reflectedPropArr));
385
- program.body.push(bGenerateMarkup(attrsAugmentation, classIdentifier, renderCall));
484
+ program.body.push(bGenerateMarkup(classIdentifier, renderCall));
386
485
  }
387
486
 
388
487
  /*
@@ -393,12 +492,6 @@ function addGenerateMarkupExport(program, state, filename) {
393
492
  */
394
493
  const visitors = {
395
494
  $: { scope: true },
396
- Identifier(path, state) {
397
- const reflectedAttrName = shared.AriaPropNameToAttrNameMap[path.node.name];
398
- if (reflectedAttrName) {
399
- state.reflectedPropsInPlay.add(path.node.name);
400
- }
401
- },
402
495
  ImportDeclaration(path, state) {
403
496
  if (!path.node || !path.node.source.value || typeof path.node.source.value !== 'string') {
404
497
  return;
@@ -474,8 +567,8 @@ const visitors = {
474
567
  }
475
568
  },
476
569
  };
477
- function compileJS(src, filename) {
478
- const ast = meriyah.parseModule(src, {
570
+ function compileJS(src, filename, compilationMode) {
571
+ let ast = meriyah.parseModule(src, {
479
572
  module: true,
480
573
  next: true,
481
574
  });
@@ -492,7 +585,6 @@ function compileJS(src, filename) {
492
585
  tmplExplicitImports: null,
493
586
  cssExplicitImports: null,
494
587
  staticStylesheetIds: null,
495
- reflectedPropsInPlay: new Set(),
496
588
  };
497
589
  estreeToolkit.traverse(ast, visitors, state);
498
590
  if (!state.isLWC) {
@@ -507,6 +599,9 @@ function compileJS(src, filename) {
507
599
  throw new Error(`Unimplemented static stylesheets, but found:\n${[...state.cssExplicitImports].join(' \n')}`);
508
600
  }
509
601
  addGenerateMarkupExport(ast, state, filename);
602
+ if (compilationMode === 'async' || compilationMode === 'sync') {
603
+ ast = transmogrify(ast, compilationMode);
604
+ }
510
605
  return {
511
606
  code: astring.generate(ast, {}),
512
607
  };
@@ -1676,7 +1771,7 @@ var CompilerMetrics;
1676
1771
  CompilerMetrics["LWCSpreadDirective"] = "lwc-spread-directive";
1677
1772
  CompilerMetrics["DynamicImportTransform"] = "dynamic-import-transform";
1678
1773
  })(CompilerMetrics || (CompilerMetrics = {}));
1679
- /** version: 8.3.0 */
1774
+ /** version: 8.4.0 */
1680
1775
 
1681
1776
  /*
1682
1777
  * Copyright (c) 2024, Salesforce, Inc.
@@ -1718,7 +1813,7 @@ function addScopeTokenDeclarations(program, filename, namespace, componentName)
1718
1813
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
1719
1814
  */
1720
1815
  const bImportHtmlEscape = (esTemplate `
1721
- import { htmlEscape } from '@lwc/shared';
1816
+ import { htmlEscape } from '@lwc/ssr-runtime';
1722
1817
  `);
1723
1818
  const importHtmlEscapeKey = 'import:htmlEscape';
1724
1819
  // This is a mostly-correct regular expression will only match if the entire string
@@ -1756,6 +1851,44 @@ function optimizeAdjacentYieldStmts(statements) {
1756
1851
  })
1757
1852
  .filter((el) => el !== null);
1758
1853
  }
1854
+ function bAttributeValue(node, attrName) {
1855
+ if (!('attributes' in node)) {
1856
+ throw new TypeError(`Cannot get attribute value from ${node.type}`);
1857
+ }
1858
+ const nameAttrValue = node.attributes.find((attr) => attr.name === attrName)?.value;
1859
+ if (!nameAttrValue) {
1860
+ return estreeToolkit.builders.literal(null);
1861
+ }
1862
+ else if (nameAttrValue.type === 'Literal') {
1863
+ const name = typeof nameAttrValue.value === 'string' ? nameAttrValue.value : '';
1864
+ return estreeToolkit.builders.literal(name);
1865
+ }
1866
+ else {
1867
+ return estreeToolkit.builders.memberExpression(estreeToolkit.builders.literal('instance'), nameAttrValue);
1868
+ }
1869
+ }
1870
+ function getRootMemberExpression$2(node) {
1871
+ return node.object.type === 'MemberExpression' ? getRootMemberExpression$2(node.object) : node;
1872
+ }
1873
+ function getRootIdentifier$1(node) {
1874
+ const rootMemberExpression = getRootMemberExpression$2(node);
1875
+ return estreeToolkit.is.identifier(rootMemberExpression?.object) ? rootMemberExpression.object : null;
1876
+ }
1877
+ /**
1878
+ * Given an expression in a context, return an expression that may be scoped to that context.
1879
+ * For example, for the expression `foo`, it will typically be `instance.foo`, but if we're
1880
+ * inside a `for:each` block then the `foo` variable may refer to the scoped `foo`,
1881
+ * e.g. `<template for:each={foos} for:item="foo">`
1882
+ * @param expression
1883
+ */
1884
+ function getScopedExpression(expression, cxt) {
1885
+ const scopeReferencedId = estreeToolkit.is.memberExpression(expression)
1886
+ ? getRootIdentifier$1(expression)
1887
+ : null;
1888
+ return cxt.isLocalVar(scopeReferencedId?.name)
1889
+ ? expression
1890
+ : estreeToolkit.builders.memberExpression(estreeToolkit.builders.identifier('instance'), expression);
1891
+ }
1759
1892
 
1760
1893
  /*
1761
1894
  * Copyright (c) 2024, salesforce.com, inc.
@@ -1778,8 +1911,8 @@ const Comment = function Comment(node, cxt) {
1778
1911
  * SPDX-License-Identifier: MIT
1779
1912
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
1780
1913
  */
1781
- function getRootMemberExpression$2(node) {
1782
- return node.object.type === 'MemberExpression' ? getRootMemberExpression$2(node.object) : node;
1914
+ function getRootMemberExpression$1(node) {
1915
+ return node.object.type === 'MemberExpression' ? getRootMemberExpression$1(node.object) : node;
1783
1916
  }
1784
1917
  function expressionIrToEs(node, cxt) {
1785
1918
  if (node.type === 'Identifier') {
@@ -1790,7 +1923,7 @@ function expressionIrToEs(node, cxt) {
1790
1923
  }
1791
1924
  else if (node.type === 'MemberExpression') {
1792
1925
  const nodeClone = structuredClone(node);
1793
- const rootMemberExpr = getRootMemberExpression$2(nodeClone);
1926
+ const rootMemberExpr = getRootMemberExpression$1(nodeClone);
1794
1927
  if (!cxt.isLocalVar(rootMemberExpr.object.name)) {
1795
1928
  rootMemberExpr.object = estreeToolkit.builders.memberExpression(estreeToolkit.builders.identifier('instance'), rootMemberExpr.object);
1796
1929
  }
@@ -1809,12 +1942,29 @@ const bYieldFromChildGenerator = (esTemplateWithYield `
1809
1942
  {
1810
1943
  const childProps = ${estreeToolkit.is.objectExpression};
1811
1944
  const childAttrs = ${estreeToolkit.is.objectExpression};
1812
- async function* childSlottedContentGenerator() {
1813
- ${estreeToolkit.is.statement}
1945
+ const slottedContent = {
1946
+ light: Object.create(null),
1947
+ shadow: async function* () {
1948
+ ${ /* shadow slot content */estreeToolkit.is.statement}
1949
+ }
1814
1950
  };
1815
- yield* ${estreeToolkit.is.identifier}(${estreeToolkit.is.literal}, childProps, childAttrs, childSlottedContentGenerator);
1951
+ function addContent(name, fn) {
1952
+ let contentList = slottedContent.light[name]
1953
+ if (contentList) {
1954
+ contentList.push(fn)
1955
+ } else {
1956
+ slottedContent.light[name] = [fn]
1957
+ }
1958
+ }
1959
+ ${ /* addContent statements */estreeToolkit.is.callExpression}
1960
+ yield* ${estreeToolkit.is.identifier}(${estreeToolkit.is.literal}, childProps, childAttrs, slottedContent);
1816
1961
  }
1817
1962
  `);
1963
+ const bAddContent = (esTemplate `
1964
+ addContent(${ /* slot name */estreeToolkit.is.expression} ?? "", async function* () {
1965
+ ${ /* slot content */estreeToolkit.is.statement}
1966
+ });
1967
+ `);
1818
1968
  const bImportGenerateMarkup = (localName, importPath) => estreeToolkit.builders.importDeclaration([estreeToolkit.builders.importSpecifier(estreeToolkit.builders.identifier('generateMarkup'), estreeToolkit.builders.identifier(localName))], estreeToolkit.builders.literal(importPath));
1819
1969
  function getChildAttrsOrProps(attrs, cxt) {
1820
1970
  const objectAttrsOrProps = attrs.map((attr) => {
@@ -1858,8 +2008,24 @@ const Component = function Component(node, cxt) {
1858
2008
  cxt.hoist(componentImport, childGeneratorLocalName);
1859
2009
  const childTagName = node.name;
1860
2010
  const attributes = [...node.attributes, ...reflectAriaPropsAsAttrs(node.properties)];
2011
+ const shadowSlotContent = optimizeAdjacentYieldStmts(irChildrenToEs(node.children, cxt));
2012
+ const lightSlotContent = node.children.map((child) => {
2013
+ if ('attributes' in child) {
2014
+ const slotName = bAttributeValue(child, 'slot');
2015
+ // FIXME: We don't know what happens for slot attributes inside an lwc:if block
2016
+ // Light DOM slots do not actually render the `slot` attribute.
2017
+ const clone = immer.produce(child, (draft) => {
2018
+ draft.attributes = draft.attributes.filter((attr) => attr.name !== 'slot');
2019
+ });
2020
+ const slotContent = irToEs(clone, cxt);
2021
+ return bAddContent(slotName, slotContent);
2022
+ }
2023
+ else {
2024
+ return bAddContent(estreeToolkit.builders.literal(''), irToEs(child, cxt));
2025
+ }
2026
+ });
1861
2027
  return [
1862
- bYieldFromChildGenerator(getChildAttrsOrProps(node.properties, cxt), getChildAttrsOrProps(attributes, cxt), optimizeAdjacentYieldStmts(irChildrenToEs(node.children, cxt)), estreeToolkit.builders.identifier(childGeneratorLocalName), estreeToolkit.builders.literal(childTagName)),
2028
+ bYieldFromChildGenerator(getChildAttrsOrProps(node.properties, cxt), getChildAttrsOrProps(attributes, cxt), shadowSlotContent, lightSlotContent, estreeToolkit.builders.identifier(childGeneratorLocalName), estreeToolkit.builders.literal(childTagName)),
1863
2029
  ];
1864
2030
  };
1865
2031
 
@@ -1873,12 +2039,12 @@ const bYield$1 = (expr) => estreeToolkit.builders.expressionStatement(estreeTool
1873
2039
  const bConditionalLiveYield = (esTemplateWithYield `
1874
2040
  {
1875
2041
  const prefix = (${ /* isClass */estreeToolkit.is.literal} && stylesheetScopeTokenClassPrefix) || '';
1876
- const attrOrPropValue = ${estreeToolkit.is.expression};
1877
- const valueType = typeof attrOrPropValue;
1878
- if (attrOrPropValue && (valueType === 'string' || valueType === 'boolean')) {
1879
- yield ' ' + ${estreeToolkit.is.literal};
2042
+ const attrValue = ${ /* attribute value expression */estreeToolkit.is.expression};
2043
+ const valueType = typeof attrValue;
2044
+ if (attrValue && (valueType === 'string' || valueType === 'boolean')) {
2045
+ yield ' ' + ${ /* attribute name */estreeToolkit.is.literal};
1880
2046
  if (valueType === 'string') {
1881
- yield \`="\${prefix}\${htmlEscape(attrOrPropValue, true)}"\`;
2047
+ yield \`="\${prefix}\${htmlEscape(attrValue, true)}"\`;
1882
2048
  }
1883
2049
  }
1884
2050
  }
@@ -1910,9 +2076,9 @@ function yieldAttrOrPropLiteralValue(name, valueNode, isClass) {
1910
2076
  }
1911
2077
  throw new Error(`Unknown attr/prop literal: ${type}`);
1912
2078
  }
1913
- function yieldAttrOrPropLiveValue(name, value, isClass) {
1914
- const instanceMemberRef = estreeToolkit.builders.memberExpression(estreeToolkit.builders.identifier('instance'), value);
1915
- return [bConditionalLiveYield(estreeToolkit.builders.literal(isClass), instanceMemberRef, estreeToolkit.builders.literal(name))];
2079
+ function yieldAttrOrPropLiveValue(name, value, isClass, cxt) {
2080
+ const scopedExpression = getScopedExpression(value, cxt);
2081
+ return [bConditionalLiveYield(estreeToolkit.builders.literal(isClass), scopedExpression, estreeToolkit.builders.literal(name))];
1916
2082
  }
1917
2083
  function reorderAttributes(attrs, props) {
1918
2084
  let classAttr = null;
@@ -1951,7 +2117,7 @@ const Element = function Element(node, cxt) {
1951
2117
  return yieldAttrOrPropLiteralValue(name, value, isClass);
1952
2118
  }
1953
2119
  else {
1954
- return yieldAttrOrPropLiveValue(name, value, isClass);
2120
+ return yieldAttrOrPropLiveValue(name, value, isClass, cxt);
1955
2121
  }
1956
2122
  });
1957
2123
  if (shared.isVoidElement(node.name, shared.HTML_NAMESPACE)) {
@@ -1988,13 +2154,6 @@ const Element = function Element(node, cxt) {
1988
2154
  * SPDX-License-Identifier: MIT
1989
2155
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
1990
2156
  */
1991
- function getRootMemberExpression$1(node) {
1992
- return node.object.type === 'MemberExpression' ? getRootMemberExpression$1(node.object) : node;
1993
- }
1994
- function getRootIdentifier$1(node) {
1995
- const rootMemberExpression = getRootMemberExpression$1(node);
1996
- return estreeToolkit.is.identifier(rootMemberExpression?.object) ? rootMemberExpression.object : null;
1997
- }
1998
2157
  const bForOfYieldFrom$1 = (esTemplate `
1999
2158
  for (let [${estreeToolkit.is.identifier}, ${estreeToolkit.is.identifier}] of Object.entries(${estreeToolkit.is.expression} ?? {})) {
2000
2159
  ${estreeToolkit.is.statement};
@@ -2009,12 +2168,7 @@ const ForEach = function ForEach(node, cxt) {
2009
2168
  });
2010
2169
  cxt.popLocalVars();
2011
2170
  const expression = node.expression;
2012
- const scopeReferencedId = estreeToolkit.is.memberExpression(expression)
2013
- ? getRootIdentifier$1(expression)
2014
- : null;
2015
- const iterable = cxt.isLocalVar(scopeReferencedId?.name)
2016
- ? node.expression
2017
- : estreeToolkit.builders.memberExpression(estreeToolkit.builders.identifier('instance'), node.expression);
2171
+ const iterable = getScopedExpression(expression, cxt);
2018
2172
  return [
2019
2173
  bForOfYieldFrom$1(estreeToolkit.builders.identifier(forIndexId), estreeToolkit.builders.identifier(forItemId), iterable, optimizeAdjacentYieldStmts(forEachStatements)),
2020
2174
  ];
@@ -2110,9 +2264,11 @@ const bConditionalSlot = (esTemplateWithYield `
2110
2264
  // start bookend HTML comment
2111
2265
  yield '<!---->';
2112
2266
 
2113
- const generator = slottedContent[${ /* slotName */estreeToolkit.is.expression} ?? ""];
2114
- if (generator) {
2115
- yield* generator();
2267
+ const generators = slottedContent?.light[${ /* slotName */estreeToolkit.is.expression} ?? ""];
2268
+ if (generators) {
2269
+ for (const generator of generators) {
2270
+ yield* generator();
2271
+ }
2116
2272
  } else {
2117
2273
  // If we're in this else block, then the generator _must_ have yielded
2118
2274
  // something. It's impossible for a slottedContent["foo"] to exist
@@ -2129,18 +2285,7 @@ const bConditionalSlot = (esTemplateWithYield `
2129
2285
  }
2130
2286
  `);
2131
2287
  const Slot = function Slot(node, ctx) {
2132
- const nameAttrValue = node.attributes.find((attr) => attr.name === 'name')?.value;
2133
- let slotName;
2134
- if (!nameAttrValue) {
2135
- slotName = estreeToolkit.builders.literal('');
2136
- }
2137
- else if (nameAttrValue.type === 'Literal') {
2138
- const name = typeof nameAttrValue.value === 'string' ? nameAttrValue.value : '';
2139
- slotName = estreeToolkit.builders.literal(name);
2140
- }
2141
- else {
2142
- slotName = estreeToolkit.builders.memberExpression(estreeToolkit.builders.literal('instance'), nameAttrValue);
2143
- }
2288
+ const slotName = bAttributeValue(node, 'name');
2144
2289
  // FIXME: avoid serializing the slot's children twice
2145
2290
  const slotAst = Element(node, ctx);
2146
2291
  const slotChildren = irChildrenToEs(node.children, ctx);
@@ -2327,14 +2472,13 @@ const bExportTemplate = (esTemplate `
2327
2472
 
2328
2473
  if (!isLightDom) {
2329
2474
  yield '</template>';
2330
- }
2331
-
2332
- if (slottedContent) {
2333
- yield* slottedContent();
2475
+ if (slottedContent?.shadow) {
2476
+ yield* slottedContent.shadow();
2477
+ }
2334
2478
  }
2335
2479
  }
2336
2480
  `);
2337
- function compileTemplate(src, filename, options) {
2481
+ function compileTemplate(src, filename, options, compilationMode) {
2338
2482
  const { root, warnings } = templateCompiler.parse(src, {
2339
2483
  // `options` is from @lwc/compiler, and may have flags that @lwc/template-compiler doesn't
2340
2484
  // know about, so we must explicitly extract the relevant props.
@@ -2374,10 +2518,13 @@ function compileTemplate(src, filename, options) {
2374
2518
  bStyleValidationImport(),
2375
2519
  bExportTemplate(optimizeAdjacentYieldStmts(statements)),
2376
2520
  ];
2377
- const program = estreeToolkit.builders.program(moduleBody, 'module');
2521
+ let program = estreeToolkit.builders.program(moduleBody, 'module');
2378
2522
  addScopeTokenDeclarations(program, filename, options.namespace, options.name);
2379
2523
  const stylesheetImports = getStylesheetImports(filename);
2380
2524
  program.body.unshift(...stylesheetImports);
2525
+ if (compilationMode === 'async' || compilationMode === 'sync') {
2526
+ program = transmogrify(program, compilationMode);
2527
+ }
2381
2528
  return {
2382
2529
  code: astring.generate(program, {}),
2383
2530
  };
@@ -2389,16 +2536,16 @@ function compileTemplate(src, filename, options) {
2389
2536
  * SPDX-License-Identifier: MIT
2390
2537
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
2391
2538
  */
2392
- function compileComponentForSSR(src, filename, _options) {
2393
- const { code } = compileJS(src, filename);
2539
+ function compileComponentForSSR(src, filename, _options, mode = 'asyncYield') {
2540
+ const { code } = compileJS(src, filename, mode);
2394
2541
  return { code, map: undefined };
2395
2542
  }
2396
- function compileTemplateForSSR(src, filename, options) {
2397
- const { code } = compileTemplate(src, filename, options);
2543
+ function compileTemplateForSSR(src, filename, options, mode = 'asyncYield') {
2544
+ const { code } = compileTemplate(src, filename, options, mode);
2398
2545
  return { code, map: undefined };
2399
2546
  }
2400
2547
 
2401
2548
  exports.compileComponentForSSR = compileComponentForSSR;
2402
2549
  exports.compileTemplateForSSR = compileTemplateForSSR;
2403
- /** version: 8.3.0 */
2550
+ /** version: 8.4.0 */
2404
2551
  //# sourceMappingURL=index.cjs.js.map