fix(rsc): handle conflict names from export * in use client and use server modules#1239
Conversation
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
export * handling with use client and use serverexport * re-export handling with use client and use server modules
Co-authored-by: Codex <noreply@openai.com>
export * re-export handling with use client and use server modulesexport * in use client and use server modules
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
There was a problem hiding this comment.
Pull request overview
This PR improves @vitejs/plugin-rsc’s handling of export * from ... inside "use client" and "use server" modules by introducing a pre-transform that expands bare star re-exports into explicit named re-exports, including logic to avoid re-exporting ambiguous/conflicting names (per ESM rules). It also adds/updates tests and fixtures around export scanning/expansion and tightens behavior for unsupported string-literal export names.
Changes:
- Add
transformExpandExportAllto recursively scan dependencies and rewrite bareexport * frominto explicitexport { ... } frombefore proxy/wrap transforms run. - Update proxy/wrap transforms to throw a clearer error for string-literal export names.
- Add unit fixtures/tests and an e2e route/test covering
"use server"+export *server actions.
Reviewed changes
Copilot reviewed 76 out of 76 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/plugin-rsc/src/transforms/wrap-export.ts | Throw clearer error for string-literal export names; simplify ExportAllDeclaration handling. |
| packages/plugin-rsc/src/transforms/wrap-export.test.ts | Add test for string-literal export-name error; adjust export * as snapshot expectation. |
| packages/plugin-rsc/src/transforms/utils.ts | Remove getExportNames helper (no longer used). |
| packages/plugin-rsc/src/transforms/proxy-export.ts | Throw clearer error for string-literal export names. |
| packages/plugin-rsc/src/transforms/proxy-export.test.ts | Add test for string-literal export-name error. |
| packages/plugin-rsc/src/transforms/index.ts | Re-export new expand-export-all transform from transforms entrypoint. |
| packages/plugin-rsc/src/transforms/expand-export-all.ts | New recursive scanner/rewriter to expand bare export * into explicit named re-exports with conflict/ambiguity handling. |
| packages/plugin-rsc/src/transforms/expand-export-all.test.ts | Fixture-driven tests validating transformExpandExportAll rewrites and error cases. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/string-export-name/entry.js.snap.js | Snapshot for string-literal export-name error case. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/string-export-name/entry.js | Fixture entry for string-literal export-name error case. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/string-export-name/dep.js | Fixture dependency exporting a string-literal export name. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/no-export-star/entry.js.snap.js | Snapshot for “no bare export-star to expand” case. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/no-export-star/entry.js | Fixture with export * as ns (should not be rewritten). |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/no-export-star/dep.js | Fixture dep for “no-export-star” case. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-filtered-conflict/repro2.js | Extra repro script for conflict behavior notes. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-filtered-conflict/repro1.js | Extra repro script for Node/V8 behavior notes. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-filtered-conflict/entry.js.snap.js | Snapshot for nested filtered conflict expansion. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-filtered-conflict/entry.js | Fixture entry re-exporting from two deps. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-filtered-conflict/dep2.js | Fixture dep with nested export * chain. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-filtered-conflict/dep2-2.js | Fixture leaf dep for nested chain. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-filtered-conflict/dep2-1.js | Fixture leaf dep for nested chain. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-filtered-conflict/dep1.js | Fixture dep for nested conflict case. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-conflict-star/entry.js.snap.js | Snapshot for nested conflict-star expansion. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-conflict-star/entry.js | Fixture entry with export * to dep that re-exports conflicting names. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-conflict-star/dep.js | Fixture dep that star-exports from two modules. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-conflict-star/b.js | Fixture leaf module. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-conflict-star/a.js | Fixture leaf module. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-conflict-explicit-reexport-wins/override.js | Fixture override module for explicit re-export precedence. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-conflict-explicit-reexport-wins/middle.js | Fixture middle module combining star + explicit re-export. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-conflict-explicit-reexport-wins/entry.js.snap.js | Snapshot for explicit re-export precedence through nesting. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-conflict-explicit-reexport-wins/entry.js | Fixture entry for precedence case. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-conflict-explicit-reexport-wins/dep.js | Fixture dep with star re-exports. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-conflict-explicit-reexport-wins/b.js | Fixture leaf module. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/nested-conflict-explicit-reexport-wins/a.js | Fixture leaf module. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/namespace-same-binding/entry.js.snap.js | Snapshot capturing current limitation for same-binding namespace duplication. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/namespace-same-binding/entry.js | Fixture entry for same-binding namespace duplication. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/namespace-same-binding/dep2.js | Fixture dep exporting namespace from same target. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/namespace-same-binding/dep1.js | Fixture dep exporting namespace from same target. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/namespace-same-binding/dep-target.js | Shared target module for namespace exports. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/explicit-wins/entry.js.snap.js | Snapshot for explicit local export shadowing star export. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/explicit-wins/entry.js | Fixture entry for explicit-shadowing case. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/explicit-wins/dep.js | Fixture dep for explicit-shadowing case. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/duplicate-star-same-source/entry.js.snap.js | Snapshot capturing current limitation for duplicate star exports from same source. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/duplicate-star-same-source/entry.js | Fixture entry repeating the same export * from multiple times. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/duplicate-star-same-source/dep.js | Fixture dep for duplicate-star case. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/default-only/entry.js.snap.js | Snapshot for star re-export of default-only module (results in empty named re-export). |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/default-only/entry.js | Fixture entry for default-only case. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/default-only/dep.js | Fixture dep exporting only default (plus side effects). |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/conflict-star/entry.js.snap.js | Snapshot for conflicting names across multiple star sources. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/conflict-star/entry.js | Fixture entry for conflict-star case. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/conflict-star/b.js | Fixture leaf module. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/conflict-star/a.js | Fixture leaf module. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/circular/entry.js.snap.js | Snapshot for circular star export graph case. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/circular/entry.js | Fixture entry for circular case. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/circular/dep2.js | Fixture dep in circular chain. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/circular/dep1.js | Fixture dep in circular chain. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/circular-same-binding/entry.js.snap.js | Snapshot capturing current limitation for cyclic same-binding case. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/circular-same-binding/entry.js | Fixture entry for cyclic same-binding case. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/circular-same-binding/b.js | Fixture dep in cyclic case. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/circular-same-binding/a.js | Fixture dep in cyclic case. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/basic/nested.js | Fixture nested module exporting a class. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/basic/entry.js.snap.js | Snapshot for basic expansion (incl. namespace + nested star). |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/basic/entry.js | Fixture entry for basic expansion. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/basic/dep.js | Fixture dep combining explicit exports, default re-export, star, and namespace export. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/basic/default.js | Fixture default-export module. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/bad-resolve/entry.js.snap.js | Snapshot for “failed to resolve” error. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/bad-resolve/entry.js | Fixture entry for “bad resolve” case. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/bad-load/entry.js.snap.js | Snapshot for “failed to load” error. |
| packages/plugin-rsc/src/transforms/fixtures/expand-export-all/bad-load/entry.js | Fixture entry for “bad load” case. |
| packages/plugin-rsc/src/plugin.ts | Use transformExpandExportAll in both use client and use server plugin transforms. |
| packages/plugin-rsc/examples/basic/src/routes/root.tsx | Wire new example route into the basic example app. |
| packages/plugin-rsc/examples/basic/src/routes/action-export-all/server.tsx | New server component exercising use server + export * server actions. |
| packages/plugin-rsc/examples/basic/src/routes/action-export-all/client.tsx | New client component invoking server action through expanded re-export. |
| packages/plugin-rsc/examples/basic/src/routes/action-export-all/actions.ts | use server module that re-exports actions via bare export *. |
| packages/plugin-rsc/examples/basic/src/routes/action-export-all/action-impl.ts | Action implementations to be re-exported and registered. |
| packages/plugin-rsc/e2e/basic.test.ts | Add e2e coverage for use server + export *; rename existing “export *” test label. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Codex <noreply@openai.com>
This comment was marked as outdated.
This comment was marked as outdated.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 76 out of 76 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
packages/plugin-rsc/src/transforms/wrap-export.ts:193
transformWrapExportno longer handlesexport * as ns from "..."(ExportAllDeclaration withexportedidentifier). This is still a valid ESM form and is already supported bytransformProxyExport; dropping support here will makeuse serverwrapping (transformServerActionServer->transformWrapExport) throwunsupported ExportAllDeclarationfor namespace re-exports.
packages/plugin-rsc/src/transforms/proxy-export.ts:118expand-export-allcan rewrite a star export intoexport {} from "..."to preserve side effects when no names are safely re-exportable.transformProxyExportcurrently treats anyExportNamedDeclarationwith specifiers as something to replace with proxy exports; when specifiers is empty it replaces the whole statement with an empty string, dropping the side-effect-only re-export.export {} fromshould be left intact.
| toAppend.push(`import * as ${localName} from ${node.source.raw}`) | ||
| wrapExport(localName, exportedName) | ||
| } else if (!options.ignoreExportAllDeclaration) { | ||
| if (!options.ignoreExportAllDeclaration) { |
There was a problem hiding this comment.
This was reverted since this change was irrelevant for original PR
Description
export *re-exports inuse clientanduse servermodules #1234This PR extracts
export *expansion into a dedicated transform with file fixtures based unti tests and tightens its handling of ESM star-export ambiguity before downstream proxy/wrap transforms run.It turned out
export *semantics have many edge cases and some cases are explicitly not handled yet but kept as test cases with TODOs, for example:export *entries that resolve to the same underlying binding instead of treating them as ambiguous.