@vitejs/plugin-rsc 0.5.32 → 0.5.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { i as tinyassert } from "../dist-uqdtSkzK.js";
2
- import { a as validateNonAsyncFunction, i as hasDirective, n as extractIdentifiers, r as extractNames, t as buildScopeTree } from "../scope-Do08R7C2.js";
2
+ import { a as isDirective, i as hasDirective, n as extractIdentifiers, o as rejectNonAsyncFunction, r as extractNames, s as validateNonAsyncFunction, t as buildScopeTree } from "../scope-Bdx33sQv.js";
3
3
  import MagicString from "magic-string";
4
4
  import { walk } from "estree-walker";
5
5
  //#region src/transforms/hoist.ts
@@ -55,20 +55,41 @@ import { walk } from "estree-walker";
55
55
  *
56
56
  * In this second sketch, `__ENCODE__(...)` and `__DECODE__(...)` likewise
57
57
  * represent the expressions returned by those code-generation callbacks.
58
+ *
59
+ * With `hoistRuntime`, the runtime result becomes the module-level binding and
60
+ * the moved function becomes its private implementation:
61
+ *
62
+ * ```js
63
+ * function Component() {
64
+ * const x = 1
65
+ * const action = $$hoist_0_action.bind(null, x)
66
+ * }
67
+ * export const $$hoist_0_action = __RUNTIME__($$hoist_0_action$$impl)
68
+ * async function $$hoist_0_action$$impl(x, y) {
69
+ * "use server"
70
+ * return x + y
71
+ * }
72
+ * ```
73
+ *
74
+ * `noExport` independently keeps `$$hoist_0_action` module-local when an
75
+ * integration needs module-scope runtime initialization without an export.
58
76
  */
59
- function transformHoistInlineDirective(input, ast, { runtime, rejectNonAsyncFunction, ...options }) {
77
+ function transformHoistInlineDirective(input, viteAst, { runtime, rejectNonAsyncFunction, ...options }) {
78
+ const ast = viteAst;
60
79
  if (!input.endsWith("\n")) input += "\n";
61
80
  const output = new MagicString(input);
62
81
  const directive = typeof options.directive === "string" ? exactRegex(options.directive) : options.directive;
63
82
  const scopeTree = buildScopeTree(ast);
64
83
  const names = [];
84
+ const runtimeHoists = [];
65
85
  walk(ast, { enter(node, parent) {
66
86
  if ((node.type === "FunctionExpression" || node.type === "FunctionDeclaration" || node.type === "ArrowFunctionExpression") && node.body.type === "BlockStatement") {
67
87
  const match = matchDirective(node.body.body, directive)?.match;
68
88
  if (!match) return;
89
+ const methodInfo = getMethodInfo(node, parent, match[0]);
69
90
  if (!node.async && rejectNonAsyncFunction) throw Object.assign(/* @__PURE__ */ new Error(`"${directive}" doesn't allow non async function`), { pos: node.start });
70
91
  const declName = node.type === "FunctionDeclaration" && node.id.name;
71
- const originalName = declName || parent?.type === "VariableDeclarator" && parent.id.type === "Identifier" && parent.id.name || "anonymous_server_function";
92
+ const originalName = declName || methodInfo?.name || parent?.type === "VariableDeclarator" && parent.id.type === "Identifier" && parent.id.name || "anonymous_server_function";
72
93
  const bindVars = getBindVars(node, scopeTree);
73
94
  let newParams = [...bindVars.map((b) => b.root), ...node.params.map((n) => input.slice(n.start, n.end))].join(", ");
74
95
  if (bindVars.length > 0 && options.decode) {
@@ -77,15 +98,27 @@ function transformHoistInlineDirective(input, ast, { runtime, rejectNonAsyncFunc
77
98
  }
78
99
  const newName = `$$hoist_${names.length}` + (originalName ? `_${originalName}` : "");
79
100
  names.push(newName);
80
- output.update(node.start, node.body.start, `\n;${options.noExport ? "" : "export "}${node.async ? "async " : ""}function${node.generator ? "*" : ""} ${newName}(${newParams}) `);
81
- output.appendLeft(node.end, `;\n/* #__PURE__ */ Object.defineProperty(${newName}, "name", { value: ${JSON.stringify(originalName)} });\n`);
101
+ const implementationName = options.hoistRuntime ? `${newName}$$impl` : newName;
102
+ output.update(node.start, node.body.start, `\n;${options.noExport || options.hoistRuntime ? "" : "export "}${node.async ? "async " : ""}function${node.generator ? "*" : ""} ${implementationName}(${newParams}) `);
103
+ const runtimeCode = `/* #__PURE__ */ ${runtime(implementationName, newName, {
104
+ directiveMatch: match,
105
+ valueNode: node
106
+ })}`;
107
+ if (options.hoistRuntime) runtimeHoists.push(`${options.noExport ? "" : "export "}const ${newName} = ${runtimeCode};\n`);
108
+ output.appendLeft(node.end, `;\n/* #__PURE__ */ Object.defineProperty(${implementationName}, "name", { value: ${JSON.stringify(originalName)} });\n`);
82
109
  output.move(node.start, node.end, input.length);
83
- let newCode = `/* #__PURE__ */ ${runtime(newName, newName, { directiveMatch: match })}`;
110
+ let newCode = options.hoistRuntime ? newName : runtimeCode;
84
111
  if (bindVars.length > 0) {
85
112
  const bindArgs = options.encode ? options.encode("[" + bindVars.map((b) => b.expr).join(", ") + "]") : bindVars.map((b) => b.expr).join(", ");
86
113
  newCode = `${newCode}.bind(null, ${bindArgs})`;
87
114
  }
88
- if (declName) {
115
+ if (methodInfo) {
116
+ const isStatic = methodInfo.node.type === "MethodDefinition";
117
+ const key = input.slice(methodInfo.node.key.start, methodInfo.node.key.end);
118
+ const propertyKey = !methodInfo.node.computed && methodInfo.node.key.type === "Identifier" ? `["${key}"]` : `[${key}]`;
119
+ output.update(methodInfo.node.start, node.start, `${isStatic ? "static " : ""}${propertyKey}${isStatic ? " = " : ": "}`);
120
+ if (isStatic) newCode += ";";
121
+ } else if (declName) {
89
122
  newCode = `const ${declName} = ${newCode};`;
90
123
  if (parent?.type === "ExportDefaultDeclaration") {
91
124
  output.remove(parent.start, node.start);
@@ -95,22 +128,44 @@ function transformHoistInlineDirective(input, ast, { runtime, rejectNonAsyncFunc
95
128
  output.appendLeft(node.start, newCode);
96
129
  }
97
130
  } });
131
+ if (runtimeHoists.length > 0) output.prependLeft(getRuntimeHoistPosition(ast), runtimeHoists.join(""));
98
132
  return {
99
133
  output,
100
134
  names
101
135
  };
102
136
  }
137
+ function getMethodInfo(node, parent, directive) {
138
+ if (node.type !== "FunctionExpression") return;
139
+ let method;
140
+ if (parent?.type === "Property" && parent.value === node && (parent.method || parent.kind !== "init")) method = parent;
141
+ else if (parent?.type === "MethodDefinition") method = parent;
142
+ else return;
143
+ if (method.type === "MethodDefinition" && !method.static) throw Object.assign(/* @__PURE__ */ new Error(`It is not allowed to define inline ${JSON.stringify(directive)} class instance methods.`), { pos: method.start });
144
+ if (method.type === "MethodDefinition" && method.key.type === "PrivateIdentifier") throw Object.assign(/* @__PURE__ */ new Error(`It is not allowed to define inline ${JSON.stringify(directive)} private class methods.`), { pos: method.start });
145
+ if (method.type === "Property" && method.kind !== "init" || method.type === "MethodDefinition" && method.kind !== "method") throw Object.assign(/* @__PURE__ */ new Error(`It is not allowed to define inline ${JSON.stringify(directive)} getters or setters.`), { pos: method.start });
146
+ const keyName = !method.computed && method.key.type === "Identifier" ? method.key.name : void 0;
147
+ return {
148
+ node: method,
149
+ name: keyName
150
+ };
151
+ }
152
+ function getRuntimeHoistPosition(ast) {
153
+ for (const statement of ast.body) if (!isDirective(statement)) return statement.start;
154
+ return 0;
155
+ }
103
156
  const exactRegex = (s) => new RegExp("^" + s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&") + "$");
104
157
  function matchDirective(body, directive) {
105
- for (const stmt of body) if (stmt.type === "ExpressionStatement" && stmt.expression.type === "Literal" && typeof stmt.expression.value === "string") {
106
- const match = stmt.expression.value.match(directive);
158
+ for (const stmt of body) {
159
+ if (!isDirective(stmt)) return;
160
+ const match = stmt.directive.match(directive);
107
161
  if (match) return {
108
162
  match,
109
163
  node: stmt.expression
110
164
  };
111
165
  }
112
166
  }
113
- function findDirectives(ast, directive) {
167
+ function findDirectives(viteAst, directive) {
168
+ const ast = viteAst;
114
169
  const directiveRE = exactRegex(directive);
115
170
  const nodes = [];
116
171
  walk(ast, { enter(node) {
@@ -209,128 +264,374 @@ function dedupeByPrefix(paths) {
209
264
  return result;
210
265
  }
211
266
  //#endregion
267
+ //#region src/transforms/module-export-scan.ts
268
+ function scanModuleExports(viteAst) {
269
+ const ast = viteAst;
270
+ const groups = [];
271
+ for (const node of ast.body) if (node.type === "ExportNamedDeclaration") if (node.declaration) if (node.declaration.type === "VariableDeclaration") {
272
+ const declaration = node.declaration;
273
+ groups.push({
274
+ type: "variable-declaration",
275
+ node,
276
+ declaration,
277
+ declarators: declaration.declarations.map((declarator) => {
278
+ const isFunction = declarator.id.type === "Identifier" && declarator.init ? getIsFunction(declarator.init) : void 0;
279
+ return {
280
+ node: declarator,
281
+ exports: extractNames(declarator.id).map((name) => ({
282
+ localName: name,
283
+ exportName: name,
284
+ meta: {
285
+ declName: name,
286
+ isFunction,
287
+ valueNode: declarator.init ?? void 0
288
+ }
289
+ }))
290
+ };
291
+ })
292
+ });
293
+ } else {
294
+ tinyassert(node.declaration.id);
295
+ const name = node.declaration.id.name;
296
+ groups.push({
297
+ type: "declaration",
298
+ node,
299
+ declaration: node.declaration,
300
+ export: {
301
+ localName: name,
302
+ exportName: name,
303
+ meta: {
304
+ declName: name,
305
+ isFunction: getIsFunction(node.declaration),
306
+ valueNode: node.declaration
307
+ }
308
+ }
309
+ });
310
+ }
311
+ else groups.push({
312
+ type: "specifiers",
313
+ node,
314
+ exports: node.specifiers.map((specifier) => {
315
+ return {
316
+ node: specifier,
317
+ localName: specifier.local.type === "Identifier" ? specifier.local.name : "__unsupported_string_export__",
318
+ exportName: specifier.exported.type === "Identifier" ? specifier.exported.name : "__unsupported_string_export__",
319
+ meta: {}
320
+ };
321
+ })
322
+ });
323
+ else if (node.type === "ExportAllDeclaration") groups.push({
324
+ type: "export-all",
325
+ node
326
+ });
327
+ else if (node.type === "ExportDefaultDeclaration") {
328
+ let kind;
329
+ let localName;
330
+ let meta;
331
+ if ((node.declaration.type === "FunctionDeclaration" || node.declaration.type === "ClassDeclaration") && node.declaration.id) {
332
+ kind = "named-declaration";
333
+ localName = node.declaration.id.name;
334
+ meta = {
335
+ declName: node.declaration.id.name,
336
+ isFunction: getIsFunction(node.declaration),
337
+ valueNode: node.declaration
338
+ };
339
+ } else if (node.declaration.type === "Identifier") {
340
+ kind = "identifier";
341
+ meta = {
342
+ defaultExportIdentifierName: node.declaration.name,
343
+ valueNode: node.declaration
344
+ };
345
+ } else {
346
+ kind = "other";
347
+ meta = {
348
+ isFunction: getIsFunction(node.declaration),
349
+ valueNode: node.declaration
350
+ };
351
+ }
352
+ groups.push({
353
+ type: "default",
354
+ kind,
355
+ node,
356
+ localName,
357
+ meta
358
+ });
359
+ }
360
+ return groups;
361
+ }
362
+ function getIsFunction(node) {
363
+ if (node.type === "FunctionDeclaration" || node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression") return true;
364
+ if (node.type === "ClassDeclaration" || node.type === "Literal" || node.type === "ObjectExpression" || node.type === "ArrayExpression" || node.type === "ClassExpression") return false;
365
+ }
366
+ //#endregion
367
+ //#region src/transforms/module-export-effect.ts
368
+ /**
369
+ * Preserves source bindings and emits registration effects at module end.
370
+ *
371
+ * Conceptually:
372
+ *
373
+ * ```js
374
+ * export async function action() {}
375
+ * export default async () => {}
376
+ * ```
377
+ *
378
+ * becomes:
379
+ *
380
+ * ```js
381
+ * async function action() {}
382
+ * const $$effect_default = async () => {}
383
+ *
384
+ * __GENERATE__(action, 'action')
385
+ * export { action }
386
+ * __GENERATE__($$effect_default, 'default')
387
+ * export default $$effect_default
388
+ * ```
389
+ *
390
+ * Here, `__GENERATE__(...)` represents the expression returned by the
391
+ * `generate` callback for each `{ binding, exportName, meta }` context.
392
+ * `references` returns those contexts in generation order, while
393
+ * `referenceNames` returns only their export names.
394
+ *
395
+ * Effects emitted through declaration and default-export rewrites remain mapped
396
+ * to their original export sites. Export-specifier effects are appended without
397
+ * explicit mappings and rely on adjacent-source fallback. React captures the
398
+ * `registerServerReference` caller as the reference source location.
399
+ *
400
+ * Generated bindings such as `$$effect_default` and `$$effect_import_*` are not
401
+ * deconflicted from user bindings, consistent with the other transform helpers.
402
+ */
403
+ function transformModuleExportEffect(input, viteAst, options) {
404
+ const output = new MagicString(input);
405
+ const filter = options.filter ?? (() => true);
406
+ const references = [];
407
+ function generate(context) {
408
+ references.push(context);
409
+ return `${options.generate(context)};`;
410
+ }
411
+ function replaceAndMove(start, end, destination, code) {
412
+ output.update(start, end, `\n${code}\n`);
413
+ if (end !== destination) output.move(start, end, destination);
414
+ }
415
+ for (const group of scanModuleExports(viteAst)) if (group.type === "declaration") {
416
+ const { localName: binding, exportName, meta } = group.export;
417
+ if (!filter(exportName, meta)) continue;
418
+ validateNonAsyncFunction(options, group.declaration);
419
+ replaceAndMove(group.node.start, group.declaration.start, input.length, `${generate({
420
+ binding,
421
+ exportName,
422
+ meta
423
+ })}\nexport { ${binding} };`);
424
+ } else if (group.type === "variable-declaration") {
425
+ const exportNames = [];
426
+ const effects = [];
427
+ for (const declarator of group.declarators) {
428
+ let validate = false;
429
+ for (const entry of declarator.exports) {
430
+ const { localName: binding, exportName, meta } = entry;
431
+ exportNames.push(exportName);
432
+ if (filter(exportName, meta)) {
433
+ validate = true;
434
+ effects.push(generate({
435
+ binding,
436
+ exportName,
437
+ meta
438
+ }));
439
+ }
440
+ }
441
+ if (validate) {
442
+ const init = declarator.node.init;
443
+ if (init) validateNonAsyncFunction(options, init);
444
+ else rejectNonAsyncFunction(options, declarator.node.start);
445
+ }
446
+ }
447
+ if (effects.length > 0) replaceAndMove(group.node.start, group.declaration.start, input.length, `${effects.join("\n")}\nexport { ${exportNames.join(", ")} };`);
448
+ } else if (group.type === "specifiers") for (const entry of group.exports) {
449
+ tinyassert(entry.node.local.type === "Identifier");
450
+ if (entry.node.exported.type !== "Identifier") throw Object.assign(/* @__PURE__ */ new Error("unsupported string literal export name"), { pos: entry.node.exported.start });
451
+ const { exportName, meta } = entry;
452
+ if (!filter(exportName, meta)) continue;
453
+ let binding = entry.localName;
454
+ if (group.node.source) {
455
+ binding = `$$effect_import_${exportName}`;
456
+ output.append(`\nimport { ${entry.localName} as ${binding} } from ${group.node.source.raw};`);
457
+ }
458
+ output.append(`\n${generate({
459
+ binding,
460
+ exportName,
461
+ meta
462
+ })}`);
463
+ }
464
+ else if (group.type === "export-all") {
465
+ if (options.exportAll !== "preserve") throw Object.assign(/* @__PURE__ */ new Error("unsupported ExportAllDeclaration"), { pos: group.node.start });
466
+ } else if (group.type === "default") {
467
+ const node = group.node;
468
+ const binding = group.localName ?? "$$effect_default";
469
+ const meta = group.meta;
470
+ if (!filter("default", meta)) continue;
471
+ validateNonAsyncFunction(options, node.declaration);
472
+ const effect = generate({
473
+ binding,
474
+ exportName: "default",
475
+ meta
476
+ });
477
+ if (group.kind === "identifier") {
478
+ const declaration = node.declaration;
479
+ const exportTokenEnd = node.start + 6;
480
+ output.update(exportTokenEnd, node.end, `const ${binding} = ${declaration.name};`);
481
+ replaceAndMove(node.start, exportTokenEnd, input.length, `${effect}\nexport default ${binding};`);
482
+ } else if (group.kind === "named-declaration") replaceAndMove(node.start, node.declaration.start, input.length, `${effect}\nexport default ${binding};`);
483
+ else {
484
+ const exportTokenEnd = node.start + 6;
485
+ output.update(exportTokenEnd, node.declaration.start, `const ${binding} = `);
486
+ replaceAndMove(node.start, exportTokenEnd, input.length, `${effect}\nexport default ${binding};`);
487
+ }
488
+ }
489
+ return {
490
+ output,
491
+ references,
492
+ referenceNames: references.map((reference) => reference.exportName)
493
+ };
494
+ }
495
+ //#endregion
212
496
  //#region src/transforms/wrap-export.ts
213
- function transformWrapExport(input, ast, options) {
497
+ /**
498
+ * Replaces selected module-local export bindings with runtime wrappers.
499
+ *
500
+ * Conceptually:
501
+ *
502
+ * ```js
503
+ * export async function action() {}
504
+ * export const loader = async () => {}
505
+ * export default function Page() {}
506
+ * ```
507
+ *
508
+ * becomes:
509
+ *
510
+ * ```js
511
+ * async function action() {}
512
+ * let loader = async () => {}
513
+ * function Page() {}
514
+ *
515
+ * action = __WRAP__(action, 'action')
516
+ * export { action }
517
+ * loader = __WRAP__(loader, 'loader')
518
+ * export { loader }
519
+ * const $$wrap_Page = __WRAP__(Page, 'default')
520
+ * export { $$wrap_Page as default }
521
+ * ```
522
+ *
523
+ * Here, `__WRAP__(...)` represents the expression returned by `runtime`.
524
+ * Unlike `transformModuleExportWrap`, direct local references observe the
525
+ * wrapped value because the original binding itself is reassigned.
526
+ *
527
+ * Declaration rewrites are moved to the end of the module so each generated
528
+ * runtime call retains the original export token's source mapping. Wrappers for
529
+ * export specifiers and default exports are appended without explicit mappings.
530
+ *
531
+ * Generated `$$wrap_*` and `$$import_*` names are not deconflicted from user
532
+ * bindings, consistent with the other transform helpers.
533
+ */
534
+ function transformWrapExport(input, viteAst, options) {
214
535
  const output = new MagicString(input);
215
536
  const exportNames = [];
216
- const toAppend = [];
537
+ const appendedCode = [];
217
538
  const filter = options.filter ?? (() => true);
218
- function wrapSimple(start, end, exports) {
219
- const filteredExports = exports.map((item) => ({
220
- ...item,
221
- shouldWrap: filter(item.name, item.meta)
222
- }));
223
- exportNames.push(...filteredExports.filter((item) => item.shouldWrap).map((item) => item.name));
224
- const newCode = filteredExports.map((e) => [e.shouldWrap && `${e.name} = /* #__PURE__ */ ${options.runtime(e.name, e.name, e.meta)};\n`, `export { ${e.name} };\n`]).flat().filter(Boolean).join("");
539
+ /**
540
+ * Strips a direct export declaration and emits assignments and exports at
541
+ * module end. All bindings are re-exported, but only selected bindings are
542
+ * assigned runtime wrappers.
543
+ *
544
+ * The rewritten source range is moved so generated runtime calls borrow the
545
+ * original export site's mapping:
546
+ *
547
+ * ```js
548
+ * // input
549
+ * export async function action() {}
550
+ * ^^^^^^
551
+ *
552
+ * // output
553
+ * async function action() {}
554
+ * action = __WRAP__(action, 'action') << maps to the `export` token
555
+ * export { action } <<
556
+ * ```
557
+ */
558
+ function emitWrappedAssignments(start, end, exports, selectedExportNames) {
559
+ exportNames.push(...selectedExportNames);
560
+ const newCode = exports.map((e) => selectedExportNames.has(e.exportName) && `${e.localName} = /* #__PURE__ */ ${options.runtime(e.localName, e.exportName, e.meta)};\n`).filter(Boolean).join("") + `export { ${exports.map((e) => e.localName).join(", ")} };\n`;
225
561
  output.update(start, end, newCode);
226
562
  output.move(start, end, input.length);
227
563
  }
228
- function wrapExport(name, exportName, meta = {}) {
229
- if (!filter(exportName, meta)) {
230
- toAppend.push(`export { ${name} as ${exportName} }`);
231
- return;
232
- }
564
+ /**
565
+ * Emits a separate wrapper binding for exports that cannot reassign an
566
+ * existing direct declaration, such as export specifiers and defaults.
567
+ *
568
+ * ```js
569
+ * // existing source binding remains unchanged
570
+ * const local = init()
571
+ * export { local as renamed } // caller removes the original export
572
+ *
573
+ * // appended code
574
+ * const $$wrap_local = __WRAP__(local, 'renamed')
575
+ * export { $$wrap_local as renamed }
576
+ * ```
577
+ */
578
+ function emitWrappedBinding(name, exportName, meta = {}) {
233
579
  exportNames.push(exportName);
234
- toAppend.push(`const $$wrap_${name} = /* #__PURE__ */ ${options.runtime(name, exportName, meta)}`, `export { $$wrap_${name} as ${exportName} }`);
580
+ appendedCode.push(`const $$wrap_${name} = /* #__PURE__ */ ${options.runtime(name, exportName, meta)}`, `export { $$wrap_${name} as ${exportName} }`);
235
581
  }
236
- for (const node of ast.body) {
237
- if (node.type === "ExportNamedDeclaration") if (node.declaration) if (node.declaration.type === "FunctionDeclaration" || node.declaration.type === "ClassDeclaration") {
238
- /**
239
- * export function foo() {}
240
- */
241
- const name = node.declaration.id.name;
242
- const meta = {
243
- isFunction: getIsFunction(node.declaration),
244
- declName: name
245
- };
246
- if (filter(name, meta)) validateNonAsyncFunction(options, node.declaration);
247
- wrapSimple(node.start, node.declaration.start, [{
248
- name,
249
- meta
250
- }]);
251
- } else if (node.declaration.type === "VariableDeclaration") {
252
- /**
253
- * export const foo = 1, bar = 2
254
- */
255
- if (node.declaration.kind === "const") output.update(node.declaration.start, node.declaration.start + 5, "let");
256
- const exports = [];
257
- for (const decl of node.declaration.declarations) {
258
- const isFunction = decl.id.type === "Identifier" && decl.init ? getIsFunction(decl.init) : void 0;
259
- const declarationExports = extractNames(decl.id).map((name) => ({
260
- name,
261
- meta: {
262
- isFunction,
263
- declName: name
264
- }
265
- }));
266
- exports.push(...declarationExports);
267
- if (decl.init && declarationExports.some(({ name, meta }) => filter(name, meta))) validateNonAsyncFunction(options, decl.init);
268
- }
269
- wrapSimple(node.start, node.declaration.start, exports);
270
- } else node.declaration;
271
- else if (node.source) {
272
- /**
273
- * export { foo, bar as car } from './foo'
274
- */
275
- output.remove(node.start, node.end);
276
- for (const spec of node.specifiers) {
277
- tinyassert(spec.local.type === "Identifier");
278
- if (spec.exported.type !== "Identifier") throw Object.assign(/* @__PURE__ */ new Error("unsupported string literal export name"), { pos: spec.exported.start });
279
- const name = spec.local.name;
280
- toAppend.push(`import { ${name} as $$import_${name} } from ${node.source.raw}`);
281
- wrapExport(`$$import_${name}`, spec.exported.name);
582
+ for (const group of scanModuleExports(viteAst)) if (group.type === "declaration") {
583
+ const entry = group.export;
584
+ if (!filter(entry.exportName, entry.meta)) continue;
585
+ validateNonAsyncFunction(options, group.declaration);
586
+ emitWrappedAssignments(group.node.start, group.declaration.start, [entry], /* @__PURE__ */ new Set([entry.exportName]));
587
+ } else if (group.type === "variable-declaration") {
588
+ const exports = group.declarators.flatMap((item) => item.exports);
589
+ const selectedExportNames = new Set(exports.filter((entry) => filter(entry.exportName, entry.meta)).map((entry) => entry.exportName));
590
+ if (selectedExportNames.size === 0) continue;
591
+ if (group.declaration.kind === "const") output.update(group.declaration.start, group.declaration.start + 5, "let");
592
+ for (const declarator of group.declarators) if (declarator.exports.some(({ exportName }) => selectedExportNames.has(exportName))) if (declarator.node.init) validateNonAsyncFunction(options, declarator.node.init);
593
+ else rejectNonAsyncFunction(options, declarator.node.start);
594
+ emitWrappedAssignments(group.node.start, group.declaration.start, exports, selectedExportNames);
595
+ } else if (group.type === "specifiers") {
596
+ const skippedExports = [];
597
+ let selected = false;
598
+ for (const entry of group.exports) {
599
+ tinyassert(entry.node.local.type === "Identifier");
600
+ if (entry.node.exported.type !== "Identifier") throw Object.assign(/* @__PURE__ */ new Error("unsupported string literal export name"), { pos: entry.node.exported.start });
601
+ if (!filter(entry.exportName, entry.meta)) {
602
+ skippedExports.push(entry.localName === entry.exportName ? entry.localName : `${entry.localName} as ${entry.exportName}`);
603
+ continue;
282
604
  }
283
- } else {
284
- /**
285
- * export { foo, bar as car }
286
- */
287
- output.remove(node.start, node.end);
288
- for (const spec of node.specifiers) {
289
- tinyassert(spec.local.type === "Identifier");
290
- if (spec.exported.type !== "Identifier") throw Object.assign(/* @__PURE__ */ new Error("unsupported string literal export name"), { pos: spec.exported.start });
291
- wrapExport(spec.local.name, spec.exported.name);
605
+ selected = true;
606
+ let binding = entry.localName;
607
+ const source = group.node.source;
608
+ if (source) {
609
+ binding = `$$import_${entry.localName}`;
610
+ appendedCode.push(`import { ${entry.localName} as ${binding} } from ${source.raw}`);
292
611
  }
612
+ emitWrappedBinding(binding, entry.exportName, entry.meta);
293
613
  }
294
- /**
295
- * export * as ns from './foo'
296
- * export * from './foo'
297
- */
298
- if (node.type === "ExportAllDeclaration") {
299
- if (!options.ignoreExportAllDeclaration) throw Object.assign(/* @__PURE__ */ new Error("unsupported ExportAllDeclaration"), { pos: node.start });
300
- }
301
- /**
302
- * export default function foo() {}
303
- * export default class Foo {}
304
- * export default () => {}
305
- */
306
- if (node.type === "ExportDefaultDeclaration") {
307
- let localName;
308
- let isFunction;
309
- let declName;
310
- let defaultExportIdentifierName;
311
- if ((node.declaration.type === "FunctionDeclaration" || node.declaration.type === "ClassDeclaration") && node.declaration.id) {
312
- localName = node.declaration.id.name;
313
- output.remove(node.start, node.declaration.start);
314
- isFunction = getIsFunction(node.declaration);
315
- declName = node.declaration.id.name;
316
- } else {
317
- localName = "$$default";
318
- output.update(node.start, node.declaration.start, "const $$default = ");
319
- if (node.declaration.type === "Identifier") defaultExportIdentifierName = node.declaration.name;
320
- else isFunction = getIsFunction(node.declaration);
614
+ if (selected) {
615
+ output.remove(group.node.start, group.node.end);
616
+ if (skippedExports.length > 0) {
617
+ const source = group.node.source ? ` from ${group.node.source.raw}` : "";
618
+ appendedCode.push(`export { ${skippedExports.join(", ")} }${source}`);
321
619
  }
322
- const defaultMeta = {
323
- isFunction,
324
- declName,
325
- defaultExportIdentifierName
326
- };
327
- if (filter("default", defaultMeta)) validateNonAsyncFunction(options, node.declaration);
328
- wrapExport(localName, "default", defaultMeta);
329
620
  }
621
+ } else if (group.type === "export-all") {
622
+ if (!options.ignoreExportAllDeclaration) throw Object.assign(/* @__PURE__ */ new Error("unsupported ExportAllDeclaration"), { pos: group.node.start });
623
+ } else if (group.type === "default") {
624
+ const meta = group.meta;
625
+ if (!filter("default", meta)) continue;
626
+ const localName = group.localName ?? "$$default";
627
+ if (group.kind === "named-declaration") output.remove(group.node.start, group.node.declaration.start);
628
+ else output.update(group.node.start, group.node.declaration.start, "const $$default = ");
629
+ validateNonAsyncFunction(options, group.node.declaration);
630
+ emitWrappedBinding(localName, "default", meta);
330
631
  }
331
- if (toAppend.length > 0) output.append([
632
+ if (appendedCode.length > 0) output.append([
332
633
  "",
333
- ...toAppend,
634
+ ...appendedCode,
334
635
  ""
335
636
  ].join(";\n"));
336
637
  return {
@@ -338,90 +639,103 @@ function transformWrapExport(input, ast, options) {
338
639
  output
339
640
  };
340
641
  }
341
- function getIsFunction(node) {
342
- if (node.type === "FunctionDeclaration" || node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression") return true;
343
- if (node.type === "ClassDeclaration" || node.type === "Literal" || node.type === "ObjectExpression" || node.type === "ArrayExpression" || node.type === "ClassExpression") return false;
344
- }
345
642
  //#endregion
346
643
  //#region src/transforms/proxy-export.ts
347
644
  function transformDirectiveProxyExport(ast, options) {
348
645
  if (!hasDirective(ast.body, options.directive)) return;
349
646
  return transformProxyExport(ast, options);
350
647
  }
351
- function transformProxyExport(ast, options) {
648
+ /**
649
+ * Replaces selected exports with proxies created by `runtime` and removes the
650
+ * original module implementation.
651
+ *
652
+ * Conceptually, with a filter that excludes `objectValue`:
653
+ *
654
+ * ```js
655
+ * import { dependency } from './dep'
656
+ * export async function action() {}
657
+ * export const objectValue = {}
658
+ * export { dependency as renamed }
659
+ * export default async () => {}
660
+ * ```
661
+ *
662
+ * becomes:
663
+ *
664
+ * ```js
665
+ * export const action = __PROXY__('action')
666
+ * export const renamed = __PROXY__('renamed')
667
+ * export default __PROXY__('default')
668
+ * ```
669
+ *
670
+ * Unlike `transformWrapExport`, this transform does not evaluate the original
671
+ * exports. Unknown values are represented only by their export names, so the
672
+ * caller must filter solely from the available static `ModuleExportMeta`.
673
+ * `keep` is a specialized mode that retains non-export implementation code and
674
+ * passes a single variable initializer to `runtime`.
675
+ */
676
+ function transformProxyExport(viteAst, options) {
677
+ const ast = viteAst;
352
678
  if (options.keep && typeof options.code !== "string") throw new Error("`keep` option requires `code`");
679
+ if (options.keep && options.filter) throw new Error("`filter` option is not supported with `keep`");
353
680
  const output = new MagicString(options.code ?? " ".repeat(ast.end));
354
681
  const exportNames = [];
682
+ const filter = options.filter ?? (() => true);
683
+ function removeNode(node) {
684
+ output.remove(node.start, node.end);
685
+ }
686
+ /** Replaces one complete export statement with the selected proxy exports. */
355
687
  function createExport(node, names) {
356
688
  exportNames.push(...names);
357
689
  const newCode = names.map((name) => (name === "default" ? `export default` : `export const ${name} =`) + ` /* #__PURE__ */ ${options.runtime(name)};\n`).join("");
358
690
  output.update(node.start, node.end, newCode);
359
691
  }
360
- for (const node of ast.body) {
361
- if (node.type === "ExportNamedDeclaration") {
362
- if (node.declaration) if (node.declaration.type === "FunctionDeclaration" || node.declaration.type === "ClassDeclaration") {
363
- /**
364
- * export function foo() {}
365
- */
366
- validateNonAsyncFunction(options, node.declaration);
367
- createExport(node, [node.declaration.id.name]);
368
- } else if (node.declaration.type === "VariableDeclaration") {
369
- /**
370
- * export const foo = 1, bar = 2
371
- */
372
- for (const decl of node.declaration.declarations) if (decl.init) validateNonAsyncFunction(options, decl.init);
373
- if (options.keep && options.code) {
374
- if (node.declaration.declarations.length === 1) {
375
- const decl = node.declaration.declarations[0];
376
- if (decl.id.type === "Identifier" && decl.init) {
377
- const name = decl.id.name;
378
- const value = options.code.slice(decl.init.start, decl.init.end);
379
- const newCode = `export const ${name} = /* #__PURE__ */ ${options.runtime(name, { value })};`;
380
- output.update(node.start, node.end, newCode);
381
- exportNames.push(name);
382
- continue;
383
- }
384
- }
385
- }
386
- createExport(node, node.declaration.declarations.flatMap((decl) => extractNames(decl.id)));
387
- } else node.declaration;
388
- else {
389
- /**
390
- * export { foo, bar as car } from './foo'
391
- * export { foo, bar as car }
392
- */
393
- const names = [];
394
- for (const spec of node.specifiers) {
395
- if (spec.exported.type !== "Identifier") throw Object.assign(/* @__PURE__ */ new Error("unsupported string literal export name"), { pos: spec.exported.start });
396
- names.push(spec.exported.name);
397
- }
398
- createExport(node, names);
692
+ const exportNodes = /* @__PURE__ */ new Set();
693
+ for (const group of scanModuleExports(viteAst)) {
694
+ const node = group.node;
695
+ exportNodes.add(node);
696
+ if (group.type === "declaration") {
697
+ const entry = group.export;
698
+ if (filter(entry.exportName, entry.meta)) {
699
+ validateNonAsyncFunction(options, group.declaration);
700
+ createExport(node, [entry.exportName]);
701
+ } else removeNode(node);
702
+ } else if (group.type === "variable-declaration") {
703
+ const selectedNames = [];
704
+ for (const declarator of group.declarators) {
705
+ const names = declarator.exports.filter((entry) => filter(entry.exportName, entry.meta)).map((entry) => entry.exportName);
706
+ if (declarator.node.init && (!options.filter || declarator.exports.length === 0 || names.length > 0)) validateNonAsyncFunction(options, declarator.node.init);
707
+ selectedNames.push(...names);
399
708
  }
400
- continue;
401
- }
402
- /**
403
- * export * as ns from './foo'
404
- * export * from './foo'
405
- */
406
- if (node.type === "ExportAllDeclaration") {
407
- if (node.exported?.type === "Identifier") {
408
- createExport(node, [node.exported.name]);
409
- continue;
709
+ if (options.keep && group.declaration.declarations.length === 1) {
710
+ const decl = group.declaration.declarations[0];
711
+ if (decl.id.type === "Identifier" && decl.init) {
712
+ const name = decl.id.name;
713
+ const value = options.code.slice(decl.init.start, decl.init.end);
714
+ const newCode = `export const ${name} = /* #__PURE__ */ ${options.runtime(name, { value })};`;
715
+ output.update(node.start, node.end, newCode);
716
+ exportNames.push(name);
717
+ continue;
718
+ }
410
719
  }
411
- if (!options.ignoreExportAllDeclaration) throw new Error("unsupported ExportAllDeclaration");
412
- }
413
- /**
414
- * export default function foo() {}
415
- * export default class Foo {}
416
- * export default () => {}
417
- */
418
- if (node.type === "ExportDefaultDeclaration") {
419
- validateNonAsyncFunction(options, node.declaration);
720
+ createExport(node, selectedNames);
721
+ } else if (group.type === "specifiers") createExport(node, group.exports.filter((entry) => {
722
+ if (entry.node.exported.type !== "Identifier") throw Object.assign(/* @__PURE__ */ new Error("unsupported string literal export name"), { pos: entry.node.exported.start });
723
+ return filter(entry.exportName, entry.meta);
724
+ }).map((entry) => entry.exportName));
725
+ else if (group.type === "export-all") {
726
+ if (group.node.exported?.type === "Identifier") {
727
+ const name = group.node.exported.name;
728
+ if (filter(name, {})) createExport(node, [name]);
729
+ else removeNode(node);
730
+ } else if (!options.ignoreExportAllDeclaration) throw new Error("unsupported ExportAllDeclaration");
731
+ else if (!options.keep) removeNode(node);
732
+ } else if (group.type === "default") if (filter("default", group.meta)) {
733
+ validateNonAsyncFunction(options, group.node.declaration);
420
734
  createExport(node, ["default"]);
421
- continue;
422
- }
423
- if (options.keep) continue;
424
- output.remove(node.start, node.end);
735
+ } else removeNode(node);
736
+ }
737
+ if (!options.keep) {
738
+ for (const node of ast.body) if (!exportNodes.has(node)) removeNode(node);
425
739
  }
426
740
  return {
427
741
  exportNames,
@@ -432,10 +746,13 @@ function transformProxyExport(ast, options) {
432
746
  //#region src/transforms/server-action.ts
433
747
  function transformServerActionServer(input, ast, options) {
434
748
  if (hasDirective(ast.body, "use server")) {
435
- const result = transformWrapExport(input, ast, options);
749
+ const result = transformModuleExportEffect(input, ast, {
750
+ rejectNonAsyncFunction: options.rejectNonAsyncFunction,
751
+ generate: ({ binding, exportName }) => options.runtime(binding, exportName)
752
+ });
436
753
  return {
437
754
  ...result,
438
- referenceNames: result.exportNames
755
+ exportNames: result.referenceNames
439
756
  };
440
757
  }
441
758
  const result = transformHoistInlineDirective(input, ast, {
@@ -450,7 +767,9 @@ function transformServerActionServer(input, ast, options) {
450
767
  //#endregion
451
768
  //#region src/transforms/expand-export-all.ts
452
769
  async function transformExpandExportAll(options) {
453
- const { plans } = resolveStarExports(await scanModuleExports(options.ast, options.importer, options));
770
+ const ast = options.ast;
771
+ const context = options;
772
+ const { plans } = resolveStarExports(await scanModuleExports$1(ast, options.importer, context));
454
773
  if (plans.length === 0) return;
455
774
  const output = new MagicString(options.code);
456
775
  for (const item of plans) {
@@ -459,7 +778,7 @@ async function transformExpandExportAll(options) {
459
778
  }
460
779
  return { code: output.toString() };
461
780
  }
462
- async function scanModuleExports(ast, importer, context, seen = /* @__PURE__ */ new Set()) {
781
+ async function scanModuleExports$1(ast, importer, context, seen = /* @__PURE__ */ new Set()) {
463
782
  const starSources = [];
464
783
  const bareStars = ast.body.filter((n) => n.type === "ExportAllDeclaration" && !n.exported);
465
784
  for (const node of bareStars) {
@@ -482,7 +801,7 @@ async function collectExportScan(resolvedId, context, seen) {
482
801
  ambiguousNames: /* @__PURE__ */ new Set()
483
802
  };
484
803
  seen.add(resolvedId);
485
- const scan = await scanModuleExports(await context.load(resolvedId), resolvedId, context, seen);
804
+ const scan = await scanModuleExports$1(await context.load(resolvedId), resolvedId, context, seen);
486
805
  const resolved = resolveStarExports(scan);
487
806
  return {
488
807
  names: [...scan.explicitNames, ...resolved.plans.flatMap((item) => item.names)],
@@ -526,4 +845,4 @@ function resolveStarExports(scan) {
526
845
  };
527
846
  }
528
847
  //#endregion
529
- export { extractIdentifiers, extractNames, findDirectives, hasDirective, transformDirectiveProxyExport, transformExpandExportAll, transformHoistInlineDirective, transformProxyExport, transformServerActionServer, transformWrapExport, validateNonAsyncFunction };
848
+ export { extractIdentifiers, extractNames, findDirectives, hasDirective, isDirective, rejectNonAsyncFunction, scanModuleExports, transformDirectiveProxyExport, transformExpandExportAll, transformHoistInlineDirective, transformModuleExportEffect, transformProxyExport, transformServerActionServer, transformWrapExport, validateNonAsyncFunction };