@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.
- package/dist/compile-js/index.d.ts +2 -1
- package/dist/compile-js/types.d.ts +0 -1
- package/dist/compile-template/index.d.ts +2 -1
- package/dist/compile-template/shared.d.ts +12 -1
- package/dist/index.cjs.js +268 -121
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +271 -124
- package/dist/index.js.map +1 -1
- package/dist/shared.d.ts +1 -0
- package/dist/transmogrify.d.ts +50 -0
- package/package.json +3 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Config as TemplateCompilerConfig } from '@lwc/template-compiler';
|
|
2
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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, ${
|
|
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
|
-
|
|
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.
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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/
|
|
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$
|
|
1782
|
-
return node.object.type === 'MemberExpression' ? getRootMemberExpression$
|
|
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$
|
|
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
|
-
|
|
1813
|
-
|
|
1945
|
+
const slottedContent = {
|
|
1946
|
+
light: Object.create(null),
|
|
1947
|
+
shadow: async function* () {
|
|
1948
|
+
${ /* shadow slot content */estreeToolkit.is.statement}
|
|
1949
|
+
}
|
|
1814
1950
|
};
|
|
1815
|
-
|
|
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),
|
|
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
|
|
1877
|
-
const valueType = typeof
|
|
1878
|
-
if (
|
|
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(
|
|
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
|
|
1915
|
-
return [bConditionalLiveYield(estreeToolkit.builders.literal(isClass),
|
|
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
|
|
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
|
|
2114
|
-
if (
|
|
2115
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
2550
|
+
/** version: 8.4.0 */
|
|
2404
2551
|
//# sourceMappingURL=index.cjs.js.map
|