@lwc/ssr-compiler 8.3.0 → 8.5.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.
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');
14
12
  var node_path = require('node:path');
13
+ var acorn = require('acorn');
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.
@@ -69,6 +210,72 @@ function catalogTmplImport(path, state) {
69
210
  state.tmplExplicitImports.set(specifier.local.name, source.value);
70
211
  }
71
212
 
213
+ /*
214
+ * Copyright (c) 2024, salesforce.com, inc.
215
+ * All rights reserved.
216
+ * SPDX-License-Identifier: MIT
217
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
218
+ */
219
+ /** Creates a default import statement, e.g. `import pkg from "pkg"` */
220
+ const bImportDefaultDeclaration = (name, source) => estreeToolkit.builders.importDeclaration([estreeToolkit.builders.importDefaultSpecifier(estreeToolkit.builders.identifier(name))], estreeToolkit.builders.literal(source));
221
+ /**
222
+ * Creates an import statement, e.g. `import { foo, bar as $bar$ } from "pkg"`.
223
+ * Does not support default or namespace imports (`import pkg` or `import * as pkg`).
224
+ * @param imports names to be imported; values can be a string (plain import) or object (aliased)
225
+ * @param source source location to import from; defaults to @lwc/ssr-runtime
226
+ */
227
+ const bImportDeclaration = (imports, source = '@lwc/ssr-runtime') => {
228
+ const specifiers = imports
229
+ .flatMap((imp) => (typeof imp === 'string' ? [[imp, imp]] : Object.entries(imp)))
230
+ .map(([imported, local]) => estreeToolkit.builders.importSpecifier(estreeToolkit.builders.identifier(imported), estreeToolkit.builders.identifier(local)));
231
+ return estreeToolkit.builders.importDeclaration(specifiers, estreeToolkit.builders.literal(source));
232
+ };
233
+
234
+ /*
235
+ * Copyright (c) 2024, salesforce.com, inc.
236
+ * All rights reserved.
237
+ * SPDX-License-Identifier: MIT
238
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
239
+ */
240
+ function catalogAndReplaceStyleImports(path, state) {
241
+ const specifier = path.node.specifiers[0];
242
+ if (typeof path.node.source.value !== 'string' ||
243
+ !path.node.source.value.endsWith('.css') ||
244
+ path.node.specifiers.length !== 1 ||
245
+ specifier.type !== 'ImportDefaultSpecifier') {
246
+ return;
247
+ }
248
+ // Any file ending in `*.scoped.css` which is directly imported into a Component `*.js` file (and assumed
249
+ // to be used for `static stylesheets`) is assumed to be scoped, so needs to be marked as such with a query param.
250
+ // Outside of SSR, this is done by `@lwc/babel-plugin-component`, so we need to emulate its behavior. The goal here
251
+ // is for `@lwc/template-compiler` to know to add `stylesheet.$scoped$ = true` to its compiled output, which it
252
+ // detects using the query param.
253
+ if (path.node?.source.value.endsWith('.scoped.css')) {
254
+ path.replaceWith(estreeToolkit.builders.importDeclaration(path.node.specifiers, estreeToolkit.builders.literal(path.node.source.value + '?scoped=true')));
255
+ }
256
+ state.cssExplicitImports = state.cssExplicitImports ?? new Map();
257
+ state.cssExplicitImports.set(specifier.local.name, path.node.source.value);
258
+ }
259
+ /**
260
+ * This adds implicit style imports to the compiled component artifact.
261
+ */
262
+ function getStylesheetImports(filepath) {
263
+ const moduleName = /(?<moduleName>[^/]+)\.html$/.exec(filepath)?.groups?.moduleName;
264
+ if (!moduleName) {
265
+ throw new Error(`Could not determine module name from file path: ${filepath}`);
266
+ }
267
+ return [
268
+ bImportDefaultDeclaration('defaultStylesheets', `./${moduleName}.css`),
269
+ bImportDefaultDeclaration('defaultScopedStylesheets', `./${moduleName}.scoped.css?scoped=true`),
270
+ ];
271
+ }
272
+ function catalogStaticStylesheets(ids, state) {
273
+ state.staticStylesheetIds = state.staticStylesheetIds ?? new Set();
274
+ for (const id of ids) {
275
+ state.staticStylesheetIds.add(id);
276
+ }
277
+ }
278
+
72
279
  /*
73
280
  * Copyright (c) 2024, salesforce.com, inc.
74
281
  * All rights reserved.
@@ -78,7 +285,7 @@ function catalogTmplImport(path, state) {
78
285
  /** Placeholder value to use to opt out of validation. */
79
286
  const NO_VALIDATION = false;
80
287
  const PLACEHOLDER_PREFIX = `__ESTEMPLATE_${Math.random().toString().slice(2)}_PLACEHOLDER__`;
81
- const getReplacementNode = (state, placeholderId, nodeType) => {
288
+ const getReplacementNode = (state, placeholderId) => {
82
289
  const key = Number(placeholderId.slice(PLACEHOLDER_PREFIX.length));
83
290
  const nodeCount = state.replacementNodes.length;
84
291
  if (key >= nodeCount) {
@@ -90,6 +297,9 @@ const getReplacementNode = (state, placeholderId, nodeType) => {
90
297
  !(Array.isArray(replacementNode)
91
298
  ? replacementNode.every(validateReplacement)
92
299
  : validateReplacement(replacementNode))) {
300
+ const nodeType = Array.isArray(replacementNode)
301
+ ? `[${replacementNode.map((n) => n.type)}.join(', ')]`
302
+ : replacementNode?.type;
93
303
  throw new Error(`Validation failed for templated node of type ${nodeType}`);
94
304
  }
95
305
  return replacementNode;
@@ -97,7 +307,7 @@ const getReplacementNode = (state, placeholderId, nodeType) => {
97
307
  const visitors$1 = {
98
308
  Identifier(path, state) {
99
309
  if (path.node?.name.startsWith(PLACEHOLDER_PREFIX)) {
100
- const replacementNode = getReplacementNode(state, path.node.name, path.node.type);
310
+ const replacementNode = getReplacementNode(state, path.node.name);
101
311
  if (replacementNode === null) {
102
312
  path.remove();
103
313
  }
@@ -123,7 +333,7 @@ const visitors$1 = {
123
333
  if (typeof path.node?.value === 'string' &&
124
334
  path.node.value.startsWith(PLACEHOLDER_PREFIX)) {
125
335
  // A literal can only be replaced with a single node
126
- const replacementNode = getReplacementNode(state, path.node.value, path.node.type);
336
+ const replacementNode = getReplacementNode(state, path.node.value);
127
337
  path.replaceWith(replacementNode);
128
338
  }
129
339
  },
@@ -213,52 +423,6 @@ function esTemplateWithYield(javascriptSegments, ...validators) {
213
423
  * SPDX-License-Identifier: MIT
214
424
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
215
425
  */
216
- const bDefaultStyleImport = (esTemplate `
217
- import defaultStylesheets from '${estreeToolkit.is.literal}';
218
- `);
219
- const bDefaultScopedStyleImport = (esTemplate `
220
- import defaultScopedStylesheets from '${estreeToolkit.is.literal}';
221
- `);
222
- function catalogStyleImport(path, state) {
223
- const specifier = path.node.specifiers[0];
224
- if (typeof path.node.source.value !== 'string' ||
225
- !path.node.source.value.endsWith('.css') ||
226
- path.node.specifiers.length !== 1 ||
227
- specifier.type !== 'ImportDefaultSpecifier') {
228
- return;
229
- }
230
- state.cssExplicitImports = state.cssExplicitImports ?? new Map();
231
- state.cssExplicitImports.set(specifier.local.name, path.node.source.value);
232
- }
233
- /**
234
- * This adds implicit style imports to the compiled component artifact.
235
- */
236
- function getStylesheetImports(filepath) {
237
- const moduleName = /(?<moduleName>[^/]+)\.html$/.exec(filepath)?.groups?.moduleName;
238
- if (!moduleName) {
239
- throw new Error(`Could not determine module name from file path: ${filepath}`);
240
- }
241
- return [
242
- bDefaultStyleImport(estreeToolkit.builders.literal(`./${moduleName}.css`)),
243
- bDefaultScopedStyleImport(estreeToolkit.builders.literal(`./${moduleName}.scoped.css?scoped=true`)),
244
- ];
245
- }
246
- function catalogStaticStylesheets(ids, state) {
247
- state.staticStylesheetIds = state.staticStylesheetIds ?? new Set();
248
- for (const id of ids) {
249
- state.staticStylesheetIds.add(id);
250
- }
251
- }
252
-
253
- /*
254
- * Copyright (c) 2024, salesforce.com, inc.
255
- * All rights reserved.
256
- * SPDX-License-Identifier: MIT
257
- * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
258
- */
259
- const isStringLiteral = (node) => {
260
- return estreeToolkit.is.literal(node) && typeof node.value === 'string';
261
- };
262
426
  /** Returns `true` if the node is an identifier or `<something>.render()`. */
263
427
  const isIdentOrRenderCall = (node) => {
264
428
  return (estreeToolkit.is.identifier(node) ||
@@ -267,20 +431,6 @@ const isIdentOrRenderCall = (node) => {
267
431
  estreeToolkit.is.identifier(node.callee.property) &&
268
432
  node.callee.property.name === 'render'));
269
433
  };
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
-
275
- /*
276
- * Copyright (c) 2024, salesforce.com, inc.
277
- * All rights reserved.
278
- * SPDX-License-Identifier: MIT
279
- * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
280
- */
281
- const bImportDeclaration = (esTemplate `
282
- import ${estreeToolkit.is.identifier} from "${isStringLiteral}";
283
- `);
284
434
 
285
435
  /*
286
436
  * Copyright (c) 2024, salesforce.com, inc.
@@ -291,11 +441,10 @@ const bImportDeclaration = (esTemplate `
291
441
  const bGenerateMarkup = (esTemplate `
292
442
  export async function* generateMarkup(tagName, props, attrs, slotted) {
293
443
  attrs = attrs ?? {};
294
- ${isNullableOf(estreeToolkit.is.expressionStatement)};
295
- const instance = new ${estreeToolkit.is.identifier}({
444
+ const instance = new ${ /* Component class */estreeToolkit.is.identifier}({
296
445
  tagName: tagName.toUpperCase(),
297
446
  });
298
- instance[__SYMBOL__SET_INTERNALS](props, __REFLECTED_PROPS__, attrs);
447
+ instance[__SYMBOL__SET_INTERNALS](props, attrs);
299
448
  instance.isConnected = true;
300
449
  if (instance.connectedCallback) {
301
450
  __mutationTracker.enable(instance);
@@ -304,55 +453,21 @@ const bGenerateMarkup = (esTemplate `
304
453
  }
305
454
  const tmplFn = ${isIdentOrRenderCall} ?? __fallbackTmpl;
306
455
  yield \`<\${tagName}\`;
307
- yield tmplFn.stylesheetScopeTokenHostClass ?? '';
308
- yield* __renderAttrs(instance, attrs)
456
+ const shouldRenderScopeToken = tmplFn.hasScopedStylesheets || hasScopedStaticStylesheets(${ /*Component class */0});
457
+ if (shouldRenderScopeToken) {
458
+ yield \` class="\${tmplFn.stylesheetScopeToken}-host"\`;
459
+ }
460
+ yield* __renderAttrs(instance, attrs);
309
461
  yield '>';
310
- yield* tmplFn(props, attrs, slotted, ${1}, instance);
462
+ yield* tmplFn(props, attrs, slotted, ${0}, instance);
311
463
  yield \`</\${tagName}>\`;
312
464
  }
313
465
  `);
314
- const bInsertFallbackTmplImport = (esTemplate `
315
- import {
316
- fallbackTmpl as __fallbackTmpl,
317
- mutationTracker as __mutationTracker,
318
- renderAttrs as __renderAttrs,
319
- SYMBOL__SET_INTERNALS as __SYMBOL__SET_INTERNALS,
320
- } from '@lwc/ssr-runtime';
321
- `);
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
- ]))));
466
+ const bAssignGenerateMarkupToComponentClass = (esTemplate `
467
+ {
468
+ ${ /* lwcClassName */estreeToolkit.is.identifier}[__SYMBOL__GENERATE_MARKUP] = generateMarkup;
344
469
  }
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
- }
470
+ `);
356
471
  /**
357
472
  * This builds a generator function `generateMarkup` and adds it to the component JS's
358
473
  * compilation output. `generateMarkup` acts as the glue between component JS and its
@@ -372,17 +487,31 @@ function addGenerateMarkupExport(program, state, filename) {
372
487
  ? estreeToolkit.builders.callExpression(estreeToolkit.builders.memberExpression(estreeToolkit.builders.identifier('instance'), estreeToolkit.builders.identifier('render')), [])
373
488
  : estreeToolkit.builders.identifier('tmpl');
374
489
  if (!tmplExplicitImports) {
375
- const defaultTmplPath = `./${node_path.basename(filename, 'js')}html`;
376
- program.body.unshift(bImportDeclaration(estreeToolkit.builders.identifier('tmpl'), estreeToolkit.builders.literal(defaultTmplPath)));
377
- }
378
- let attrsAugmentation = null;
379
- if (state.reflectedPropsInPlay.size) {
380
- attrsAugmentation = bReflectedAttrsObj([...state.reflectedPropsInPlay]);
490
+ const defaultTmplPath = `./${node_path.parse(filename).name}.html`;
491
+ program.body.unshift(bImportDefaultDeclaration('tmpl', defaultTmplPath));
381
492
  }
382
- const reflectedPropArr = estreeToolkit.builders.arrayExpression([...state.reflectedPropsInPlay].map((propName) => estreeToolkit.builders.literal(propName)));
383
- program.body.unshift(bInsertFallbackTmplImport());
384
- program.body.push(bCreateReflectedPropArr(reflectedPropArr));
385
- program.body.push(bGenerateMarkup(attrsAugmentation, classIdentifier, renderCall));
493
+ program.body.unshift(bImportDeclaration([
494
+ {
495
+ fallbackTmpl: '__fallbackTmpl',
496
+ mutationTracker: '__mutationTracker',
497
+ renderAttrs: '__renderAttrs',
498
+ SYMBOL__SET_INTERNALS: '__SYMBOL__SET_INTERNALS',
499
+ },
500
+ ]));
501
+ program.body.unshift(bImportDeclaration(['hasScopedStaticStylesheets']));
502
+ program.body.push(bGenerateMarkup(classIdentifier, renderCall));
503
+ }
504
+ /**
505
+ * Attach the `generateMarkup` function to the Component class so that it can be found later
506
+ * during `renderComponent`.
507
+ */
508
+ function assignGenerateMarkupToComponent(program, state) {
509
+ program.body.unshift(bImportDeclaration([
510
+ {
511
+ SYMBOL__GENERATE_MARKUP: '__SYMBOL__GENERATE_MARKUP',
512
+ },
513
+ ]));
514
+ program.body.push(bAssignGenerateMarkupToComponentClass(estreeToolkit.builders.identifier(state.lwcClassName)));
386
515
  }
387
516
 
388
517
  /*
@@ -393,19 +522,13 @@ function addGenerateMarkupExport(program, state, filename) {
393
522
  */
394
523
  const visitors = {
395
524
  $: { 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
525
  ImportDeclaration(path, state) {
403
526
  if (!path.node || !path.node.source.value || typeof path.node.source.value !== 'string') {
404
527
  return;
405
528
  }
406
529
  replaceLwcImport(path, state);
407
530
  catalogTmplImport(path, state);
408
- catalogStyleImport(path, state);
531
+ catalogAndReplaceStyleImports(path, state);
409
532
  },
410
533
  ClassDeclaration(path, state) {
411
534
  if (!path.node?.superClass) {
@@ -474,8 +597,8 @@ const visitors = {
474
597
  }
475
598
  },
476
599
  };
477
- function compileJS(src, filename) {
478
- const ast = meriyah.parseModule(src, {
600
+ function compileJS(src, filename, compilationMode) {
601
+ let ast = meriyah.parseModule(src, {
479
602
  module: true,
480
603
  next: true,
481
604
  });
@@ -492,7 +615,6 @@ function compileJS(src, filename) {
492
615
  tmplExplicitImports: null,
493
616
  cssExplicitImports: null,
494
617
  staticStylesheetIds: null,
495
- reflectedPropsInPlay: new Set(),
496
618
  };
497
619
  estreeToolkit.traverse(ast, visitors, state);
498
620
  if (!state.isLWC) {
@@ -503,10 +625,11 @@ function compileJS(src, filename) {
503
625
  code: astring.generate(ast, {}),
504
626
  };
505
627
  }
506
- if (state.cssExplicitImports || state.staticStylesheetIds) {
507
- throw new Error(`Unimplemented static stylesheets, but found:\n${[...state.cssExplicitImports].join(' \n')}`);
508
- }
509
628
  addGenerateMarkupExport(ast, state, filename);
629
+ assignGenerateMarkupToComponent(ast, state);
630
+ if (compilationMode === 'async' || compilationMode === 'sync') {
631
+ ast = transmogrify(ast, compilationMode);
632
+ }
510
633
  return {
511
634
  code: astring.generate(ast, {}),
512
635
  };
@@ -1676,7 +1799,7 @@ var CompilerMetrics;
1676
1799
  CompilerMetrics["LWCSpreadDirective"] = "lwc-spread-directive";
1677
1800
  CompilerMetrics["DynamicImportTransform"] = "dynamic-import-transform";
1678
1801
  })(CompilerMetrics || (CompilerMetrics = {}));
1679
- /** version: 8.3.0 */
1802
+ /** version: 8.5.0 */
1680
1803
 
1681
1804
  /*
1682
1805
  * Copyright (c) 2024, Salesforce, Inc.
@@ -1687,27 +1810,21 @@ var CompilerMetrics;
1687
1810
  const bStylesheetTokenDeclaration = (esTemplate `
1688
1811
  const stylesheetScopeToken = '${estreeToolkit.is.literal}';
1689
1812
  `);
1690
- const bAdditionalDeclarations = [
1691
- (esTemplate `
1692
- const hasScopedStylesheets = defaultScopedStylesheets && defaultScopedStylesheets.length > 0;
1693
- `),
1694
- (esTemplate `
1695
- const stylesheetScopeTokenClass = hasScopedStylesheets ? \` class="\${stylesheetScopeToken}"\` : '';
1696
- `),
1697
- (esTemplate `
1698
- const stylesheetScopeTokenHostClass = hasScopedStylesheets ? \` class="\${stylesheetScopeToken}-host"\` : '';
1699
- `),
1700
- (esTemplate `
1701
- const stylesheetScopeTokenClassPrefix = hasScopedStylesheets ? (stylesheetScopeToken + ' ') : '';
1702
- `),
1703
- ];
1813
+ const bHasScopedStylesheetsDeclaration = (esTemplate `
1814
+ const hasScopedStylesheets = defaultScopedStylesheets !== undefined && defaultScopedStylesheets.length > 0;
1815
+ `);
1704
1816
  // Scope tokens are associated with a given template. This is assigned here so that it can be used in `generateMarkup`.
1817
+ // We also need to keep track of whether the template has any scoped styles or not so that we can render (or not) the
1818
+ // scope token.
1705
1819
  const tmplAssignmentBlock = (esTemplate `
1706
- ${estreeToolkit.is.identifier}.stylesheetScopeTokenHostClass = stylesheetScopeTokenHostClass;
1820
+ {
1821
+ ${ /* template */estreeToolkit.is.identifier}.hasScopedStylesheets = hasScopedStylesheets;
1822
+ ${ /* template */0}.stylesheetScopeToken = stylesheetScopeToken;
1823
+ }
1707
1824
  `);
1708
1825
  function addScopeTokenDeclarations(program, filename, namespace, componentName) {
1709
1826
  const { scopeToken } = templateCompiler.generateScopeTokens(filename, namespace, componentName);
1710
- program.body.unshift(bStylesheetTokenDeclaration(builders.builders.literal(scopeToken)), ...bAdditionalDeclarations.map((declaration) => declaration()));
1827
+ program.body.unshift(bStylesheetTokenDeclaration(builders.builders.literal(scopeToken)), bHasScopedStylesheetsDeclaration());
1711
1828
  program.body.push(tmplAssignmentBlock(builders.builders.identifier('tmpl')));
1712
1829
  }
1713
1830
 
@@ -1717,9 +1834,7 @@ function addScopeTokenDeclarations(program, filename, namespace, componentName)
1717
1834
  * SPDX-License-Identifier: MIT
1718
1835
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
1719
1836
  */
1720
- const bImportHtmlEscape = (esTemplate `
1721
- import { htmlEscape } from '@lwc/shared';
1722
- `);
1837
+ const bImportHtmlEscape = () => bImportDeclaration(['htmlEscape']);
1723
1838
  const importHtmlEscapeKey = 'import:htmlEscape';
1724
1839
  // This is a mostly-correct regular expression will only match if the entire string
1725
1840
  // provided is a valid ECMAScript identifier. Its imperfections lie in the fact that
@@ -1756,6 +1871,44 @@ function optimizeAdjacentYieldStmts(statements) {
1756
1871
  })
1757
1872
  .filter((el) => el !== null);
1758
1873
  }
1874
+ function bAttributeValue(node, attrName) {
1875
+ if (!('attributes' in node)) {
1876
+ throw new TypeError(`Cannot get attribute value from ${node.type}`);
1877
+ }
1878
+ const nameAttrValue = node.attributes.find((attr) => attr.name === attrName)?.value;
1879
+ if (!nameAttrValue) {
1880
+ return estreeToolkit.builders.literal(null);
1881
+ }
1882
+ else if (nameAttrValue.type === 'Literal') {
1883
+ const name = typeof nameAttrValue.value === 'string' ? nameAttrValue.value : '';
1884
+ return estreeToolkit.builders.literal(name);
1885
+ }
1886
+ else {
1887
+ return estreeToolkit.builders.memberExpression(estreeToolkit.builders.literal('instance'), nameAttrValue);
1888
+ }
1889
+ }
1890
+ function getRootMemberExpression$2(node) {
1891
+ return node.object.type === 'MemberExpression' ? getRootMemberExpression$2(node.object) : node;
1892
+ }
1893
+ function getRootIdentifier$1(node) {
1894
+ const rootMemberExpression = getRootMemberExpression$2(node);
1895
+ return estreeToolkit.is.identifier(rootMemberExpression?.object) ? rootMemberExpression.object : null;
1896
+ }
1897
+ /**
1898
+ * Given an expression in a context, return an expression that may be scoped to that context.
1899
+ * For example, for the expression `foo`, it will typically be `instance.foo`, but if we're
1900
+ * inside a `for:each` block then the `foo` variable may refer to the scoped `foo`,
1901
+ * e.g. `<template for:each={foos} for:item="foo">`
1902
+ * @param expression
1903
+ */
1904
+ function getScopedExpression(expression, cxt) {
1905
+ const scopeReferencedId = estreeToolkit.is.memberExpression(expression)
1906
+ ? getRootIdentifier$1(expression)
1907
+ : null;
1908
+ return cxt.isLocalVar(scopeReferencedId?.name)
1909
+ ? expression
1910
+ : estreeToolkit.builders.memberExpression(estreeToolkit.builders.identifier('instance'), expression);
1911
+ }
1759
1912
 
1760
1913
  /*
1761
1914
  * Copyright (c) 2024, salesforce.com, inc.
@@ -1778,8 +1931,8 @@ const Comment = function Comment(node, cxt) {
1778
1931
  * SPDX-License-Identifier: MIT
1779
1932
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
1780
1933
  */
1781
- function getRootMemberExpression$2(node) {
1782
- return node.object.type === 'MemberExpression' ? getRootMemberExpression$2(node.object) : node;
1934
+ function getRootMemberExpression$1(node) {
1935
+ return node.object.type === 'MemberExpression' ? getRootMemberExpression$1(node.object) : node;
1783
1936
  }
1784
1937
  function expressionIrToEs(node, cxt) {
1785
1938
  if (node.type === 'Identifier') {
@@ -1790,7 +1943,7 @@ function expressionIrToEs(node, cxt) {
1790
1943
  }
1791
1944
  else if (node.type === 'MemberExpression') {
1792
1945
  const nodeClone = structuredClone(node);
1793
- const rootMemberExpr = getRootMemberExpression$2(nodeClone);
1946
+ const rootMemberExpr = getRootMemberExpression$1(nodeClone);
1794
1947
  if (!cxt.isLocalVar(rootMemberExpr.object.name)) {
1795
1948
  rootMemberExpr.object = estreeToolkit.builders.memberExpression(estreeToolkit.builders.identifier('instance'), rootMemberExpr.object);
1796
1949
  }
@@ -1809,12 +1962,29 @@ const bYieldFromChildGenerator = (esTemplateWithYield `
1809
1962
  {
1810
1963
  const childProps = ${estreeToolkit.is.objectExpression};
1811
1964
  const childAttrs = ${estreeToolkit.is.objectExpression};
1812
- async function* childSlottedContentGenerator() {
1813
- ${estreeToolkit.is.statement}
1965
+ const slottedContent = {
1966
+ light: Object.create(null),
1967
+ shadow: async function* () {
1968
+ ${ /* shadow slot content */estreeToolkit.is.statement}
1969
+ }
1814
1970
  };
1815
- yield* ${estreeToolkit.is.identifier}(${estreeToolkit.is.literal}, childProps, childAttrs, childSlottedContentGenerator);
1971
+ function addContent(name, fn) {
1972
+ let contentList = slottedContent.light[name]
1973
+ if (contentList) {
1974
+ contentList.push(fn)
1975
+ } else {
1976
+ slottedContent.light[name] = [fn]
1977
+ }
1978
+ }
1979
+ ${ /* addContent statements */estreeToolkit.is.callExpression}
1980
+ yield* ${estreeToolkit.is.identifier}(${estreeToolkit.is.literal}, childProps, childAttrs, slottedContent);
1816
1981
  }
1817
1982
  `);
1983
+ const bAddContent = (esTemplate `
1984
+ addContent(${ /* slot name */estreeToolkit.is.expression} ?? "", async function* () {
1985
+ ${ /* slot content */estreeToolkit.is.statement}
1986
+ });
1987
+ `);
1818
1988
  const bImportGenerateMarkup = (localName, importPath) => estreeToolkit.builders.importDeclaration([estreeToolkit.builders.importSpecifier(estreeToolkit.builders.identifier('generateMarkup'), estreeToolkit.builders.identifier(localName))], estreeToolkit.builders.literal(importPath));
1819
1989
  function getChildAttrsOrProps(attrs, cxt) {
1820
1990
  const objectAttrsOrProps = attrs.map((attr) => {
@@ -1858,8 +2028,24 @@ const Component = function Component(node, cxt) {
1858
2028
  cxt.hoist(componentImport, childGeneratorLocalName);
1859
2029
  const childTagName = node.name;
1860
2030
  const attributes = [...node.attributes, ...reflectAriaPropsAsAttrs(node.properties)];
2031
+ const shadowSlotContent = optimizeAdjacentYieldStmts(irChildrenToEs(node.children, cxt));
2032
+ const lightSlotContent = node.children.map((child) => {
2033
+ if ('attributes' in child) {
2034
+ const slotName = bAttributeValue(child, 'slot');
2035
+ // FIXME: We don't know what happens for slot attributes inside an lwc:if block
2036
+ // Light DOM slots do not actually render the `slot` attribute.
2037
+ const clone = immer.produce(child, (draft) => {
2038
+ draft.attributes = draft.attributes.filter((attr) => attr.name !== 'slot');
2039
+ });
2040
+ const slotContent = irToEs(clone, cxt);
2041
+ return bAddContent(slotName, slotContent);
2042
+ }
2043
+ else {
2044
+ return bAddContent(estreeToolkit.builders.literal(''), irToEs(child, cxt));
2045
+ }
2046
+ });
1861
2047
  return [
1862
- bYieldFromChildGenerator(getChildAttrsOrProps(node.properties, cxt), getChildAttrsOrProps(attributes, cxt), optimizeAdjacentYieldStmts(irChildrenToEs(node.children, cxt)), estreeToolkit.builders.identifier(childGeneratorLocalName), estreeToolkit.builders.literal(childTagName)),
2048
+ bYieldFromChildGenerator(getChildAttrsOrProps(node.properties, cxt), getChildAttrsOrProps(attributes, cxt), shadowSlotContent, lightSlotContent, estreeToolkit.builders.identifier(childGeneratorLocalName), estreeToolkit.builders.literal(childTagName)),
1863
2049
  ];
1864
2050
  };
1865
2051
 
@@ -1870,23 +2056,39 @@ const Component = function Component(node, cxt) {
1870
2056
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
1871
2057
  */
1872
2058
  const bYield$1 = (expr) => estreeToolkit.builders.expressionStatement(estreeToolkit.builders.yieldExpression(expr));
2059
+ // TODO [#4714]: scope token renders as a suffix for literals, but prefix for expressions
1873
2060
  const bConditionalLiveYield = (esTemplateWithYield `
1874
2061
  {
1875
- 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};
2062
+ const shouldRenderScopeToken = ${ /* isClass */estreeToolkit.is.literal} &&
2063
+ (hasScopedStylesheets || hasScopedStaticStylesheets(Cmp));
2064
+ const prefix = shouldRenderScopeToken ? stylesheetScopeToken + ' ' : '';
2065
+
2066
+ const attrValue = ${ /* attribute value expression */estreeToolkit.is.expression};
2067
+ const valueType = typeof attrValue;
2068
+
2069
+ if (attrValue && (valueType === 'string' || valueType === 'boolean')) {
2070
+ yield ' ' + ${ /* attribute name */estreeToolkit.is.literal};
1880
2071
  if (valueType === 'string') {
1881
- yield \`="\${prefix}\${htmlEscape(attrOrPropValue, true)}"\`;
2072
+ yield \`="\${prefix}\${htmlEscape(attrValue, true)}"\`;
1882
2073
  }
1883
2074
  }
1884
2075
  }
1885
2076
  `);
2077
+ // TODO [#4714]: scope token renders as a suffix for literals, but prefix for expressions
1886
2078
  const bStringLiteralYield = (esTemplateWithYield `
1887
2079
  {
1888
- const prefix = (${ /* isClass */estreeToolkit.is.literal} && stylesheetScopeTokenClassPrefix) || '';
1889
- yield ' ' + ${estreeToolkit.is.literal} + '="' + prefix + "${estreeToolkit.is.literal}" + '"'
2080
+ const shouldRenderScopeToken = ${ /* isClass */estreeToolkit.is.literal} &&
2081
+ (hasScopedStylesheets || hasScopedStaticStylesheets(Cmp));
2082
+ const suffix = shouldRenderScopeToken ? ' ' + stylesheetScopeToken : '';
2083
+ yield ' ' + ${ /* attribute name */estreeToolkit.is.literal} + '="' + "${ /* attribute value */estreeToolkit.is.literal}" + suffix + '"'
2084
+ }
2085
+ `);
2086
+ const bConditionallyYieldScopeTokenClass = (esTemplateWithYield `
2087
+ {
2088
+ const shouldRenderScopeToken = hasScopedStylesheets || hasScopedStaticStylesheets(Cmp);
2089
+ if (shouldRenderScopeToken) {
2090
+ yield \` class="\${stylesheetScopeToken}"\`;
2091
+ }
1890
2092
  }
1891
2093
  `);
1892
2094
  function yieldAttrOrPropLiteralValue(name, valueNode, isClass) {
@@ -1910,9 +2112,9 @@ function yieldAttrOrPropLiteralValue(name, valueNode, isClass) {
1910
2112
  }
1911
2113
  throw new Error(`Unknown attr/prop literal: ${type}`);
1912
2114
  }
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))];
2115
+ function yieldAttrOrPropLiveValue(name, value, isClass, cxt) {
2116
+ const scopedExpression = getScopedExpression(value, cxt);
2117
+ return [bConditionalLiveYield(estreeToolkit.builders.literal(isClass), scopedExpression, estreeToolkit.builders.literal(name))];
1916
2118
  }
1917
2119
  function reorderAttributes(attrs, props) {
1918
2120
  let classAttr = null;
@@ -1951,7 +2153,7 @@ const Element = function Element(node, cxt) {
1951
2153
  return yieldAttrOrPropLiteralValue(name, value, isClass);
1952
2154
  }
1953
2155
  else {
1954
- return yieldAttrOrPropLiveValue(name, value, isClass);
2156
+ return yieldAttrOrPropLiveValue(name, value, isClass, cxt);
1955
2157
  }
1956
2158
  });
1957
2159
  if (shared.isVoidElement(node.name, shared.HTML_NAMESPACE)) {
@@ -1974,7 +2176,7 @@ const Element = function Element(node, cxt) {
1974
2176
  return [
1975
2177
  bYield$1(estreeToolkit.builders.literal(`<${node.name}`)),
1976
2178
  // If we haven't already prefixed the scope token to an existing class, add an explicit class here
1977
- ...(hasClassAttribute ? [] : [bYield$1(estreeToolkit.builders.identifier('stylesheetScopeTokenClass'))]),
2179
+ ...(hasClassAttribute ? [] : [bConditionallyYieldScopeTokenClass()]),
1978
2180
  ...yieldAttrsAndProps,
1979
2181
  bYield$1(estreeToolkit.builders.literal(`>`)),
1980
2182
  ...childContent,
@@ -1988,13 +2190,6 @@ const Element = function Element(node, cxt) {
1988
2190
  * SPDX-License-Identifier: MIT
1989
2191
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
1990
2192
  */
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
2193
  const bForOfYieldFrom$1 = (esTemplate `
1999
2194
  for (let [${estreeToolkit.is.identifier}, ${estreeToolkit.is.identifier}] of Object.entries(${estreeToolkit.is.expression} ?? {})) {
2000
2195
  ${estreeToolkit.is.statement};
@@ -2009,12 +2204,7 @@ const ForEach = function ForEach(node, cxt) {
2009
2204
  });
2010
2205
  cxt.popLocalVars();
2011
2206
  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);
2207
+ const iterable = getScopedExpression(expression, cxt);
2018
2208
  return [
2019
2209
  bForOfYieldFrom$1(estreeToolkit.builders.identifier(forIndexId), estreeToolkit.builders.identifier(forItemId), iterable, optimizeAdjacentYieldStmts(forEachStatements)),
2020
2210
  ];
@@ -2038,9 +2228,6 @@ const bForOfYieldFrom = (esTemplate `
2038
2228
  ${estreeToolkit.is.statement};
2039
2229
  }
2040
2230
  `);
2041
- const bToIteratorDirectiveImport = (esTemplate `
2042
- import { toIteratorDirective } from '@lwc/ssr-runtime';
2043
- `);
2044
2231
  const ForOf = function ForEach(node, cxt) {
2045
2232
  const id = node.iterator.name;
2046
2233
  cxt.pushLocalVars([id]);
@@ -2055,7 +2242,7 @@ const ForOf = function ForEach(node, cxt) {
2055
2242
  const iterable = cxt.isLocalVar(scopeReferencedId?.name)
2056
2243
  ? node.expression
2057
2244
  : estreeToolkit.builders.memberExpression(estreeToolkit.builders.identifier('instance'), node.expression);
2058
- cxt.hoist(bToIteratorDirectiveImport(), 'toIteratorDirective');
2245
+ cxt.hoist(bImportDeclaration(['toIteratorDirective']), 'import:toIteratorDirective');
2059
2246
  return [
2060
2247
  bForOfYieldFrom(estreeToolkit.builders.identifier(id), iterable, optimizeAdjacentYieldStmts(forEachStatements)),
2061
2248
  ];
@@ -2110,9 +2297,11 @@ const bConditionalSlot = (esTemplateWithYield `
2110
2297
  // start bookend HTML comment
2111
2298
  yield '<!---->';
2112
2299
 
2113
- const generator = slottedContent[${ /* slotName */estreeToolkit.is.expression} ?? ""];
2114
- if (generator) {
2115
- yield* generator();
2300
+ const generators = slottedContent?.light[${ /* slotName */estreeToolkit.is.expression} ?? ""];
2301
+ if (generators) {
2302
+ for (const generator of generators) {
2303
+ yield* generator();
2304
+ }
2116
2305
  } else {
2117
2306
  // If we're in this else block, then the generator _must_ have yielded
2118
2307
  // something. It's impossible for a slottedContent["foo"] to exist
@@ -2129,18 +2318,7 @@ const bConditionalSlot = (esTemplateWithYield `
2129
2318
  }
2130
2319
  `);
2131
2320
  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
- }
2321
+ const slotName = bAttributeValue(node, 'name');
2144
2322
  // FIXME: avoid serializing the slot's children twice
2145
2323
  const slotAst = Element(node, ctx);
2146
2324
  const slotChildren = irChildrenToEs(node.children, ctx);
@@ -2296,9 +2474,6 @@ function templateIrToEsTree(node, contextOpts) {
2296
2474
  * SPDX-License-Identifier: MIT
2297
2475
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
2298
2476
  */
2299
- const bStyleValidationImport = (esTemplate `
2300
- import { validateStyleTextContents } from '@lwc/ssr-runtime';
2301
- `);
2302
2477
  // TODO [#4663]: Render mode mismatch between template and compiler should throw.
2303
2478
  const bExportTemplate = (esTemplate `
2304
2479
  export default async function* tmpl(props, attrs, slottedContent, Cmp, instance) {
@@ -2307,34 +2482,28 @@ const bExportTemplate = (esTemplate `
2307
2482
  yield \`<template shadowrootmode="open"\${Cmp.delegatesFocus ? ' shadowrootdelegatesfocus' : ''}>\`
2308
2483
  }
2309
2484
 
2310
- if (defaultStylesheets || defaultScopedStylesheets) {
2311
- // Flatten all stylesheets infinitely and concatenate
2312
- const stylesheets = [defaultStylesheets, defaultScopedStylesheets].filter(Boolean).flat(Infinity);
2313
-
2314
- for (const stylesheet of stylesheets) {
2315
- const token = stylesheet.$scoped$ ? stylesheetScopeToken : undefined;
2316
- const useActualHostSelector = !stylesheet.$scoped$ || Cmp.renderMode !== 'light';
2317
- const useNativeDirPseudoclass = true;
2318
- yield '<style' + stylesheetScopeTokenClass + ' type="text/css">';
2319
- const styleContents = stylesheet(token, useActualHostSelector, useNativeDirPseudoclass);
2320
- validateStyleTextContents(styleContents);
2321
- yield styleContents;
2322
- yield '</style>';
2323
- }
2485
+ const { stylesheets: staticStylesheets } = Cmp;
2486
+ if (defaultStylesheets || defaultScopedStylesheets || staticStylesheets) {
2487
+ const stylesheets = [defaultStylesheets, defaultScopedStylesheets, staticStylesheets];
2488
+ yield renderStylesheets(
2489
+ stylesheets,
2490
+ stylesheetScopeToken,
2491
+ Cmp,
2492
+ hasScopedStylesheets,
2493
+ );
2324
2494
  }
2325
2495
 
2326
2496
  ${estreeToolkit.is.statement};
2327
2497
 
2328
2498
  if (!isLightDom) {
2329
2499
  yield '</template>';
2330
- }
2331
-
2332
- if (slottedContent) {
2333
- yield* slottedContent();
2500
+ if (slottedContent?.shadow) {
2501
+ yield* slottedContent.shadow();
2502
+ }
2334
2503
  }
2335
2504
  }
2336
2505
  `);
2337
- function compileTemplate(src, filename, options) {
2506
+ function compileTemplate(src, filename, options, compilationMode) {
2338
2507
  const { root, warnings } = templateCompiler.parse(src, {
2339
2508
  // `options` is from @lwc/compiler, and may have flags that @lwc/template-compiler doesn't
2340
2509
  // know about, so we must explicitly extract the relevant props.
@@ -2371,13 +2540,16 @@ function compileTemplate(src, filename, options) {
2371
2540
  const { hoisted, statements } = templateIrToEsTree(root, { preserveComments });
2372
2541
  const moduleBody = [
2373
2542
  ...hoisted,
2374
- bStyleValidationImport(),
2543
+ bImportDeclaration(['renderStylesheets', 'hasScopedStaticStylesheets']),
2375
2544
  bExportTemplate(optimizeAdjacentYieldStmts(statements)),
2376
2545
  ];
2377
- const program = estreeToolkit.builders.program(moduleBody, 'module');
2546
+ let program = estreeToolkit.builders.program(moduleBody, 'module');
2378
2547
  addScopeTokenDeclarations(program, filename, options.namespace, options.name);
2379
2548
  const stylesheetImports = getStylesheetImports(filename);
2380
2549
  program.body.unshift(...stylesheetImports);
2550
+ if (compilationMode === 'async' || compilationMode === 'sync') {
2551
+ program = transmogrify(program, compilationMode);
2552
+ }
2381
2553
  return {
2382
2554
  code: astring.generate(program, {}),
2383
2555
  };
@@ -2389,16 +2561,16 @@ function compileTemplate(src, filename, options) {
2389
2561
  * SPDX-License-Identifier: MIT
2390
2562
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
2391
2563
  */
2392
- function compileComponentForSSR(src, filename, _options) {
2393
- const { code } = compileJS(src, filename);
2564
+ function compileComponentForSSR(src, filename, _options, mode = 'asyncYield') {
2565
+ const { code } = compileJS(src, filename, mode);
2394
2566
  return { code, map: undefined };
2395
2567
  }
2396
- function compileTemplateForSSR(src, filename, options) {
2397
- const { code } = compileTemplate(src, filename, options);
2568
+ function compileTemplateForSSR(src, filename, options, mode = 'asyncYield') {
2569
+ const { code } = compileTemplate(src, filename, options, mode);
2398
2570
  return { code, map: undefined };
2399
2571
  }
2400
2572
 
2401
2573
  exports.compileComponentForSSR = compileComponentForSSR;
2402
2574
  exports.compileTemplateForSSR = compileTemplateForSSR;
2403
- /** version: 8.3.0 */
2575
+ /** version: 8.5.0 */
2404
2576
  //# sourceMappingURL=index.cjs.js.map