@lwc/ssr-compiler 8.24.0 → 8.25.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 +290 -218
- package/dist/index.js +291 -219
- package/package.json +5 -5
package/dist/index.cjs.js
CHANGED
|
@@ -11,39 +11,203 @@ var estreeToolkit = require('estree-toolkit');
|
|
|
11
11
|
var meriyah = require('meriyah');
|
|
12
12
|
var errors = require('@lwc/errors');
|
|
13
13
|
var immer = require('immer');
|
|
14
|
-
var node_path = require('node:path');
|
|
15
14
|
var acorn = require('acorn');
|
|
15
|
+
var node_path = require('node:path');
|
|
16
16
|
var templateCompiler = require('@lwc/template-compiler');
|
|
17
17
|
var builders = require('estree-toolkit/dist/builders');
|
|
18
18
|
var types = require('@babel/types');
|
|
19
19
|
var util = require('util');
|
|
20
20
|
|
|
21
|
+
/*
|
|
22
|
+
* Copyright (c) 2024, salesforce.com, inc.
|
|
23
|
+
* All rights reserved.
|
|
24
|
+
* SPDX-License-Identifier: MIT
|
|
25
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
26
|
+
*/
|
|
27
|
+
/** Placeholder value to use to opt out of validation. */
|
|
28
|
+
const NO_VALIDATION = false;
|
|
29
|
+
/**
|
|
30
|
+
* `esTemplate` generates JS code with "holes" to be filled later. In order to have a valid AST,
|
|
31
|
+
* it uses identifiers with this prefix at the location of the holes.
|
|
32
|
+
*/
|
|
33
|
+
const PLACEHOLDER_PREFIX = '__lwc_ESTEMPLATE_PLACEHOLDER__';
|
|
34
|
+
const getReplacementNode = (state, placeholderId) => {
|
|
35
|
+
const key = Number(placeholderId.slice(PLACEHOLDER_PREFIX.length));
|
|
36
|
+
const nodeCount = state.replacementNodes.length;
|
|
37
|
+
if (key >= nodeCount) {
|
|
38
|
+
throw new Error(`Cannot use index ${key} when only ${nodeCount} values have been provided.`);
|
|
39
|
+
}
|
|
40
|
+
const validateReplacement = state.placeholderToValidator.get(key);
|
|
41
|
+
const replacementNode = state.replacementNodes[key];
|
|
42
|
+
if (validateReplacement &&
|
|
43
|
+
!(Array.isArray(replacementNode)
|
|
44
|
+
? replacementNode.every(validateReplacement)
|
|
45
|
+
: validateReplacement(replacementNode))) {
|
|
46
|
+
const expectedType = validateReplacement.__debugName ||
|
|
47
|
+
validateReplacement.name ||
|
|
48
|
+
'(could not determine)';
|
|
49
|
+
const actualType = Array.isArray(replacementNode)
|
|
50
|
+
? `[${replacementNode.map((n) => n && n.type).join(', ')}]`
|
|
51
|
+
: replacementNode?.type;
|
|
52
|
+
throw new Error(`Validation failed for templated node. Expected type ${expectedType}, but received ${actualType}.`);
|
|
53
|
+
}
|
|
54
|
+
return replacementNode;
|
|
55
|
+
};
|
|
56
|
+
const visitors$2 = {
|
|
57
|
+
Identifier(path, state) {
|
|
58
|
+
if (path.node?.name.startsWith(PLACEHOLDER_PREFIX)) {
|
|
59
|
+
const replacementNode = getReplacementNode(state, path.node.name);
|
|
60
|
+
if (replacementNode === null) {
|
|
61
|
+
path.remove();
|
|
62
|
+
}
|
|
63
|
+
else if (Array.isArray(replacementNode)) {
|
|
64
|
+
if (replacementNode.length === 0) {
|
|
65
|
+
path.remove();
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
if (path.parentPath?.node?.type === 'ExpressionStatement') {
|
|
69
|
+
path.parentPath.replaceWithMultiple(replacementNode);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
path.replaceWithMultiple(replacementNode);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
path.replaceWith(replacementNode);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
Literal(path, state) {
|
|
82
|
+
if (typeof path.node?.value === 'string' &&
|
|
83
|
+
path.node.value.startsWith(PLACEHOLDER_PREFIX)) {
|
|
84
|
+
// A literal can only be replaced with a single node
|
|
85
|
+
const replacementNode = getReplacementNode(state, path.node.value);
|
|
86
|
+
path.replaceWith(replacementNode);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
function esTemplateImpl(javascriptSegments, validators, wrap, unwrap) {
|
|
91
|
+
let placeholderCount = 0;
|
|
92
|
+
let parsableCode = javascriptSegments[0];
|
|
93
|
+
const placeholderToValidator = new Map();
|
|
94
|
+
for (let i = 1; i < javascriptSegments.length; i += 1) {
|
|
95
|
+
const segment = javascriptSegments[i];
|
|
96
|
+
const validator = validators[i - 1]; // always one less value than strings in template literals
|
|
97
|
+
if (typeof validator === 'function' || validator === NO_VALIDATION) {
|
|
98
|
+
// Template slot will be filled by a *new* argument passed to the generated function
|
|
99
|
+
if (validator !== NO_VALIDATION) {
|
|
100
|
+
placeholderToValidator.set(placeholderCount, validator);
|
|
101
|
+
}
|
|
102
|
+
parsableCode += `${PLACEHOLDER_PREFIX}${placeholderCount}`;
|
|
103
|
+
placeholderCount += 1;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
// Template slot uses a *previously defined* argument passed to the generated function
|
|
107
|
+
if (validator >= placeholderCount) {
|
|
108
|
+
throw new Error(`Reference to argument ${validator} at index ${i} cannot be used. Only ${placeholderCount - 1} arguments have been defined.`);
|
|
109
|
+
}
|
|
110
|
+
parsableCode += `${PLACEHOLDER_PREFIX}${validator}`;
|
|
111
|
+
}
|
|
112
|
+
parsableCode += segment;
|
|
113
|
+
}
|
|
114
|
+
if (wrap) {
|
|
115
|
+
parsableCode = wrap(parsableCode);
|
|
116
|
+
}
|
|
117
|
+
const originalAstProgram = acorn.parse(parsableCode, {
|
|
118
|
+
ecmaVersion: 2022,
|
|
119
|
+
allowAwaitOutsideFunction: true,
|
|
120
|
+
allowReturnOutsideFunction: true,
|
|
121
|
+
allowSuperOutsideMethod: true,
|
|
122
|
+
allowImportExportEverywhere: true,
|
|
123
|
+
locations: false,
|
|
124
|
+
});
|
|
125
|
+
let originalAst;
|
|
126
|
+
const finalCharacter = javascriptSegments.at(-1)?.trimEnd()?.at(-1);
|
|
127
|
+
if (originalAstProgram.body.length === 1) {
|
|
128
|
+
originalAst =
|
|
129
|
+
finalCharacter === ';' && originalAstProgram.body[0].type === 'ExpressionStatement'
|
|
130
|
+
? (originalAst = originalAstProgram.body[0].expression)
|
|
131
|
+
: (originalAst = originalAstProgram.body[0]);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
originalAst = originalAstProgram.body;
|
|
135
|
+
}
|
|
136
|
+
// Turns Acorn AST objects into POJOs, for use with Immer.
|
|
137
|
+
originalAst = JSON.parse(JSON.stringify(originalAst));
|
|
138
|
+
return function templatedAst(...replacementNodes) {
|
|
139
|
+
const result = immer.produce(originalAst, (astDraft) => estreeToolkit.traverse(astDraft, visitors$2, {
|
|
140
|
+
placeholderToValidator,
|
|
141
|
+
replacementNodes,
|
|
142
|
+
}));
|
|
143
|
+
return (unwrap ? unwrap(result) : result);
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Template literal tag that generates a builder function. Like estree's `builders`, but for more
|
|
148
|
+
* complex structures. The template values should be estree `is` validators or a back reference to
|
|
149
|
+
* a previous slot (to re-use the referenced value).
|
|
150
|
+
*
|
|
151
|
+
* To have the generated function return a particular node type, the generic comes _after_ the
|
|
152
|
+
* template literal. Kinda weird, but it's necessary to infer the types of the template values.
|
|
153
|
+
* (If it were at the start, we'd need to explicitly provide _all_ type params. Tedious!)
|
|
154
|
+
* @example
|
|
155
|
+
* const bSum = esTemplate`(${is.identifier}, ${is.identifier}) => ${0} + ${1}`<EsArrowFunctionExpression>
|
|
156
|
+
* const sumFuncNode = bSum(b.identifier('a'), b.identifier('b'))
|
|
157
|
+
* // `sumFuncNode` is an AST node representing `(a, b) => a + b`
|
|
158
|
+
*/
|
|
159
|
+
function esTemplate(javascriptSegments, ...Validators) {
|
|
160
|
+
return esTemplateImpl(javascriptSegments, Validators);
|
|
161
|
+
}
|
|
162
|
+
/** Similar to {@linkcode esTemplate}, but supports `yield` expressions. */
|
|
163
|
+
function esTemplateWithYield(javascriptSegments, ...validators) {
|
|
164
|
+
const wrap = (code) => `function* placeholder() {${code}}`;
|
|
165
|
+
const unwrap = (node) => node.body.body.length === 1 ? node.body.body[0] : node.body.body;
|
|
166
|
+
return esTemplateImpl(javascriptSegments, validators, wrap, unwrap);
|
|
167
|
+
}
|
|
168
|
+
|
|
21
169
|
/*
|
|
22
170
|
* Copyright (c) 2024, Salesforce, Inc.
|
|
23
171
|
* All rights reserved.
|
|
24
172
|
* SPDX-License-Identifier: MIT
|
|
25
173
|
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
26
174
|
*/
|
|
27
|
-
const EMIT_IDENT = estreeToolkit.builders.identifier('$$emit');
|
|
28
175
|
/** Function names that may be transmogrified. All should start with `__lwc`. */
|
|
29
176
|
// Rollup may rename variables to prevent shadowing. When it does, it uses the format `foo$0`, `foo$1`, etc.
|
|
30
177
|
const TRANSMOGRIFY_TARGET = /^__lwc(Generate|Tmpl).*$/;
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
178
|
+
const isNonArrowFunction = (node) => {
|
|
179
|
+
return estreeToolkit.is.functionDeclaration(node) || estreeToolkit.is.functionExpression(node);
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Determines whether a node is a function we want to transmogrify or within one, at any level.
|
|
183
|
+
*/
|
|
184
|
+
const isWithinTargetFunc = (nodePath) => {
|
|
185
|
+
let path = isNonArrowFunction(nodePath)
|
|
186
|
+
? nodePath
|
|
187
|
+
: nodePath.findParent(isNonArrowFunction);
|
|
188
|
+
while (path?.node) {
|
|
189
|
+
const { id } = path.node;
|
|
190
|
+
if (id && TRANSMOGRIFY_TARGET.test(id.name)) {
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
path = path.findParent(isNonArrowFunction);
|
|
43
194
|
}
|
|
44
195
|
return false;
|
|
45
196
|
};
|
|
46
|
-
|
|
197
|
+
/**
|
|
198
|
+
* Determines whether the nearest function encapsulating this node is a function we transmogrify.
|
|
199
|
+
*/
|
|
200
|
+
const isImmediateWithinTargetFunc = (nodePath) => {
|
|
201
|
+
const parentFunc = nodePath.findParent(estreeToolkit.is.function);
|
|
202
|
+
return Boolean(parentFunc &&
|
|
203
|
+
isNonArrowFunction(parentFunc) &&
|
|
204
|
+
parentFunc.node?.id &&
|
|
205
|
+
TRANSMOGRIFY_TARGET.test(parentFunc.node.id.name));
|
|
206
|
+
};
|
|
207
|
+
const bDeclareYieldVar = (esTemplate `let __lwcYield = '';`);
|
|
208
|
+
const bAppendToYieldVar = (esTemplate `__lwcYield += ${estreeToolkit.is.expression};`);
|
|
209
|
+
const bReturnYieldVar = (esTemplate `return __lwcYield;`);
|
|
210
|
+
const visitors$1 = {
|
|
47
211
|
// @ts-expect-error types for `traverse` do not support sharing a visitor between node types:
|
|
48
212
|
// https://github.com/sarsamurmu/estree-toolkit/issues/20
|
|
49
213
|
'FunctionDeclaration|FunctionExpression'(path, state) {
|
|
@@ -54,12 +218,12 @@ const visitors$2 = {
|
|
|
54
218
|
// Component authors might conceivably use async generator functions in their own code. Therefore,
|
|
55
219
|
// when traversing & transforming written+generated code, we need to disambiguate generated async
|
|
56
220
|
// generator functions from those that were written by the component author.
|
|
57
|
-
if (!
|
|
221
|
+
if (!isWithinTargetFunc(path)) {
|
|
58
222
|
return;
|
|
59
223
|
}
|
|
60
224
|
node.generator = false;
|
|
61
225
|
node.async = state.mode === 'async';
|
|
62
|
-
node.
|
|
226
|
+
node.body.body = [bDeclareYieldVar(), ...node.body.body, bReturnYieldVar()];
|
|
63
227
|
},
|
|
64
228
|
YieldExpression(path, state) {
|
|
65
229
|
const { node } = path;
|
|
@@ -69,26 +233,15 @@ const visitors$2 = {
|
|
|
69
233
|
// Component authors might conceivably use generator functions within their own code. Therefore,
|
|
70
234
|
// when traversing & transforming written+generated code, we need to disambiguate generated yield
|
|
71
235
|
// expressions from those that were written by the component author.
|
|
72
|
-
if (!
|
|
236
|
+
if (!isWithinTargetFunc(path)) {
|
|
73
237
|
return;
|
|
74
238
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
const callExpr = node.argument;
|
|
81
|
-
callExpr.arguments.unshift(EMIT_IDENT);
|
|
82
|
-
path.replaceWith(state.mode === 'sync' ? callExpr : estreeToolkit.builders.awaitExpression(callExpr));
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
// transform `yield foo` into `$$emit(foo)`
|
|
86
|
-
const emittedExpression = node.argument;
|
|
87
|
-
if (!emittedExpression) {
|
|
88
|
-
throw new Error('Implementation error: cannot transform a yield expression that yields nothing');
|
|
89
|
-
}
|
|
90
|
-
path.replaceWith(estreeToolkit.builders.callExpression(EMIT_IDENT, [emittedExpression]));
|
|
239
|
+
const arg = node.argument;
|
|
240
|
+
if (!arg) {
|
|
241
|
+
const type = node.delegate ? 'yield*' : 'yield';
|
|
242
|
+
throw new Error(`Cannot transmogrify ${type} statement without an argument.`);
|
|
91
243
|
}
|
|
244
|
+
path.replaceWith(bAppendToYieldVar(state.mode === 'sync' ? arg : estreeToolkit.builders.awaitExpression(arg)));
|
|
92
245
|
},
|
|
93
246
|
ImportSpecifier(path, _state) {
|
|
94
247
|
// @lwc/ssr-runtime has a couple of helper functions that need to conform to either the generator or
|
|
@@ -115,6 +268,17 @@ const visitors$2 = {
|
|
|
115
268
|
node.imported.name = 'renderAttrsNoYield';
|
|
116
269
|
}
|
|
117
270
|
},
|
|
271
|
+
ReturnStatement(path) {
|
|
272
|
+
if (!isImmediateWithinTargetFunc(path)) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
// The transmogrify result returns __lwcYield, so we skip it
|
|
276
|
+
const arg = path.node?.argument;
|
|
277
|
+
if (estreeToolkit.is.identifier(arg) && arg.name === '__lwcYield') {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
throw new Error('Cannot transmogrify function with return statement.');
|
|
281
|
+
},
|
|
118
282
|
};
|
|
119
283
|
/**
|
|
120
284
|
* Transforms async-generator code into either the async or synchronous alternatives that are
|
|
@@ -160,7 +324,7 @@ function transmogrify(compiledComponentAst, mode = 'sync') {
|
|
|
160
324
|
const state = {
|
|
161
325
|
mode,
|
|
162
326
|
};
|
|
163
|
-
return immer.produce(compiledComponentAst, (astDraft) => estreeToolkit.traverse(astDraft, visitors$
|
|
327
|
+
return immer.produce(compiledComponentAst, (astDraft) => estreeToolkit.traverse(astDraft, visitors$1, state));
|
|
164
328
|
}
|
|
165
329
|
|
|
166
330
|
/******************************************************************************
|
|
@@ -369,154 +533,6 @@ function catalogStaticStylesheets(ids, state) {
|
|
|
369
533
|
}
|
|
370
534
|
}
|
|
371
535
|
|
|
372
|
-
/*
|
|
373
|
-
* Copyright (c) 2024, salesforce.com, inc.
|
|
374
|
-
* All rights reserved.
|
|
375
|
-
* SPDX-License-Identifier: MIT
|
|
376
|
-
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
377
|
-
*/
|
|
378
|
-
/** Placeholder value to use to opt out of validation. */
|
|
379
|
-
const NO_VALIDATION = false;
|
|
380
|
-
/**
|
|
381
|
-
* `esTemplate` generates JS code with "holes" to be filled later. In order to have a valid AST,
|
|
382
|
-
* it uses identifiers with this prefix at the location of the holes.
|
|
383
|
-
*/
|
|
384
|
-
const PLACEHOLDER_PREFIX = '__lwc_ESTEMPLATE_PLACEHOLDER__';
|
|
385
|
-
const getReplacementNode = (state, placeholderId) => {
|
|
386
|
-
const key = Number(placeholderId.slice(PLACEHOLDER_PREFIX.length));
|
|
387
|
-
const nodeCount = state.replacementNodes.length;
|
|
388
|
-
if (key >= nodeCount) {
|
|
389
|
-
throw new Error(`Cannot use index ${key} when only ${nodeCount} values have been provided.`);
|
|
390
|
-
}
|
|
391
|
-
const validateReplacement = state.placeholderToValidator.get(key);
|
|
392
|
-
const replacementNode = state.replacementNodes[key];
|
|
393
|
-
if (validateReplacement &&
|
|
394
|
-
!(Array.isArray(replacementNode)
|
|
395
|
-
? replacementNode.every(validateReplacement)
|
|
396
|
-
: validateReplacement(replacementNode))) {
|
|
397
|
-
const expectedType = validateReplacement.__debugName ||
|
|
398
|
-
validateReplacement.name ||
|
|
399
|
-
'(could not determine)';
|
|
400
|
-
const actualType = Array.isArray(replacementNode)
|
|
401
|
-
? `[${replacementNode.map((n) => n && n.type).join(', ')}]`
|
|
402
|
-
: replacementNode?.type;
|
|
403
|
-
throw new Error(`Validation failed for templated node. Expected type ${expectedType}, but received ${actualType}.`);
|
|
404
|
-
}
|
|
405
|
-
return replacementNode;
|
|
406
|
-
};
|
|
407
|
-
const visitors$1 = {
|
|
408
|
-
Identifier(path, state) {
|
|
409
|
-
if (path.node?.name.startsWith(PLACEHOLDER_PREFIX)) {
|
|
410
|
-
const replacementNode = getReplacementNode(state, path.node.name);
|
|
411
|
-
if (replacementNode === null) {
|
|
412
|
-
path.remove();
|
|
413
|
-
}
|
|
414
|
-
else if (Array.isArray(replacementNode)) {
|
|
415
|
-
if (replacementNode.length === 0) {
|
|
416
|
-
path.remove();
|
|
417
|
-
}
|
|
418
|
-
else {
|
|
419
|
-
if (path.parentPath?.node?.type === 'ExpressionStatement') {
|
|
420
|
-
path.parentPath.replaceWithMultiple(replacementNode);
|
|
421
|
-
}
|
|
422
|
-
else {
|
|
423
|
-
path.replaceWithMultiple(replacementNode);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
else {
|
|
428
|
-
path.replaceWith(replacementNode);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
},
|
|
432
|
-
Literal(path, state) {
|
|
433
|
-
if (typeof path.node?.value === 'string' &&
|
|
434
|
-
path.node.value.startsWith(PLACEHOLDER_PREFIX)) {
|
|
435
|
-
// A literal can only be replaced with a single node
|
|
436
|
-
const replacementNode = getReplacementNode(state, path.node.value);
|
|
437
|
-
path.replaceWith(replacementNode);
|
|
438
|
-
}
|
|
439
|
-
},
|
|
440
|
-
};
|
|
441
|
-
function esTemplateImpl(javascriptSegments, validators, wrap, unwrap) {
|
|
442
|
-
let placeholderCount = 0;
|
|
443
|
-
let parsableCode = javascriptSegments[0];
|
|
444
|
-
const placeholderToValidator = new Map();
|
|
445
|
-
for (let i = 1; i < javascriptSegments.length; i += 1) {
|
|
446
|
-
const segment = javascriptSegments[i];
|
|
447
|
-
const validator = validators[i - 1]; // always one less value than strings in template literals
|
|
448
|
-
if (typeof validator === 'function' || validator === NO_VALIDATION) {
|
|
449
|
-
// Template slot will be filled by a *new* argument passed to the generated function
|
|
450
|
-
if (validator !== NO_VALIDATION) {
|
|
451
|
-
placeholderToValidator.set(placeholderCount, validator);
|
|
452
|
-
}
|
|
453
|
-
parsableCode += `${PLACEHOLDER_PREFIX}${placeholderCount}`;
|
|
454
|
-
placeholderCount += 1;
|
|
455
|
-
}
|
|
456
|
-
else {
|
|
457
|
-
// Template slot uses a *previously defined* argument passed to the generated function
|
|
458
|
-
if (validator >= placeholderCount) {
|
|
459
|
-
throw new Error(`Reference to argument ${validator} at index ${i} cannot be used. Only ${placeholderCount - 1} arguments have been defined.`);
|
|
460
|
-
}
|
|
461
|
-
parsableCode += `${PLACEHOLDER_PREFIX}${validator}`;
|
|
462
|
-
}
|
|
463
|
-
parsableCode += segment;
|
|
464
|
-
}
|
|
465
|
-
if (wrap) {
|
|
466
|
-
parsableCode = wrap(parsableCode);
|
|
467
|
-
}
|
|
468
|
-
const originalAstProgram = acorn.parse(parsableCode, {
|
|
469
|
-
ecmaVersion: 2022,
|
|
470
|
-
allowAwaitOutsideFunction: true,
|
|
471
|
-
allowReturnOutsideFunction: true,
|
|
472
|
-
allowSuperOutsideMethod: true,
|
|
473
|
-
allowImportExportEverywhere: true,
|
|
474
|
-
locations: false,
|
|
475
|
-
});
|
|
476
|
-
let originalAst;
|
|
477
|
-
const finalCharacter = javascriptSegments.at(-1)?.trimEnd()?.at(-1);
|
|
478
|
-
if (originalAstProgram.body.length === 1) {
|
|
479
|
-
originalAst =
|
|
480
|
-
finalCharacter === ';' && originalAstProgram.body[0].type === 'ExpressionStatement'
|
|
481
|
-
? (originalAst = originalAstProgram.body[0].expression)
|
|
482
|
-
: (originalAst = originalAstProgram.body[0]);
|
|
483
|
-
}
|
|
484
|
-
else {
|
|
485
|
-
originalAst = originalAstProgram.body;
|
|
486
|
-
}
|
|
487
|
-
// Turns Acorn AST objects into POJOs, for use with Immer.
|
|
488
|
-
originalAst = JSON.parse(JSON.stringify(originalAst));
|
|
489
|
-
return function templatedAst(...replacementNodes) {
|
|
490
|
-
const result = immer.produce(originalAst, (astDraft) => estreeToolkit.traverse(astDraft, visitors$1, {
|
|
491
|
-
placeholderToValidator,
|
|
492
|
-
replacementNodes,
|
|
493
|
-
}));
|
|
494
|
-
return (unwrap ? unwrap(result) : result);
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
/**
|
|
498
|
-
* Template literal tag that generates a builder function. Like estree's `builders`, but for more
|
|
499
|
-
* complex structures. The template values should be estree `is` validators or a back reference to
|
|
500
|
-
* a previous slot (to re-use the referenced value).
|
|
501
|
-
*
|
|
502
|
-
* To have the generated function return a particular node type, the generic comes _after_ the
|
|
503
|
-
* template literal. Kinda weird, but it's necessary to infer the types of the template values.
|
|
504
|
-
* (If it were at the start, we'd need to explicitly provide _all_ type params. Tedious!)
|
|
505
|
-
* @example
|
|
506
|
-
* const bSum = esTemplate`(${is.identifier}, ${is.identifier}) => ${0} + ${1}`<EsArrowFunctionExpression>
|
|
507
|
-
* const sumFuncNode = bSum(b.identifier('a'), b.identifier('b'))
|
|
508
|
-
* // `sumFuncNode` is an AST node representing `(a, b) => a + b`
|
|
509
|
-
*/
|
|
510
|
-
function esTemplate(javascriptSegments, ...Validators) {
|
|
511
|
-
return esTemplateImpl(javascriptSegments, Validators);
|
|
512
|
-
}
|
|
513
|
-
/** Similar to {@linkcode esTemplate}, but supports `yield` expressions. */
|
|
514
|
-
function esTemplateWithYield(javascriptSegments, ...validators) {
|
|
515
|
-
const wrap = (code) => `function* placeholder() {${code}}`;
|
|
516
|
-
const unwrap = (node) => node.body.body.length === 1 ? node.body.body[0] : node.body.body;
|
|
517
|
-
return esTemplateImpl(javascriptSegments, validators, wrap, unwrap);
|
|
518
|
-
}
|
|
519
|
-
|
|
520
536
|
/*
|
|
521
537
|
* Copyright (c) 2024, Salesforce, Inc.
|
|
522
538
|
* All rights reserved.
|
|
@@ -720,13 +736,13 @@ const bGenerateMarkup = (esTemplate `
|
|
|
720
736
|
enumerable: false,
|
|
721
737
|
writable: false,
|
|
722
738
|
value: async function* __lwcGenerateMarkup(
|
|
723
|
-
// The $$emit function is magically inserted here
|
|
724
739
|
tagName,
|
|
725
740
|
props,
|
|
726
741
|
attrs,
|
|
727
742
|
parent,
|
|
728
743
|
scopeToken,
|
|
729
744
|
contextfulParent,
|
|
745
|
+
renderContext,
|
|
730
746
|
shadowSlottedContent,
|
|
731
747
|
lightSlottedContent,
|
|
732
748
|
scopedSlottedContent,
|
|
@@ -770,7 +786,8 @@ const bGenerateMarkup = (esTemplate `
|
|
|
770
786
|
lightSlottedContent,
|
|
771
787
|
scopedSlottedContent,
|
|
772
788
|
${ /*component class*/0},
|
|
773
|
-
instance
|
|
789
|
+
instance,
|
|
790
|
+
renderContext
|
|
774
791
|
);
|
|
775
792
|
yield \`</\${tagName}>\`;
|
|
776
793
|
}
|
|
@@ -1304,33 +1321,86 @@ function getRootIdentifier$1(node) {
|
|
|
1304
1321
|
* SPDX-License-Identifier: MIT
|
|
1305
1322
|
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
1306
1323
|
*/
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
estreeToolkit.is.expressionStatement(stmt) &&
|
|
1322
|
-
estreeToolkit.is.yieldExpression(stmt.expression) &&
|
|
1323
|
-
!stmt.expression.delegate &&
|
|
1324
|
-
stmt.expression.argument &&
|
|
1325
|
-
estreeToolkit.is.literal(stmt.expression.argument) &&
|
|
1326
|
-
typeof stmt.expression.argument.value === 'string') {
|
|
1327
|
-
prevStmt.expression.argument.value += stmt.expression.argument.value;
|
|
1324
|
+
const bYieldTernary = (esTemplateWithYield `yield ${estreeToolkit.is.expression} ? ${estreeToolkit.is.expression} : ${estreeToolkit.is.expression}`);
|
|
1325
|
+
const bOptimizedYield = (esTemplateWithYield `yield ${estreeToolkit.is.expression};`);
|
|
1326
|
+
function isOptimizableYield(stmt) {
|
|
1327
|
+
return (estreeToolkit.is.expressionStatement(stmt) &&
|
|
1328
|
+
estreeToolkit.is.yieldExpression(stmt.expression) &&
|
|
1329
|
+
stmt.expression.delegate === false);
|
|
1330
|
+
}
|
|
1331
|
+
/** Returns null if the statement cannot be optimized. */
|
|
1332
|
+
function optimizeSingleStatement(stmt) {
|
|
1333
|
+
if (estreeToolkit.is.blockStatement(stmt)) {
|
|
1334
|
+
// `if (cond) { ... }` => optimize inner yields and see if we can condense
|
|
1335
|
+
const optimizedBlock = optimizeAdjacentYieldStmts(stmt.body);
|
|
1336
|
+
// More than one statement cannot be optimized into a single yield
|
|
1337
|
+
if (optimizedBlock.length !== 1)
|
|
1328
1338
|
return null;
|
|
1339
|
+
const [optimized] = optimizedBlock;
|
|
1340
|
+
return isOptimizableYield(optimized) ? optimized : null;
|
|
1341
|
+
}
|
|
1342
|
+
else if (estreeToolkit.is.expressionStatement(stmt)) {
|
|
1343
|
+
// `if (cond) expression` => just check if expression is a yield
|
|
1344
|
+
return estreeToolkit.is.yieldExpression(stmt) ? stmt : null;
|
|
1345
|
+
}
|
|
1346
|
+
else {
|
|
1347
|
+
// Can only optimize expression/block statements
|
|
1348
|
+
return null;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Tries to reduce if statements that only contain yields into a single yielded ternary
|
|
1353
|
+
* Returns null if the statement cannot be optimized.
|
|
1354
|
+
*/
|
|
1355
|
+
function optimizeIfStatement(stmt) {
|
|
1356
|
+
const consequent = optimizeSingleStatement(stmt.consequent)?.expression.argument;
|
|
1357
|
+
if (!consequent) {
|
|
1358
|
+
return null;
|
|
1359
|
+
}
|
|
1360
|
+
const alternate = stmt.alternate
|
|
1361
|
+
? optimizeSingleStatement(stmt.alternate)?.expression.argument
|
|
1362
|
+
: estreeToolkit.builders.literal('');
|
|
1363
|
+
if (!alternate) {
|
|
1364
|
+
return null;
|
|
1365
|
+
}
|
|
1366
|
+
return bYieldTernary(stmt.test, consequent, alternate);
|
|
1367
|
+
}
|
|
1368
|
+
function optimizeAdjacentYieldStmts(statements) {
|
|
1369
|
+
return statements.reduce((result, stmt) => {
|
|
1370
|
+
if (estreeToolkit.is.ifStatement(stmt)) {
|
|
1371
|
+
const optimized = optimizeIfStatement(stmt);
|
|
1372
|
+
if (optimized) {
|
|
1373
|
+
stmt = optimized;
|
|
1374
|
+
}
|
|
1329
1375
|
}
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1376
|
+
const prev = result.at(-1);
|
|
1377
|
+
if (!isOptimizableYield(stmt) || !isOptimizableYield(prev)) {
|
|
1378
|
+
// nothing to do
|
|
1379
|
+
return [...result, stmt];
|
|
1380
|
+
}
|
|
1381
|
+
const arg = stmt.expression.argument;
|
|
1382
|
+
if (!arg || (estreeToolkit.is.literal(arg) && arg.value === '')) {
|
|
1383
|
+
// bare `yield` and `yield ""` amount to nothing, so we can drop them
|
|
1384
|
+
return result;
|
|
1385
|
+
}
|
|
1386
|
+
const newArg = immer.produce(prev.expression.argument, (draft) => {
|
|
1387
|
+
if (estreeToolkit.is.literal(arg) && typeof arg.value === 'string') {
|
|
1388
|
+
let concatTail = draft;
|
|
1389
|
+
while (estreeToolkit.is.binaryExpression(concatTail) && concatTail.operator === '+') {
|
|
1390
|
+
concatTail = concatTail.right;
|
|
1391
|
+
}
|
|
1392
|
+
if (estreeToolkit.is.literal(concatTail) && typeof concatTail.value === 'string') {
|
|
1393
|
+
// conat adjacent strings now, rather than at runtime
|
|
1394
|
+
concatTail.value += arg.value;
|
|
1395
|
+
return draft;
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
// concat arbitrary values at runtime
|
|
1399
|
+
return estreeToolkit.builders.binaryExpression('+', draft, arg);
|
|
1400
|
+
});
|
|
1401
|
+
// replace the last `+` chain with a new one with the new arg
|
|
1402
|
+
return [...result.slice(0, -1), bOptimizedYield(newArg)];
|
|
1403
|
+
}, []);
|
|
1334
1404
|
}
|
|
1335
1405
|
function bAttributeValue(node, attrName) {
|
|
1336
1406
|
if (!('attributes' in node)) {
|
|
@@ -1794,13 +1864,14 @@ estreeToolkit.is.statement}
|
|
|
1794
1864
|
instance,
|
|
1795
1865
|
scopeToken,
|
|
1796
1866
|
contextfulParent,
|
|
1867
|
+
renderContext,
|
|
1797
1868
|
shadowSlottedContent,
|
|
1798
1869
|
lightSlottedContentMap,
|
|
1799
1870
|
scopedSlottedContentMap
|
|
1800
1871
|
);
|
|
1801
1872
|
} else {
|
|
1802
1873
|
yield \`<\${tagName}>\`;
|
|
1803
|
-
yield* __fallbackTmpl(shadowSlottedContent, lightSlottedContentMap, scopedSlottedContentMap, ${ /* Component */3}, instance)
|
|
1874
|
+
yield* __fallbackTmpl(shadowSlottedContent, lightSlottedContentMap, scopedSlottedContentMap, ${ /* Component */3}, instance, renderContext)
|
|
1804
1875
|
yield \`</\${tagName}>\`;
|
|
1805
1876
|
}
|
|
1806
1877
|
}
|
|
@@ -1859,6 +1930,7 @@ estreeToolkit.is.statement}
|
|
|
1859
1930
|
instance,
|
|
1860
1931
|
scopeToken,
|
|
1861
1932
|
contextfulParent,
|
|
1933
|
+
renderContext,
|
|
1862
1934
|
shadowSlottedContent,
|
|
1863
1935
|
lightSlottedContentMap,
|
|
1864
1936
|
scopedSlottedContentMap
|
|
@@ -2185,9 +2257,9 @@ const bConditionalSlot = (esTemplateWithYield `
|
|
|
2185
2257
|
if (isLightDom) {
|
|
2186
2258
|
const isScopedSlot = ${ /* isScopedSlot */estreeToolkit.is.literal};
|
|
2187
2259
|
const isSlotted = ${ /* isSlotted */estreeToolkit.is.literal};
|
|
2188
|
-
const slotName = ${ /* slotName */estreeToolkit.is.expression};
|
|
2189
|
-
const lightGenerators = lightSlottedContent?.[slotName
|
|
2190
|
-
const scopedGenerators = scopedSlottedContent?.[slotName
|
|
2260
|
+
const slotName = ${ /* slotName */estreeToolkit.is.expression} ?? "";
|
|
2261
|
+
const lightGenerators = lightSlottedContent?.[slotName];
|
|
2262
|
+
const scopedGenerators = scopedSlottedContent?.[slotName];
|
|
2191
2263
|
const mismatchedSlots = isScopedSlot ? lightGenerators : scopedGenerators;
|
|
2192
2264
|
const generators = isScopedSlot ? scopedGenerators : lightGenerators;
|
|
2193
2265
|
/*
|
|
@@ -2494,12 +2566,12 @@ function templateIrToEsTree(node, contextOpts) {
|
|
|
2494
2566
|
// TODO [#4663]: Render mode mismatch between template and compiler should throw.
|
|
2495
2567
|
const bExportTemplate = (esTemplate `
|
|
2496
2568
|
export default async function* __lwcTmpl(
|
|
2497
|
-
// This is where $$emit comes from
|
|
2498
2569
|
shadowSlottedContent,
|
|
2499
2570
|
lightSlottedContent,
|
|
2500
2571
|
scopedSlottedContent,
|
|
2501
2572
|
Cmp,
|
|
2502
|
-
instance
|
|
2573
|
+
instance,
|
|
2574
|
+
renderContext
|
|
2503
2575
|
) {
|
|
2504
2576
|
// Deliberately using let so we can mutate as many times as we want in the same scope.
|
|
2505
2577
|
// These should be scoped to the "tmpl" function however, to avoid conflicts with other templates.
|
|
@@ -2521,7 +2593,7 @@ const bExportTemplate = (esTemplate `
|
|
|
2521
2593
|
const { stylesheets: staticStylesheets } = Cmp;
|
|
2522
2594
|
if (defaultStylesheets || defaultScopedStylesheets || staticStylesheets) {
|
|
2523
2595
|
yield renderStylesheets(
|
|
2524
|
-
|
|
2596
|
+
renderContext,
|
|
2525
2597
|
defaultStylesheets,
|
|
2526
2598
|
defaultScopedStylesheets,
|
|
2527
2599
|
staticStylesheets,
|
|
@@ -2644,5 +2716,5 @@ function compileTemplateForSSR(src, filename, options, mode = shared.DEFAULT_SSR
|
|
|
2644
2716
|
|
|
2645
2717
|
exports.compileComponentForSSR = compileComponentForSSR;
|
|
2646
2718
|
exports.compileTemplateForSSR = compileTemplateForSSR;
|
|
2647
|
-
/** version: 8.
|
|
2719
|
+
/** version: 8.25.0 */
|
|
2648
2720
|
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.js
CHANGED
|
@@ -3,43 +3,207 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { AMBIGUOUS_PROP_SET, DISALLOWED_PROP_SET, LWC_VERSION_COMMENT, isAPIFeatureEnabled, normalizeStyleAttributeValue, normalizeTabIndex, StringReplace, StringTrim, entries, kebabCaseToCamelCase, isUndefined, HTML_NAMESPACE, isVoidElement, isBooleanAttribute, DEFAULT_SSR_MODE, generateCustomElementTagName } from '@lwc/shared';
|
|
5
5
|
import { generate } from 'astring';
|
|
6
|
-
import {
|
|
6
|
+
import { traverse, is, builders } from 'estree-toolkit';
|
|
7
7
|
import { parseModule } from 'meriyah';
|
|
8
8
|
import { generateCompilerError, DecoratorErrors, SsrCompilerErrors, LWCClassErrors } from '@lwc/errors';
|
|
9
9
|
import { produce } from 'immer';
|
|
10
|
-
import { parse as parse$1 } from 'node:path';
|
|
11
10
|
import { parse } from 'acorn';
|
|
11
|
+
import { parse as parse$1 } from 'node:path';
|
|
12
12
|
import { generateScopeTokens, bindExpression, toPropertyName, kebabcaseToCamelcase, parse as parse$2 } from '@lwc/template-compiler';
|
|
13
13
|
import { builders as builders$1 } from 'estree-toolkit/dist/builders';
|
|
14
14
|
import { isValidES3Identifier } from '@babel/types';
|
|
15
15
|
import { inspect } from 'util';
|
|
16
16
|
|
|
17
|
+
/*
|
|
18
|
+
* Copyright (c) 2024, salesforce.com, inc.
|
|
19
|
+
* All rights reserved.
|
|
20
|
+
* SPDX-License-Identifier: MIT
|
|
21
|
+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
22
|
+
*/
|
|
23
|
+
/** Placeholder value to use to opt out of validation. */
|
|
24
|
+
const NO_VALIDATION = false;
|
|
25
|
+
/**
|
|
26
|
+
* `esTemplate` generates JS code with "holes" to be filled later. In order to have a valid AST,
|
|
27
|
+
* it uses identifiers with this prefix at the location of the holes.
|
|
28
|
+
*/
|
|
29
|
+
const PLACEHOLDER_PREFIX = '__lwc_ESTEMPLATE_PLACEHOLDER__';
|
|
30
|
+
const getReplacementNode = (state, placeholderId) => {
|
|
31
|
+
const key = Number(placeholderId.slice(PLACEHOLDER_PREFIX.length));
|
|
32
|
+
const nodeCount = state.replacementNodes.length;
|
|
33
|
+
if (key >= nodeCount) {
|
|
34
|
+
throw new Error(`Cannot use index ${key} when only ${nodeCount} values have been provided.`);
|
|
35
|
+
}
|
|
36
|
+
const validateReplacement = state.placeholderToValidator.get(key);
|
|
37
|
+
const replacementNode = state.replacementNodes[key];
|
|
38
|
+
if (validateReplacement &&
|
|
39
|
+
!(Array.isArray(replacementNode)
|
|
40
|
+
? replacementNode.every(validateReplacement)
|
|
41
|
+
: validateReplacement(replacementNode))) {
|
|
42
|
+
const expectedType = validateReplacement.__debugName ||
|
|
43
|
+
validateReplacement.name ||
|
|
44
|
+
'(could not determine)';
|
|
45
|
+
const actualType = Array.isArray(replacementNode)
|
|
46
|
+
? `[${replacementNode.map((n) => n && n.type).join(', ')}]`
|
|
47
|
+
: replacementNode?.type;
|
|
48
|
+
throw new Error(`Validation failed for templated node. Expected type ${expectedType}, but received ${actualType}.`);
|
|
49
|
+
}
|
|
50
|
+
return replacementNode;
|
|
51
|
+
};
|
|
52
|
+
const visitors$2 = {
|
|
53
|
+
Identifier(path, state) {
|
|
54
|
+
if (path.node?.name.startsWith(PLACEHOLDER_PREFIX)) {
|
|
55
|
+
const replacementNode = getReplacementNode(state, path.node.name);
|
|
56
|
+
if (replacementNode === null) {
|
|
57
|
+
path.remove();
|
|
58
|
+
}
|
|
59
|
+
else if (Array.isArray(replacementNode)) {
|
|
60
|
+
if (replacementNode.length === 0) {
|
|
61
|
+
path.remove();
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
if (path.parentPath?.node?.type === 'ExpressionStatement') {
|
|
65
|
+
path.parentPath.replaceWithMultiple(replacementNode);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
path.replaceWithMultiple(replacementNode);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
path.replaceWith(replacementNode);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
Literal(path, state) {
|
|
78
|
+
if (typeof path.node?.value === 'string' &&
|
|
79
|
+
path.node.value.startsWith(PLACEHOLDER_PREFIX)) {
|
|
80
|
+
// A literal can only be replaced with a single node
|
|
81
|
+
const replacementNode = getReplacementNode(state, path.node.value);
|
|
82
|
+
path.replaceWith(replacementNode);
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
function esTemplateImpl(javascriptSegments, validators, wrap, unwrap) {
|
|
87
|
+
let placeholderCount = 0;
|
|
88
|
+
let parsableCode = javascriptSegments[0];
|
|
89
|
+
const placeholderToValidator = new Map();
|
|
90
|
+
for (let i = 1; i < javascriptSegments.length; i += 1) {
|
|
91
|
+
const segment = javascriptSegments[i];
|
|
92
|
+
const validator = validators[i - 1]; // always one less value than strings in template literals
|
|
93
|
+
if (typeof validator === 'function' || validator === NO_VALIDATION) {
|
|
94
|
+
// Template slot will be filled by a *new* argument passed to the generated function
|
|
95
|
+
if (validator !== NO_VALIDATION) {
|
|
96
|
+
placeholderToValidator.set(placeholderCount, validator);
|
|
97
|
+
}
|
|
98
|
+
parsableCode += `${PLACEHOLDER_PREFIX}${placeholderCount}`;
|
|
99
|
+
placeholderCount += 1;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// Template slot uses a *previously defined* argument passed to the generated function
|
|
103
|
+
if (validator >= placeholderCount) {
|
|
104
|
+
throw new Error(`Reference to argument ${validator} at index ${i} cannot be used. Only ${placeholderCount - 1} arguments have been defined.`);
|
|
105
|
+
}
|
|
106
|
+
parsableCode += `${PLACEHOLDER_PREFIX}${validator}`;
|
|
107
|
+
}
|
|
108
|
+
parsableCode += segment;
|
|
109
|
+
}
|
|
110
|
+
if (wrap) {
|
|
111
|
+
parsableCode = wrap(parsableCode);
|
|
112
|
+
}
|
|
113
|
+
const originalAstProgram = parse(parsableCode, {
|
|
114
|
+
ecmaVersion: 2022,
|
|
115
|
+
allowAwaitOutsideFunction: true,
|
|
116
|
+
allowReturnOutsideFunction: true,
|
|
117
|
+
allowSuperOutsideMethod: true,
|
|
118
|
+
allowImportExportEverywhere: true,
|
|
119
|
+
locations: false,
|
|
120
|
+
});
|
|
121
|
+
let originalAst;
|
|
122
|
+
const finalCharacter = javascriptSegments.at(-1)?.trimEnd()?.at(-1);
|
|
123
|
+
if (originalAstProgram.body.length === 1) {
|
|
124
|
+
originalAst =
|
|
125
|
+
finalCharacter === ';' && originalAstProgram.body[0].type === 'ExpressionStatement'
|
|
126
|
+
? (originalAst = originalAstProgram.body[0].expression)
|
|
127
|
+
: (originalAst = originalAstProgram.body[0]);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
originalAst = originalAstProgram.body;
|
|
131
|
+
}
|
|
132
|
+
// Turns Acorn AST objects into POJOs, for use with Immer.
|
|
133
|
+
originalAst = JSON.parse(JSON.stringify(originalAst));
|
|
134
|
+
return function templatedAst(...replacementNodes) {
|
|
135
|
+
const result = produce(originalAst, (astDraft) => traverse(astDraft, visitors$2, {
|
|
136
|
+
placeholderToValidator,
|
|
137
|
+
replacementNodes,
|
|
138
|
+
}));
|
|
139
|
+
return (unwrap ? unwrap(result) : result);
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Template literal tag that generates a builder function. Like estree's `builders`, but for more
|
|
144
|
+
* complex structures. The template values should be estree `is` validators or a back reference to
|
|
145
|
+
* a previous slot (to re-use the referenced value).
|
|
146
|
+
*
|
|
147
|
+
* To have the generated function return a particular node type, the generic comes _after_ the
|
|
148
|
+
* template literal. Kinda weird, but it's necessary to infer the types of the template values.
|
|
149
|
+
* (If it were at the start, we'd need to explicitly provide _all_ type params. Tedious!)
|
|
150
|
+
* @example
|
|
151
|
+
* const bSum = esTemplate`(${is.identifier}, ${is.identifier}) => ${0} + ${1}`<EsArrowFunctionExpression>
|
|
152
|
+
* const sumFuncNode = bSum(b.identifier('a'), b.identifier('b'))
|
|
153
|
+
* // `sumFuncNode` is an AST node representing `(a, b) => a + b`
|
|
154
|
+
*/
|
|
155
|
+
function esTemplate(javascriptSegments, ...Validators) {
|
|
156
|
+
return esTemplateImpl(javascriptSegments, Validators);
|
|
157
|
+
}
|
|
158
|
+
/** Similar to {@linkcode esTemplate}, but supports `yield` expressions. */
|
|
159
|
+
function esTemplateWithYield(javascriptSegments, ...validators) {
|
|
160
|
+
const wrap = (code) => `function* placeholder() {${code}}`;
|
|
161
|
+
const unwrap = (node) => node.body.body.length === 1 ? node.body.body[0] : node.body.body;
|
|
162
|
+
return esTemplateImpl(javascriptSegments, validators, wrap, unwrap);
|
|
163
|
+
}
|
|
164
|
+
|
|
17
165
|
/*
|
|
18
166
|
* Copyright (c) 2024, Salesforce, Inc.
|
|
19
167
|
* All rights reserved.
|
|
20
168
|
* SPDX-License-Identifier: MIT
|
|
21
169
|
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
22
170
|
*/
|
|
23
|
-
const EMIT_IDENT = builders.identifier('$$emit');
|
|
24
171
|
/** Function names that may be transmogrified. All should start with `__lwc`. */
|
|
25
172
|
// Rollup may rename variables to prevent shadowing. When it does, it uses the format `foo$0`, `foo$1`, etc.
|
|
26
173
|
const TRANSMOGRIFY_TARGET = /^__lwc(Generate|Tmpl).*$/;
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
174
|
+
const isNonArrowFunction = (node) => {
|
|
175
|
+
return is.functionDeclaration(node) || is.functionExpression(node);
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* Determines whether a node is a function we want to transmogrify or within one, at any level.
|
|
179
|
+
*/
|
|
180
|
+
const isWithinTargetFunc = (nodePath) => {
|
|
181
|
+
let path = isNonArrowFunction(nodePath)
|
|
182
|
+
? nodePath
|
|
183
|
+
: nodePath.findParent(isNonArrowFunction);
|
|
184
|
+
while (path?.node) {
|
|
185
|
+
const { id } = path.node;
|
|
186
|
+
if (id && TRANSMOGRIFY_TARGET.test(id.name)) {
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
path = path.findParent(isNonArrowFunction);
|
|
39
190
|
}
|
|
40
191
|
return false;
|
|
41
192
|
};
|
|
42
|
-
|
|
193
|
+
/**
|
|
194
|
+
* Determines whether the nearest function encapsulating this node is a function we transmogrify.
|
|
195
|
+
*/
|
|
196
|
+
const isImmediateWithinTargetFunc = (nodePath) => {
|
|
197
|
+
const parentFunc = nodePath.findParent(is.function);
|
|
198
|
+
return Boolean(parentFunc &&
|
|
199
|
+
isNonArrowFunction(parentFunc) &&
|
|
200
|
+
parentFunc.node?.id &&
|
|
201
|
+
TRANSMOGRIFY_TARGET.test(parentFunc.node.id.name));
|
|
202
|
+
};
|
|
203
|
+
const bDeclareYieldVar = (esTemplate `let __lwcYield = '';`);
|
|
204
|
+
const bAppendToYieldVar = (esTemplate `__lwcYield += ${is.expression};`);
|
|
205
|
+
const bReturnYieldVar = (esTemplate `return __lwcYield;`);
|
|
206
|
+
const visitors$1 = {
|
|
43
207
|
// @ts-expect-error types for `traverse` do not support sharing a visitor between node types:
|
|
44
208
|
// https://github.com/sarsamurmu/estree-toolkit/issues/20
|
|
45
209
|
'FunctionDeclaration|FunctionExpression'(path, state) {
|
|
@@ -50,12 +214,12 @@ const visitors$2 = {
|
|
|
50
214
|
// Component authors might conceivably use async generator functions in their own code. Therefore,
|
|
51
215
|
// when traversing & transforming written+generated code, we need to disambiguate generated async
|
|
52
216
|
// generator functions from those that were written by the component author.
|
|
53
|
-
if (!
|
|
217
|
+
if (!isWithinTargetFunc(path)) {
|
|
54
218
|
return;
|
|
55
219
|
}
|
|
56
220
|
node.generator = false;
|
|
57
221
|
node.async = state.mode === 'async';
|
|
58
|
-
node.
|
|
222
|
+
node.body.body = [bDeclareYieldVar(), ...node.body.body, bReturnYieldVar()];
|
|
59
223
|
},
|
|
60
224
|
YieldExpression(path, state) {
|
|
61
225
|
const { node } = path;
|
|
@@ -65,26 +229,15 @@ const visitors$2 = {
|
|
|
65
229
|
// Component authors might conceivably use generator functions within their own code. Therefore,
|
|
66
230
|
// when traversing & transforming written+generated code, we need to disambiguate generated yield
|
|
67
231
|
// expressions from those that were written by the component author.
|
|
68
|
-
if (!
|
|
232
|
+
if (!isWithinTargetFunc(path)) {
|
|
69
233
|
return;
|
|
70
234
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
const callExpr = node.argument;
|
|
77
|
-
callExpr.arguments.unshift(EMIT_IDENT);
|
|
78
|
-
path.replaceWith(state.mode === 'sync' ? callExpr : builders.awaitExpression(callExpr));
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
// transform `yield foo` into `$$emit(foo)`
|
|
82
|
-
const emittedExpression = node.argument;
|
|
83
|
-
if (!emittedExpression) {
|
|
84
|
-
throw new Error('Implementation error: cannot transform a yield expression that yields nothing');
|
|
85
|
-
}
|
|
86
|
-
path.replaceWith(builders.callExpression(EMIT_IDENT, [emittedExpression]));
|
|
235
|
+
const arg = node.argument;
|
|
236
|
+
if (!arg) {
|
|
237
|
+
const type = node.delegate ? 'yield*' : 'yield';
|
|
238
|
+
throw new Error(`Cannot transmogrify ${type} statement without an argument.`);
|
|
87
239
|
}
|
|
240
|
+
path.replaceWith(bAppendToYieldVar(state.mode === 'sync' ? arg : builders.awaitExpression(arg)));
|
|
88
241
|
},
|
|
89
242
|
ImportSpecifier(path, _state) {
|
|
90
243
|
// @lwc/ssr-runtime has a couple of helper functions that need to conform to either the generator or
|
|
@@ -111,6 +264,17 @@ const visitors$2 = {
|
|
|
111
264
|
node.imported.name = 'renderAttrsNoYield';
|
|
112
265
|
}
|
|
113
266
|
},
|
|
267
|
+
ReturnStatement(path) {
|
|
268
|
+
if (!isImmediateWithinTargetFunc(path)) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
// The transmogrify result returns __lwcYield, so we skip it
|
|
272
|
+
const arg = path.node?.argument;
|
|
273
|
+
if (is.identifier(arg) && arg.name === '__lwcYield') {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
throw new Error('Cannot transmogrify function with return statement.');
|
|
277
|
+
},
|
|
114
278
|
};
|
|
115
279
|
/**
|
|
116
280
|
* Transforms async-generator code into either the async or synchronous alternatives that are
|
|
@@ -156,7 +320,7 @@ function transmogrify(compiledComponentAst, mode = 'sync') {
|
|
|
156
320
|
const state = {
|
|
157
321
|
mode,
|
|
158
322
|
};
|
|
159
|
-
return produce(compiledComponentAst, (astDraft) => traverse(astDraft, visitors$
|
|
323
|
+
return produce(compiledComponentAst, (astDraft) => traverse(astDraft, visitors$1, state));
|
|
160
324
|
}
|
|
161
325
|
|
|
162
326
|
/******************************************************************************
|
|
@@ -365,154 +529,6 @@ function catalogStaticStylesheets(ids, state) {
|
|
|
365
529
|
}
|
|
366
530
|
}
|
|
367
531
|
|
|
368
|
-
/*
|
|
369
|
-
* Copyright (c) 2024, salesforce.com, inc.
|
|
370
|
-
* All rights reserved.
|
|
371
|
-
* SPDX-License-Identifier: MIT
|
|
372
|
-
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
373
|
-
*/
|
|
374
|
-
/** Placeholder value to use to opt out of validation. */
|
|
375
|
-
const NO_VALIDATION = false;
|
|
376
|
-
/**
|
|
377
|
-
* `esTemplate` generates JS code with "holes" to be filled later. In order to have a valid AST,
|
|
378
|
-
* it uses identifiers with this prefix at the location of the holes.
|
|
379
|
-
*/
|
|
380
|
-
const PLACEHOLDER_PREFIX = '__lwc_ESTEMPLATE_PLACEHOLDER__';
|
|
381
|
-
const getReplacementNode = (state, placeholderId) => {
|
|
382
|
-
const key = Number(placeholderId.slice(PLACEHOLDER_PREFIX.length));
|
|
383
|
-
const nodeCount = state.replacementNodes.length;
|
|
384
|
-
if (key >= nodeCount) {
|
|
385
|
-
throw new Error(`Cannot use index ${key} when only ${nodeCount} values have been provided.`);
|
|
386
|
-
}
|
|
387
|
-
const validateReplacement = state.placeholderToValidator.get(key);
|
|
388
|
-
const replacementNode = state.replacementNodes[key];
|
|
389
|
-
if (validateReplacement &&
|
|
390
|
-
!(Array.isArray(replacementNode)
|
|
391
|
-
? replacementNode.every(validateReplacement)
|
|
392
|
-
: validateReplacement(replacementNode))) {
|
|
393
|
-
const expectedType = validateReplacement.__debugName ||
|
|
394
|
-
validateReplacement.name ||
|
|
395
|
-
'(could not determine)';
|
|
396
|
-
const actualType = Array.isArray(replacementNode)
|
|
397
|
-
? `[${replacementNode.map((n) => n && n.type).join(', ')}]`
|
|
398
|
-
: replacementNode?.type;
|
|
399
|
-
throw new Error(`Validation failed for templated node. Expected type ${expectedType}, but received ${actualType}.`);
|
|
400
|
-
}
|
|
401
|
-
return replacementNode;
|
|
402
|
-
};
|
|
403
|
-
const visitors$1 = {
|
|
404
|
-
Identifier(path, state) {
|
|
405
|
-
if (path.node?.name.startsWith(PLACEHOLDER_PREFIX)) {
|
|
406
|
-
const replacementNode = getReplacementNode(state, path.node.name);
|
|
407
|
-
if (replacementNode === null) {
|
|
408
|
-
path.remove();
|
|
409
|
-
}
|
|
410
|
-
else if (Array.isArray(replacementNode)) {
|
|
411
|
-
if (replacementNode.length === 0) {
|
|
412
|
-
path.remove();
|
|
413
|
-
}
|
|
414
|
-
else {
|
|
415
|
-
if (path.parentPath?.node?.type === 'ExpressionStatement') {
|
|
416
|
-
path.parentPath.replaceWithMultiple(replacementNode);
|
|
417
|
-
}
|
|
418
|
-
else {
|
|
419
|
-
path.replaceWithMultiple(replacementNode);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
else {
|
|
424
|
-
path.replaceWith(replacementNode);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
},
|
|
428
|
-
Literal(path, state) {
|
|
429
|
-
if (typeof path.node?.value === 'string' &&
|
|
430
|
-
path.node.value.startsWith(PLACEHOLDER_PREFIX)) {
|
|
431
|
-
// A literal can only be replaced with a single node
|
|
432
|
-
const replacementNode = getReplacementNode(state, path.node.value);
|
|
433
|
-
path.replaceWith(replacementNode);
|
|
434
|
-
}
|
|
435
|
-
},
|
|
436
|
-
};
|
|
437
|
-
function esTemplateImpl(javascriptSegments, validators, wrap, unwrap) {
|
|
438
|
-
let placeholderCount = 0;
|
|
439
|
-
let parsableCode = javascriptSegments[0];
|
|
440
|
-
const placeholderToValidator = new Map();
|
|
441
|
-
for (let i = 1; i < javascriptSegments.length; i += 1) {
|
|
442
|
-
const segment = javascriptSegments[i];
|
|
443
|
-
const validator = validators[i - 1]; // always one less value than strings in template literals
|
|
444
|
-
if (typeof validator === 'function' || validator === NO_VALIDATION) {
|
|
445
|
-
// Template slot will be filled by a *new* argument passed to the generated function
|
|
446
|
-
if (validator !== NO_VALIDATION) {
|
|
447
|
-
placeholderToValidator.set(placeholderCount, validator);
|
|
448
|
-
}
|
|
449
|
-
parsableCode += `${PLACEHOLDER_PREFIX}${placeholderCount}`;
|
|
450
|
-
placeholderCount += 1;
|
|
451
|
-
}
|
|
452
|
-
else {
|
|
453
|
-
// Template slot uses a *previously defined* argument passed to the generated function
|
|
454
|
-
if (validator >= placeholderCount) {
|
|
455
|
-
throw new Error(`Reference to argument ${validator} at index ${i} cannot be used. Only ${placeholderCount - 1} arguments have been defined.`);
|
|
456
|
-
}
|
|
457
|
-
parsableCode += `${PLACEHOLDER_PREFIX}${validator}`;
|
|
458
|
-
}
|
|
459
|
-
parsableCode += segment;
|
|
460
|
-
}
|
|
461
|
-
if (wrap) {
|
|
462
|
-
parsableCode = wrap(parsableCode);
|
|
463
|
-
}
|
|
464
|
-
const originalAstProgram = parse(parsableCode, {
|
|
465
|
-
ecmaVersion: 2022,
|
|
466
|
-
allowAwaitOutsideFunction: true,
|
|
467
|
-
allowReturnOutsideFunction: true,
|
|
468
|
-
allowSuperOutsideMethod: true,
|
|
469
|
-
allowImportExportEverywhere: true,
|
|
470
|
-
locations: false,
|
|
471
|
-
});
|
|
472
|
-
let originalAst;
|
|
473
|
-
const finalCharacter = javascriptSegments.at(-1)?.trimEnd()?.at(-1);
|
|
474
|
-
if (originalAstProgram.body.length === 1) {
|
|
475
|
-
originalAst =
|
|
476
|
-
finalCharacter === ';' && originalAstProgram.body[0].type === 'ExpressionStatement'
|
|
477
|
-
? (originalAst = originalAstProgram.body[0].expression)
|
|
478
|
-
: (originalAst = originalAstProgram.body[0]);
|
|
479
|
-
}
|
|
480
|
-
else {
|
|
481
|
-
originalAst = originalAstProgram.body;
|
|
482
|
-
}
|
|
483
|
-
// Turns Acorn AST objects into POJOs, for use with Immer.
|
|
484
|
-
originalAst = JSON.parse(JSON.stringify(originalAst));
|
|
485
|
-
return function templatedAst(...replacementNodes) {
|
|
486
|
-
const result = produce(originalAst, (astDraft) => traverse(astDraft, visitors$1, {
|
|
487
|
-
placeholderToValidator,
|
|
488
|
-
replacementNodes,
|
|
489
|
-
}));
|
|
490
|
-
return (unwrap ? unwrap(result) : result);
|
|
491
|
-
};
|
|
492
|
-
}
|
|
493
|
-
/**
|
|
494
|
-
* Template literal tag that generates a builder function. Like estree's `builders`, but for more
|
|
495
|
-
* complex structures. The template values should be estree `is` validators or a back reference to
|
|
496
|
-
* a previous slot (to re-use the referenced value).
|
|
497
|
-
*
|
|
498
|
-
* To have the generated function return a particular node type, the generic comes _after_ the
|
|
499
|
-
* template literal. Kinda weird, but it's necessary to infer the types of the template values.
|
|
500
|
-
* (If it were at the start, we'd need to explicitly provide _all_ type params. Tedious!)
|
|
501
|
-
* @example
|
|
502
|
-
* const bSum = esTemplate`(${is.identifier}, ${is.identifier}) => ${0} + ${1}`<EsArrowFunctionExpression>
|
|
503
|
-
* const sumFuncNode = bSum(b.identifier('a'), b.identifier('b'))
|
|
504
|
-
* // `sumFuncNode` is an AST node representing `(a, b) => a + b`
|
|
505
|
-
*/
|
|
506
|
-
function esTemplate(javascriptSegments, ...Validators) {
|
|
507
|
-
return esTemplateImpl(javascriptSegments, Validators);
|
|
508
|
-
}
|
|
509
|
-
/** Similar to {@linkcode esTemplate}, but supports `yield` expressions. */
|
|
510
|
-
function esTemplateWithYield(javascriptSegments, ...validators) {
|
|
511
|
-
const wrap = (code) => `function* placeholder() {${code}}`;
|
|
512
|
-
const unwrap = (node) => node.body.body.length === 1 ? node.body.body[0] : node.body.body;
|
|
513
|
-
return esTemplateImpl(javascriptSegments, validators, wrap, unwrap);
|
|
514
|
-
}
|
|
515
|
-
|
|
516
532
|
/*
|
|
517
533
|
* Copyright (c) 2024, Salesforce, Inc.
|
|
518
534
|
* All rights reserved.
|
|
@@ -716,13 +732,13 @@ const bGenerateMarkup = (esTemplate `
|
|
|
716
732
|
enumerable: false,
|
|
717
733
|
writable: false,
|
|
718
734
|
value: async function* __lwcGenerateMarkup(
|
|
719
|
-
// The $$emit function is magically inserted here
|
|
720
735
|
tagName,
|
|
721
736
|
props,
|
|
722
737
|
attrs,
|
|
723
738
|
parent,
|
|
724
739
|
scopeToken,
|
|
725
740
|
contextfulParent,
|
|
741
|
+
renderContext,
|
|
726
742
|
shadowSlottedContent,
|
|
727
743
|
lightSlottedContent,
|
|
728
744
|
scopedSlottedContent,
|
|
@@ -766,7 +782,8 @@ const bGenerateMarkup = (esTemplate `
|
|
|
766
782
|
lightSlottedContent,
|
|
767
783
|
scopedSlottedContent,
|
|
768
784
|
${ /*component class*/0},
|
|
769
|
-
instance
|
|
785
|
+
instance,
|
|
786
|
+
renderContext
|
|
770
787
|
);
|
|
771
788
|
yield \`</\${tagName}>\`;
|
|
772
789
|
}
|
|
@@ -1300,33 +1317,86 @@ function getRootIdentifier$1(node) {
|
|
|
1300
1317
|
* SPDX-License-Identifier: MIT
|
|
1301
1318
|
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
|
|
1302
1319
|
*/
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
is.expressionStatement(stmt) &&
|
|
1318
|
-
is.yieldExpression(stmt.expression) &&
|
|
1319
|
-
!stmt.expression.delegate &&
|
|
1320
|
-
stmt.expression.argument &&
|
|
1321
|
-
is.literal(stmt.expression.argument) &&
|
|
1322
|
-
typeof stmt.expression.argument.value === 'string') {
|
|
1323
|
-
prevStmt.expression.argument.value += stmt.expression.argument.value;
|
|
1320
|
+
const bYieldTernary = (esTemplateWithYield `yield ${is.expression} ? ${is.expression} : ${is.expression}`);
|
|
1321
|
+
const bOptimizedYield = (esTemplateWithYield `yield ${is.expression};`);
|
|
1322
|
+
function isOptimizableYield(stmt) {
|
|
1323
|
+
return (is.expressionStatement(stmt) &&
|
|
1324
|
+
is.yieldExpression(stmt.expression) &&
|
|
1325
|
+
stmt.expression.delegate === false);
|
|
1326
|
+
}
|
|
1327
|
+
/** Returns null if the statement cannot be optimized. */
|
|
1328
|
+
function optimizeSingleStatement(stmt) {
|
|
1329
|
+
if (is.blockStatement(stmt)) {
|
|
1330
|
+
// `if (cond) { ... }` => optimize inner yields and see if we can condense
|
|
1331
|
+
const optimizedBlock = optimizeAdjacentYieldStmts(stmt.body);
|
|
1332
|
+
// More than one statement cannot be optimized into a single yield
|
|
1333
|
+
if (optimizedBlock.length !== 1)
|
|
1324
1334
|
return null;
|
|
1335
|
+
const [optimized] = optimizedBlock;
|
|
1336
|
+
return isOptimizableYield(optimized) ? optimized : null;
|
|
1337
|
+
}
|
|
1338
|
+
else if (is.expressionStatement(stmt)) {
|
|
1339
|
+
// `if (cond) expression` => just check if expression is a yield
|
|
1340
|
+
return is.yieldExpression(stmt) ? stmt : null;
|
|
1341
|
+
}
|
|
1342
|
+
else {
|
|
1343
|
+
// Can only optimize expression/block statements
|
|
1344
|
+
return null;
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
/**
|
|
1348
|
+
* Tries to reduce if statements that only contain yields into a single yielded ternary
|
|
1349
|
+
* Returns null if the statement cannot be optimized.
|
|
1350
|
+
*/
|
|
1351
|
+
function optimizeIfStatement(stmt) {
|
|
1352
|
+
const consequent = optimizeSingleStatement(stmt.consequent)?.expression.argument;
|
|
1353
|
+
if (!consequent) {
|
|
1354
|
+
return null;
|
|
1355
|
+
}
|
|
1356
|
+
const alternate = stmt.alternate
|
|
1357
|
+
? optimizeSingleStatement(stmt.alternate)?.expression.argument
|
|
1358
|
+
: builders.literal('');
|
|
1359
|
+
if (!alternate) {
|
|
1360
|
+
return null;
|
|
1361
|
+
}
|
|
1362
|
+
return bYieldTernary(stmt.test, consequent, alternate);
|
|
1363
|
+
}
|
|
1364
|
+
function optimizeAdjacentYieldStmts(statements) {
|
|
1365
|
+
return statements.reduce((result, stmt) => {
|
|
1366
|
+
if (is.ifStatement(stmt)) {
|
|
1367
|
+
const optimized = optimizeIfStatement(stmt);
|
|
1368
|
+
if (optimized) {
|
|
1369
|
+
stmt = optimized;
|
|
1370
|
+
}
|
|
1325
1371
|
}
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1372
|
+
const prev = result.at(-1);
|
|
1373
|
+
if (!isOptimizableYield(stmt) || !isOptimizableYield(prev)) {
|
|
1374
|
+
// nothing to do
|
|
1375
|
+
return [...result, stmt];
|
|
1376
|
+
}
|
|
1377
|
+
const arg = stmt.expression.argument;
|
|
1378
|
+
if (!arg || (is.literal(arg) && arg.value === '')) {
|
|
1379
|
+
// bare `yield` and `yield ""` amount to nothing, so we can drop them
|
|
1380
|
+
return result;
|
|
1381
|
+
}
|
|
1382
|
+
const newArg = produce(prev.expression.argument, (draft) => {
|
|
1383
|
+
if (is.literal(arg) && typeof arg.value === 'string') {
|
|
1384
|
+
let concatTail = draft;
|
|
1385
|
+
while (is.binaryExpression(concatTail) && concatTail.operator === '+') {
|
|
1386
|
+
concatTail = concatTail.right;
|
|
1387
|
+
}
|
|
1388
|
+
if (is.literal(concatTail) && typeof concatTail.value === 'string') {
|
|
1389
|
+
// conat adjacent strings now, rather than at runtime
|
|
1390
|
+
concatTail.value += arg.value;
|
|
1391
|
+
return draft;
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
// concat arbitrary values at runtime
|
|
1395
|
+
return builders.binaryExpression('+', draft, arg);
|
|
1396
|
+
});
|
|
1397
|
+
// replace the last `+` chain with a new one with the new arg
|
|
1398
|
+
return [...result.slice(0, -1), bOptimizedYield(newArg)];
|
|
1399
|
+
}, []);
|
|
1330
1400
|
}
|
|
1331
1401
|
function bAttributeValue(node, attrName) {
|
|
1332
1402
|
if (!('attributes' in node)) {
|
|
@@ -1790,13 +1860,14 @@ is.statement}
|
|
|
1790
1860
|
instance,
|
|
1791
1861
|
scopeToken,
|
|
1792
1862
|
contextfulParent,
|
|
1863
|
+
renderContext,
|
|
1793
1864
|
shadowSlottedContent,
|
|
1794
1865
|
lightSlottedContentMap,
|
|
1795
1866
|
scopedSlottedContentMap
|
|
1796
1867
|
);
|
|
1797
1868
|
} else {
|
|
1798
1869
|
yield \`<\${tagName}>\`;
|
|
1799
|
-
yield* __fallbackTmpl(shadowSlottedContent, lightSlottedContentMap, scopedSlottedContentMap, ${ /* Component */3}, instance)
|
|
1870
|
+
yield* __fallbackTmpl(shadowSlottedContent, lightSlottedContentMap, scopedSlottedContentMap, ${ /* Component */3}, instance, renderContext)
|
|
1800
1871
|
yield \`</\${tagName}>\`;
|
|
1801
1872
|
}
|
|
1802
1873
|
}
|
|
@@ -1855,6 +1926,7 @@ is.statement}
|
|
|
1855
1926
|
instance,
|
|
1856
1927
|
scopeToken,
|
|
1857
1928
|
contextfulParent,
|
|
1929
|
+
renderContext,
|
|
1858
1930
|
shadowSlottedContent,
|
|
1859
1931
|
lightSlottedContentMap,
|
|
1860
1932
|
scopedSlottedContentMap
|
|
@@ -2181,9 +2253,9 @@ const bConditionalSlot = (esTemplateWithYield `
|
|
|
2181
2253
|
if (isLightDom) {
|
|
2182
2254
|
const isScopedSlot = ${ /* isScopedSlot */is.literal};
|
|
2183
2255
|
const isSlotted = ${ /* isSlotted */is.literal};
|
|
2184
|
-
const slotName = ${ /* slotName */is.expression};
|
|
2185
|
-
const lightGenerators = lightSlottedContent?.[slotName
|
|
2186
|
-
const scopedGenerators = scopedSlottedContent?.[slotName
|
|
2256
|
+
const slotName = ${ /* slotName */is.expression} ?? "";
|
|
2257
|
+
const lightGenerators = lightSlottedContent?.[slotName];
|
|
2258
|
+
const scopedGenerators = scopedSlottedContent?.[slotName];
|
|
2187
2259
|
const mismatchedSlots = isScopedSlot ? lightGenerators : scopedGenerators;
|
|
2188
2260
|
const generators = isScopedSlot ? scopedGenerators : lightGenerators;
|
|
2189
2261
|
/*
|
|
@@ -2490,12 +2562,12 @@ function templateIrToEsTree(node, contextOpts) {
|
|
|
2490
2562
|
// TODO [#4663]: Render mode mismatch between template and compiler should throw.
|
|
2491
2563
|
const bExportTemplate = (esTemplate `
|
|
2492
2564
|
export default async function* __lwcTmpl(
|
|
2493
|
-
// This is where $$emit comes from
|
|
2494
2565
|
shadowSlottedContent,
|
|
2495
2566
|
lightSlottedContent,
|
|
2496
2567
|
scopedSlottedContent,
|
|
2497
2568
|
Cmp,
|
|
2498
|
-
instance
|
|
2569
|
+
instance,
|
|
2570
|
+
renderContext
|
|
2499
2571
|
) {
|
|
2500
2572
|
// Deliberately using let so we can mutate as many times as we want in the same scope.
|
|
2501
2573
|
// These should be scoped to the "tmpl" function however, to avoid conflicts with other templates.
|
|
@@ -2517,7 +2589,7 @@ const bExportTemplate = (esTemplate `
|
|
|
2517
2589
|
const { stylesheets: staticStylesheets } = Cmp;
|
|
2518
2590
|
if (defaultStylesheets || defaultScopedStylesheets || staticStylesheets) {
|
|
2519
2591
|
yield renderStylesheets(
|
|
2520
|
-
|
|
2592
|
+
renderContext,
|
|
2521
2593
|
defaultStylesheets,
|
|
2522
2594
|
defaultScopedStylesheets,
|
|
2523
2595
|
staticStylesheets,
|
|
@@ -2639,5 +2711,5 @@ function compileTemplateForSSR(src, filename, options, mode = DEFAULT_SSR_MODE)
|
|
|
2639
2711
|
}
|
|
2640
2712
|
|
|
2641
2713
|
export { compileComponentForSSR, compileTemplateForSSR };
|
|
2642
|
-
/** version: 8.
|
|
2714
|
+
/** version: 8.25.0 */
|
|
2643
2715
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"You can safely modify dependencies, devDependencies, keywords, etc., but other props will be overwritten."
|
|
5
5
|
],
|
|
6
6
|
"name": "@lwc/ssr-compiler",
|
|
7
|
-
"version": "8.
|
|
7
|
+
"version": "8.25.0",
|
|
8
8
|
"description": "Compile component for use during server-side rendering",
|
|
9
9
|
"keywords": [
|
|
10
10
|
"compiler",
|
|
@@ -49,9 +49,9 @@
|
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@babel/types": "7.28.5",
|
|
52
|
-
"@lwc/errors": "8.
|
|
53
|
-
"@lwc/shared": "8.
|
|
54
|
-
"@lwc/template-compiler": "8.
|
|
52
|
+
"@lwc/errors": "8.25.0",
|
|
53
|
+
"@lwc/shared": "8.25.0",
|
|
54
|
+
"@lwc/template-compiler": "8.25.0",
|
|
55
55
|
"acorn": "8.15.0",
|
|
56
56
|
"astring": "^1.9.0",
|
|
57
57
|
"estree-toolkit": "^1.7.13",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"meriyah": "^5.0.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@lwc/babel-plugin-component": "8.
|
|
62
|
+
"@lwc/babel-plugin-component": "8.25.0",
|
|
63
63
|
"@types/estree": "^1.0.8"
|
|
64
64
|
}
|
|
65
65
|
}
|