@vitejs/plugin-rsc 0.5.32 → 0.5.33

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