From 601d3e402828c648af16e2090837dcad12ca63fb Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:23:14 +0900 Subject: [PATCH 1/5] refactor(rsc): expose export value nodes Co-authored-by: OpenCode --- .oxfmtrc.json | 1 + .../plugin-rsc/e2e/use-cache-callable.test.ts | 2 + .../callable-cache-plugin.ts | 5 ++ .../file-directive-from-server/action.ts | 5 ++ .../file-directive-from-server/client.tsx | 6 ++ .../file-directive-from-server/server.tsx | 3 +- .../source-map/wrap-export/classes.js | 7 +++ .../wrap-export/classes.js.map.snap.md | 34 ++++++++++ .../source-map/wrap-export/classes.js.snap.md | 63 +++++++++++++++++++ .../wrap-export/default-anonymous-class.js | 3 + .../default-anonymous-class.js.map.snap.md | 21 +++++++ .../default-anonymous-class.js.snap.md | 41 ++++++++++++ .../wrap-export/default-comments.js | 5 ++ .../default-comments.js.map.snap.md | 25 ++++++++ .../wrap-export/default-comments.js.snap.md | 47 ++++++++++++++ .../wrap-export/local-binding-preservation.js | 6 ++ .../local-binding-preservation.js.map.snap.md | 25 ++++++++ .../local-binding-preservation.js.snap.md | 54 ++++++++++++++++ .../wrap-export/multiple-aliases.js | 7 +++ .../multiple-aliases.js.map.snap.md | 25 ++++++++ .../wrap-export/multiple-aliases.js.snap.md | 55 ++++++++++++++++ .../source-map/wrap-export/recursive.js | 6 ++ .../wrap-export/recursive.js.map.snap.md | 24 +++++++ .../wrap-export/recursive.js.snap.md | 49 +++++++++++++++ .../transforms/module-export-effect.test.ts | 4 +- .../src/transforms/module-export-scan.test.ts | 9 ++- .../src/transforms/module-export-scan.ts | 23 ++++++- .../src/transforms/wrap-export.test.ts | 35 +++++++++-- .../plugin-rsc/src/transforms/wrap-export.ts | 9 +++ 29 files changed, 589 insertions(+), 10 deletions(-) create mode 100644 packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/classes.js create mode 100644 packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/classes.js.map.snap.md create mode 100644 packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/classes.js.snap.md create mode 100644 packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-anonymous-class.js create mode 100644 packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-anonymous-class.js.map.snap.md create mode 100644 packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-anonymous-class.js.snap.md create mode 100644 packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-comments.js create mode 100644 packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-comments.js.map.snap.md create mode 100644 packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-comments.js.snap.md create mode 100644 packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/local-binding-preservation.js create mode 100644 packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/local-binding-preservation.js.map.snap.md create mode 100644 packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/local-binding-preservation.js.snap.md create mode 100644 packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/multiple-aliases.js create mode 100644 packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/multiple-aliases.js.map.snap.md create mode 100644 packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/multiple-aliases.js.snap.md create mode 100644 packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/recursive.js create mode 100644 packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/recursive.js.map.snap.md create mode 100644 packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/recursive.js.snap.md diff --git a/.oxfmtrc.json b/.oxfmtrc.json index ec2b1779f..60b34effb 100644 --- a/.oxfmtrc.json +++ b/.oxfmtrc.json @@ -10,6 +10,7 @@ }, "ignorePatterns": [ "*.snap.*", + "packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-comments.js", "typescript-eslint/", "packages/*/CHANGELOG.md", "playground-temp/", diff --git a/packages/plugin-rsc/e2e/use-cache-callable.test.ts b/packages/plugin-rsc/e2e/use-cache-callable.test.ts index e30eb24a6..5a2ae9be7 100644 --- a/packages/plugin-rsc/e2e/use-cache-callable.test.ts +++ b/packages/plugin-rsc/e2e/use-cache-callable.test.ts @@ -97,11 +97,13 @@ function defineTests(f: Fixture) { const example = page.getByTestId('file-directive-from-server') const submissionCount = example.getByTestId('submission-count') const executionCount = example.getByTestId('execution-count') + const ordinaryExports = example.getByTestId('ordinary-exports') const result = example.getByTestId('result') const argument = example.getByRole('textbox', { name: 'Cache key' }) await page.getByRole('button', { name: 'Reset' }).click() await expect(submissionCount).toHaveText('0') await expect(executionCount).toHaveText('0') + await expect(ordinaryExports).toHaveText('cached metadata: cache') await expect(result).toHaveText('not called') // The wrapped export is passed from a Server Component to a Client Component. diff --git a/packages/plugin-rsc/examples/use-cache-callable/callable-cache-plugin.ts b/packages/plugin-rsc/examples/use-cache-callable/callable-cache-plugin.ts index f84717491..b2b6da65e 100644 --- a/packages/plugin-rsc/examples/use-cache-callable/callable-cache-plugin.ts +++ b/packages/plugin-rsc/examples/use-cache-callable/callable-cache-plugin.ts @@ -37,6 +37,11 @@ export function callableCachePlugin(): Plugin { const result = hasDirective(ast.body, directive) ? transformWrapExport(code, ast, { runtime, + // Next.js permits object and array exports for metadata and + // viewport values in "use cache" modules. + filter: (_name, meta) => + meta.valueNode?.type !== 'ObjectExpression' && + meta.valueNode?.type !== 'ArrayExpression', rejectNonAsyncFunction: true, }) : transformHoistInlineDirective(code, ast, { diff --git a/packages/plugin-rsc/examples/use-cache-callable/src/features/file-directive-from-server/action.ts b/packages/plugin-rsc/examples/use-cache-callable/src/features/file-directive-from-server/action.ts index c912d165f..2de9df9ca 100644 --- a/packages/plugin-rsc/examples/use-cache-callable/src/features/file-directive-from-server/action.ts +++ b/packages/plugin-rsc/examples/use-cache-callable/src/features/file-directive-from-server/action.ts @@ -2,6 +2,11 @@ import { state } from './state' +// Ordinary values remain available from "use cache" modules without becoming +// callable server references. +export const metadata = { title: 'cached metadata' } +export const tags = ['cache'] + export async function cachedFromServer(formData: FormData) { const argument = String(formData.get('argument')) state.executionCount++ diff --git a/packages/plugin-rsc/examples/use-cache-callable/src/features/file-directive-from-server/client.tsx b/packages/plugin-rsc/examples/use-cache-callable/src/features/file-directive-from-server/client.tsx index 9ffa05f25..ff58bf808 100644 --- a/packages/plugin-rsc/examples/use-cache-callable/src/features/file-directive-from-server/client.tsx +++ b/packages/plugin-rsc/examples/use-cache-callable/src/features/file-directive-from-server/client.tsx @@ -5,6 +5,7 @@ import { useState } from 'react' export function FileDirectiveFromServerClient(props: { action: (formData: FormData) => Promise executionCount: number + ordinaryExports: string resetAction: () => Promise result: string }) { @@ -35,6 +36,11 @@ export function FileDirectiveFromServerClient(props: { {props.executionCount}
+ Ordinary exports:{' '} + + {props.ordinaryExports} + +
Result: {props.result}

diff --git a/packages/plugin-rsc/examples/use-cache-callable/src/features/file-directive-from-server/server.tsx b/packages/plugin-rsc/examples/use-cache-callable/src/features/file-directive-from-server/server.tsx index e6c340114..3f09befef 100644 --- a/packages/plugin-rsc/examples/use-cache-callable/src/features/file-directive-from-server/server.tsx +++ b/packages/plugin-rsc/examples/use-cache-callable/src/features/file-directive-from-server/server.tsx @@ -1,4 +1,4 @@ -import { cachedFromServer } from './action' +import { cachedFromServer, metadata, tags } from './action' import { FileDirectiveFromServerClient } from './client' import { resetAction } from './reset' import { state } from './state' @@ -8,6 +8,7 @@ export function FileDirectiveFromServer() { diff --git a/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/classes.js b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/classes.js new file mode 100644 index 000000000..8f55ccbeb --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/classes.js @@ -0,0 +1,7 @@ +'use server' + +export class NamedClass {} + +export const ClassExpression = class InnerClass {} + +export default class DefaultClass {} diff --git a/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/classes.js.map.snap.md b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/classes.js.map.snap.md new file mode 100644 index 000000000..4667dfeec --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/classes.js.map.snap.md @@ -0,0 +1,34 @@ +## wrap-export + +```txt +(0:0) "'use server'\n" --> (0:0) "'use server'\n" +(2:7) "class NamedClass {}\n" --> (2:0) "class NamedClass {}\n" +(4:7) "const" --> (4:0) "let" +(4:12) " ClassExpression = class InnerClass {}\n" --> (4:3) " ClassExpression = class InnerClass {}\n" +(6:15) "class DefaultClass {}\n" --> (6:0) "class DefaultClass {}\n" +(2:0) "export class NamedClass {}\n" --> (7:0) "NamedClass = /* #__PURE__ */ registerServerReference(NamedClass, \"NamedClass\");\n" +(2:0) "export class NamedClass {}\n" --> (8:0) "export { NamedClass };\n" +(4:0) "export const ClassExpression = class InnerClass {}\n" --> (9:0) "ClassExpression = /* #__PURE__ */ registerServerReference(ClassExpression, \"ClassExpression\");\n" +(4:0) "export const ClassExpression = class InnerClass {}\n" --> (10:0) "export { ClassExpression };\n" +[unmapped] --> (11:0) ";\n" +[unmapped] --> (12:0) "const $$wrap_DefaultClass = /* #__PURE__ */ registerServerReference(DefaultClass, \"default\");\n" +[unmapped] --> (13:0) "export { $$wrap_DefaultClass as default };\n" +``` + +## module-export-effect + +```txt +(0:0) "'use server'\n" --> (0:0) "'use server'\n" +(2:7) "class NamedClass {}\n" --> (2:0) "class NamedClass {}\n" +(4:7) "const ClassExpression = class InnerClass {}\n" --> (4:0) "const ClassExpression = class InnerClass {}\n" +(6:15) "class DefaultClass {}\n" --> (6:0) "class DefaultClass {}\n" +(2:0) "export class NamedClass {}\n" --> (7:0) "\n" +(2:0) "export class NamedClass {}\n" --> (8:0) "registerServerReference(NamedClass, \"NamedClass\");\n" +(2:0) "export class NamedClass {}\n" --> (9:0) "export { NamedClass };\n" +(4:0) "export const ClassExpression = class InnerClass {}\n" --> (10:0) "\n" +(4:0) "export const ClassExpression = class InnerClass {}\n" --> (11:0) "registerServerReference(ClassExpression, \"ClassExpression\");\n" +(4:0) "export const ClassExpression = class InnerClass {}\n" --> (12:0) "export { ClassExpression };\n" +(6:0) "export default class DefaultClass {}\n" --> (13:0) "\n" +(6:0) "export default class DefaultClass {}\n" --> (14:0) "registerServerReference(DefaultClass, \"default\");\n" +(6:0) "export default class DefaultClass {}\n" --> (15:0) "export default DefaultClass;\n" +``` diff --git a/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/classes.js.snap.md b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/classes.js.snap.md new file mode 100644 index 000000000..358c7c1b2 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/classes.js.snap.md @@ -0,0 +1,63 @@ +## Input + +```js +'use server' + +export class NamedClass {} + +export const ClassExpression = class InnerClass {} + +export default class DefaultClass {} +``` + +## wrap-export + +**Status:** transformed + +**References:** NamedClass, ClassExpression, default + +[Source map visualization](https://evanw.github.io/source-map-visualization/#NDY1ACd1c2Ugc2VydmVyJwoKY2xhc3MgTmFtZWRDbGFzcyB7fQoKbGV0IENsYXNzRXhwcmVzc2lvbiA9IGNsYXNzIElubmVyQ2xhc3Mge30KCmNsYXNzIERlZmF1bHRDbGFzcyB7fQpOYW1lZENsYXNzID0gLyogI19fUFVSRV9fICovIHJlZ2lzdGVyU2VydmVyUmVmZXJlbmNlKE5hbWVkQ2xhc3MsICJOYW1lZENsYXNzIik7CmV4cG9ydCB7IE5hbWVkQ2xhc3MgfTsKQ2xhc3NFeHByZXNzaW9uID0gLyogI19fUFVSRV9fICovIHJlZ2lzdGVyU2VydmVyUmVmZXJlbmNlKENsYXNzRXhwcmVzc2lvbiwgIkNsYXNzRXhwcmVzc2lvbiIpOwpleHBvcnQgeyBDbGFzc0V4cHJlc3Npb24gfTsKOwpjb25zdCAkJHdyYXBfRGVmYXVsdENsYXNzID0gLyogI19fUFVSRV9fICovIHJlZ2lzdGVyU2VydmVyUmVmZXJlbmNlKERlZmF1bHRDbGFzcywgImRlZmF1bHQiKTsKZXhwb3J0IHsgJCR3cmFwX0RlZmF1bHRDbGFzcyBhcyBkZWZhdWx0IH07CjM4NwB7InZlcnNpb24iOjMsInNvdXJjZXMiOlsiIl0sInNvdXJjZXNDb250ZW50IjpbIid1c2Ugc2VydmVyJ1xuXG5leHBvcnQgY2xhc3MgTmFtZWRDbGFzcyB7fVxuXG5leHBvcnQgY29uc3QgQ2xhc3NFeHByZXNzaW9uID0gY2xhc3MgSW5uZXJDbGFzcyB7fVxuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBEZWZhdWx0Q2xhc3Mge31cbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxDQUFDLEdBQUcsQ0FBQyxNQUFNOztBQUVKLEtBQUssQ0FBQyxVQUFVLENBQUMsQ0FBQzs7QUFFbEIsR0FBSyxDQUFDLGVBQWUsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDOztBQUVsQyxLQUFLLENBQUMsWUFBWSxDQUFDLENBQUM7QUFKbkM7QUFBQTtBQUVBO0FBQUE7Ozs7In0=) + +```js +'use server' + +class NamedClass {} + +let ClassExpression = class InnerClass {} + +class DefaultClass {} +NamedClass = /* #__PURE__ */ registerServerReference(NamedClass, "NamedClass"); +export { NamedClass }; +ClassExpression = /* #__PURE__ */ registerServerReference(ClassExpression, "ClassExpression"); +export { ClassExpression }; +; +const $$wrap_DefaultClass = /* #__PURE__ */ registerServerReference(DefaultClass, "default"); +export { $$wrap_DefaultClass as default }; +``` + +## module-export-effect + +**Status:** transformed + +**References:** NamedClass, ClassExpression, default + +[Source map visualization](https://evanw.github.io/source-map-visualization/#MzQ3ACd1c2Ugc2VydmVyJwoKY2xhc3MgTmFtZWRDbGFzcyB7fQoKY29uc3QgQ2xhc3NFeHByZXNzaW9uID0gY2xhc3MgSW5uZXJDbGFzcyB7fQoKY2xhc3MgRGVmYXVsdENsYXNzIHt9CgpyZWdpc3RlclNlcnZlclJlZmVyZW5jZShOYW1lZENsYXNzLCAiTmFtZWRDbGFzcyIpOwpleHBvcnQgeyBOYW1lZENsYXNzIH07CgpyZWdpc3RlclNlcnZlclJlZmVyZW5jZShDbGFzc0V4cHJlc3Npb24sICJDbGFzc0V4cHJlc3Npb24iKTsKZXhwb3J0IHsgQ2xhc3NFeHByZXNzaW9uIH07CgpyZWdpc3RlclNlcnZlclJlZmVyZW5jZShEZWZhdWx0Q2xhc3MsICJkZWZhdWx0Iik7CmV4cG9ydCBkZWZhdWx0IERlZmF1bHRDbGFzczsKNDA5AHsidmVyc2lvbiI6Mywic291cmNlcyI6WyIiXSwic291cmNlc0NvbnRlbnQiOlsiJ3VzZSBzZXJ2ZXInXG5cbmV4cG9ydCBjbGFzcyBOYW1lZENsYXNzIHt9XG5cbmV4cG9ydCBjb25zdCBDbGFzc0V4cHJlc3Npb24gPSBjbGFzcyBJbm5lckNsYXNzIHt9XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIERlZmF1bHRDbGFzcyB7fVxuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLENBQUMsR0FBRyxDQUFDLE1BQU07O0FBRUosS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDOztBQUVsQixLQUFLLENBQUMsZUFBZSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLENBQUM7O0FBRWxDLEtBQUssQ0FBQyxZQUFZLENBQUMsQ0FBQztBQUpuQztBQUFBO0FBQUE7QUFFQTtBQUFBO0FBQUE7QUFFQTtBQUFBO0FBQUE7In0=) + +```js +'use server' + +class NamedClass {} + +const ClassExpression = class InnerClass {} + +class DefaultClass {} + +registerServerReference(NamedClass, "NamedClass"); +export { NamedClass }; + +registerServerReference(ClassExpression, "ClassExpression"); +export { ClassExpression }; + +registerServerReference(DefaultClass, "default"); +export default DefaultClass; +``` diff --git a/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-anonymous-class.js b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-anonymous-class.js new file mode 100644 index 000000000..4bd0d1182 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-anonymous-class.js @@ -0,0 +1,3 @@ +'use server' + +export default class {} diff --git a/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-anonymous-class.js.map.snap.md b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-anonymous-class.js.map.snap.md new file mode 100644 index 000000000..0ecfabcc0 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-anonymous-class.js.map.snap.md @@ -0,0 +1,21 @@ +## wrap-export + +```txt +(0:0) "'use server'\n" --> (0:0) "'use server'\n" +(2:0) "export default " --> (2:0) "const $$default = " +(2:15) "class {}\n" --> (2:18) "class {}\n" +[unmapped] --> (3:0) ";\n" +[unmapped] --> (4:0) "const $$wrap_$$default = /* #__PURE__ */ registerServerReference($$default, \"default\");\n" +[unmapped] --> (5:0) "export { $$wrap_$$default as default };\n" +``` + +## module-export-effect + +```txt +(0:0) "'use server'\n" --> (0:0) "'use server'\n" +(2:6) " default " --> (2:0) "const $$effect_default = " +(2:15) "class {}\n" --> (2:25) "class {}\n" +(2:0) "export default class {}\n" --> (3:0) "\n" +(2:0) "export default class {}\n" --> (4:0) "registerServerReference($$effect_default, \"default\");\n" +(2:0) "export default class {}\n" --> (5:0) "export default $$effect_default;\n" +``` diff --git a/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-anonymous-class.js.snap.md b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-anonymous-class.js.snap.md new file mode 100644 index 000000000..7b7f84fb7 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-anonymous-class.js.snap.md @@ -0,0 +1,41 @@ +## Input + +```js +'use server' + +export default class {} +``` + +## wrap-export + +**Status:** transformed + +**References:** default + +[Source map visualization](https://evanw.github.io/source-map-visualization/#MTcxACd1c2Ugc2VydmVyJwoKY29uc3QgJCRkZWZhdWx0ID0gY2xhc3Mge30KOwpjb25zdCAkJHdyYXBfJCRkZWZhdWx0ID0gLyogI19fUFVSRV9fICovIHJlZ2lzdGVyU2VydmVyUmVmZXJlbmNlKCQkZGVmYXVsdCwgImRlZmF1bHQiKTsKZXhwb3J0IHsgJCR3cmFwXyQkZGVmYXVsdCBhcyBkZWZhdWx0IH07CjE3MQB7InZlcnNpb24iOjMsInNvdXJjZXMiOlsiIl0sInNvdXJjZXNDb250ZW50IjpbIid1c2Ugc2VydmVyJ1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyB7fVxuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLENBQUMsR0FBRyxDQUFDLE1BQU07O0FBRVgsa0JBQWUsS0FBSyxDQUFDLENBQUM7Ozs7In0=) + +```js +'use server' + +const $$default = class {} +; +const $$wrap_$$default = /* #__PURE__ */ registerServerReference($$default, "default"); +export { $$wrap_$$default as default }; +``` + +## module-export-effect + +**Status:** transformed + +**References:** default + +[Source map visualization](https://evanw.github.io/source-map-visualization/#MTM2ACd1c2Ugc2VydmVyJwoKY29uc3QgJCRlZmZlY3RfZGVmYXVsdCA9IGNsYXNzIHt9CgpyZWdpc3RlclNlcnZlclJlZmVyZW5jZSgkJGVmZmVjdF9kZWZhdWx0LCAiZGVmYXVsdCIpOwpleHBvcnQgZGVmYXVsdCAkJGVmZmVjdF9kZWZhdWx0OwoxODQAeyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHNlcnZlcidcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3Mge31cbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxDQUFDLEdBQUcsQ0FBQyxNQUFNOztBQUVMLHlCQUFTLEtBQUssQ0FBQyxDQUFDO0FBQXRCO0FBQUE7QUFBQTsifQ==) + +```js +'use server' + +const $$effect_default = class {} + +registerServerReference($$effect_default, "default"); +export default $$effect_default; +``` diff --git a/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-comments.js b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-comments.js new file mode 100644 index 000000000..b4c2fe4b9 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-comments.js @@ -0,0 +1,5 @@ +'use server' + +export /* before */ default /* after */ async function () { + return 'default comments called' +} diff --git a/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-comments.js.map.snap.md b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-comments.js.map.snap.md new file mode 100644 index 000000000..50dc072a3 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-comments.js.map.snap.md @@ -0,0 +1,25 @@ +## wrap-export + +```txt +(0:0) "'use server'\n" --> (0:0) "'use server'\n" +(2:0) "export /* before */ default /* after */ " --> (2:0) "const $$default = " +(2:40) "async function () {\n" --> (2:18) "async function () {\n" +(3:0) " return 'default comments called'\n" --> (3:0) " return 'default comments called'\n" +(4:0) "}\n" --> (4:0) "}\n" +[unmapped] --> (5:0) ";\n" +[unmapped] --> (6:0) "const $$wrap_$$default = /* #__PURE__ */ registerServerReference($$default, \"default\");\n" +[unmapped] --> (7:0) "export { $$wrap_$$default as default };\n" +``` + +## module-export-effect + +```txt +(0:0) "'use server'\n" --> (0:0) "'use server'\n" +(2:6) " /* before */ default /* after */ " --> (2:0) "const $$effect_default = " +(2:40) "async function () {\n" --> (2:25) "async function () {\n" +(3:0) " return 'default comments called'\n" --> (3:0) " return 'default comments called'\n" +(4:0) "}\n" --> (4:0) "}\n" +(2:0) "export /* before */ default /* after */ async function () {\n" --> (5:0) "\n" +(2:0) "export /* before */ default /* after */ async function () {\n" --> (6:0) "registerServerReference($$effect_default, \"default\");\n" +(2:0) "export /* before */ default /* after */ async function () {\n" --> (7:0) "export default $$effect_default;\n" +``` diff --git a/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-comments.js.snap.md b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-comments.js.snap.md new file mode 100644 index 000000000..32756539d --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/default-comments.js.snap.md @@ -0,0 +1,47 @@ +## Input + +```js +'use server' + +export /* before */ default /* after */ async function () { + return 'default comments called' +} +``` + +## wrap-export + +**Status:** transformed + +**References:** default + +[Source map visualization](https://evanw.github.io/source-map-visualization/#MjE5ACd1c2Ugc2VydmVyJwoKY29uc3QgJCRkZWZhdWx0ID0gYXN5bmMgZnVuY3Rpb24gKCkgewogIHJldHVybiAnZGVmYXVsdCBjb21tZW50cyBjYWxsZWQnCn0KOwpjb25zdCAkJHdyYXBfJCRkZWZhdWx0ID0gLyogI19fUFVSRV9fICovIHJlZ2lzdGVyU2VydmVyUmVmZXJlbmNlKCQkZGVmYXVsdCwgImRlZmF1bHQiKTsKZXhwb3J0IHsgJCR3cmFwXyQkZGVmYXVsdCBhcyBkZWZhdWx0IH07CjMyOQB7InZlcnNpb24iOjMsInNvdXJjZXMiOlsiIl0sInNvdXJjZXNDb250ZW50IjpbIid1c2Ugc2VydmVyJ1xuXG5leHBvcnQgLyogYmVmb3JlICovIGRlZmF1bHQgLyogYWZ0ZXIgKi8gYXN5bmMgZnVuY3Rpb24gKCkge1xuICByZXR1cm4gJ2RlZmF1bHQgY29tbWVudHMgY2FsbGVkJ1xufVxuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLENBQUMsR0FBRyxDQUFDLE1BQU07O0FBRVgsa0JBQXdDLEtBQUssQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDMUQsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUMsTUFBTTtBQUNqQzs7OzsifQ==) + +```js +'use server' + +const $$default = async function () { + return 'default comments called' +} +; +const $$wrap_$$default = /* #__PURE__ */ registerServerReference($$default, "default"); +export { $$wrap_$$default as default }; +``` + +## module-export-effect + +**Status:** transformed + +**References:** default + +[Source map visualization](https://evanw.github.io/source-map-visualization/#MTg0ACd1c2Ugc2VydmVyJwoKY29uc3QgJCRlZmZlY3RfZGVmYXVsdCA9IGFzeW5jIGZ1bmN0aW9uICgpIHsKICByZXR1cm4gJ2RlZmF1bHQgY29tbWVudHMgY2FsbGVkJwp9CgpyZWdpc3RlclNlcnZlclJlZmVyZW5jZSgkJGVmZmVjdF9kZWZhdWx0LCAiZGVmYXVsdCIpOwpleHBvcnQgZGVmYXVsdCAkJGVmZmVjdF9kZWZhdWx0OwozNDEAeyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHNlcnZlcidcblxuZXhwb3J0IC8qIGJlZm9yZSAqLyBkZWZhdWx0IC8qIGFmdGVyICovIGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgcmV0dXJuICdkZWZhdWx0IGNvbW1lbnRzIGNhbGxlZCdcbn1cbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxDQUFDLEdBQUcsQ0FBQyxNQUFNOztBQUVMLHlCQUFrQyxLQUFLLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQzFELENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLE1BQU07QUFDakM7QUFGQTtBQUFBO0FBQUE7In0=) + +```js +'use server' + +const $$effect_default = async function () { + return 'default comments called' +} + +registerServerReference($$effect_default, "default"); +export default $$effect_default; +``` diff --git a/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/local-binding-preservation.js b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/local-binding-preservation.js new file mode 100644 index 000000000..06e0bcbf2 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/local-binding-preservation.js @@ -0,0 +1,6 @@ +export async function direct() {} + +const indirect = async () => {} +export { indirect } + +consume(direct, indirect) diff --git a/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/local-binding-preservation.js.map.snap.md b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/local-binding-preservation.js.map.snap.md new file mode 100644 index 000000000..34b62c715 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/local-binding-preservation.js.map.snap.md @@ -0,0 +1,25 @@ +## wrap-export + +```txt +(0:7) "async function direct() {}\n" --> (0:0) "async function direct() {}\n" +(2:0) "const indirect = async () => {}\n" --> (2:0) "const indirect = async () => {}\n" +(5:0) "consume(direct, indirect)\n" --> (5:0) "consume(direct, indirect)\n" +(0:0) "export async function direct() {}\n" --> (6:0) "direct = /* #__PURE__ */ registerServerReference(direct, \"direct\");\n" +(0:0) "export async function direct() {}\n" --> (7:0) "export { direct };\n" +[unmapped] --> (8:0) ";\n" +[unmapped] --> (9:0) "const $$wrap_indirect = /* #__PURE__ */ registerServerReference(indirect, \"indirect\");\n" +[unmapped] --> (10:0) "export { $$wrap_indirect as indirect };\n" +``` + +## module-export-effect + +```txt +(0:7) "async function direct() {}\n" --> (0:0) "async function direct() {}\n" +(2:0) "const indirect = async () => {}\n" --> (2:0) "const indirect = async () => {}\n" +(3:0) "export { indirect }\n" --> (3:0) "export { indirect }\n" +(5:0) "consume(direct, indirect)\n" --> (5:0) "consume(direct, indirect)\n" +(0:0) "export async function direct() {}\n" --> (6:0) "\n" +(0:0) "export async function direct() {}\n" --> (7:0) "registerServerReference(direct, \"direct\");\n" +(0:0) "export async function direct() {}\n" --> (8:0) "export { direct };\n" +[unmapped] --> (10:0) "registerServerReference(indirect, \"indirect\");" +``` diff --git a/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/local-binding-preservation.js.snap.md b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/local-binding-preservation.js.snap.md new file mode 100644 index 000000000..a095b3ac4 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/local-binding-preservation.js.snap.md @@ -0,0 +1,54 @@ +## Input + +```js +export async function direct() {} + +const indirect = async () => {} +export { indirect } + +consume(direct, indirect) +``` + +## wrap-export + +**Status:** transformed + +**References:** direct, indirect + +[Source map visualization](https://evanw.github.io/source-map-visualization/#MzA0AGFzeW5jIGZ1bmN0aW9uIGRpcmVjdCgpIHt9Cgpjb25zdCBpbmRpcmVjdCA9IGFzeW5jICgpID0+IHt9CgoKY29uc3VtZShkaXJlY3QsIGluZGlyZWN0KQpkaXJlY3QgPSAvKiAjX19QVVJFX18gKi8gcmVnaXN0ZXJTZXJ2ZXJSZWZlcmVuY2UoZGlyZWN0LCAiZGlyZWN0Iik7CmV4cG9ydCB7IGRpcmVjdCB9Owo7CmNvbnN0ICQkd3JhcF9pbmRpcmVjdCA9IC8qICNfX1BVUkVfXyAqLyByZWdpc3RlclNlcnZlclJlZmVyZW5jZShpbmRpcmVjdCwgImluZGlyZWN0Iik7CmV4cG9ydCB7ICQkd3JhcF9pbmRpcmVjdCBhcyBpbmRpcmVjdCB9OwozNzkAeyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIiJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgYXN5bmMgZnVuY3Rpb24gZGlyZWN0KCkge31cblxuY29uc3QgaW5kaXJlY3QgPSBhc3luYyAoKSA9PiB7fVxuZXhwb3J0IHsgaW5kaXJlY3QgfVxuXG5jb25zdW1lKGRpcmVjdCwgaW5kaXJlY3QpXG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQU8sS0FBSyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7O0FBRWhDLEtBQUssQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDOzs7QUFHOUIsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLFFBQVE7QUFMeEI7QUFBQTs7OzsifQ==) + +```js +async function direct() {} + +const indirect = async () => {} + + +consume(direct, indirect) +direct = /* #__PURE__ */ registerServerReference(direct, "direct"); +export { direct }; +; +const $$wrap_indirect = /* #__PURE__ */ registerServerReference(indirect, "indirect"); +export { $$wrap_indirect as indirect }; +``` + +## module-export-effect + +**Status:** transformed + +**References:** direct, indirect + +[Source map visualization](https://evanw.github.io/source-map-visualization/#MjE3AGFzeW5jIGZ1bmN0aW9uIGRpcmVjdCgpIHt9Cgpjb25zdCBpbmRpcmVjdCA9IGFzeW5jICgpID0+IHt9CmV4cG9ydCB7IGluZGlyZWN0IH0KCmNvbnN1bWUoZGlyZWN0LCBpbmRpcmVjdCkKCnJlZ2lzdGVyU2VydmVyUmVmZXJlbmNlKGRpcmVjdCwgImRpcmVjdCIpOwpleHBvcnQgeyBkaXJlY3QgfTsKCnJlZ2lzdGVyU2VydmVyUmVmZXJlbmNlKGluZGlyZWN0LCAiaW5kaXJlY3QiKTs0MTcAeyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIiJdLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnQgYXN5bmMgZnVuY3Rpb24gZGlyZWN0KCkge31cblxuY29uc3QgaW5kaXJlY3QgPSBhc3luYyAoKSA9PiB7fVxuZXhwb3J0IHsgaW5kaXJlY3QgfVxuXG5jb25zdW1lKGRpcmVjdCwgaW5kaXJlY3QpXG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQU8sS0FBSyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUM7O0FBRWhDLEtBQUssQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQzlCLE1BQU0sQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDOztBQUVsQixPQUFPLENBQUMsTUFBTSxDQUFDLENBQUMsUUFBUTtBQUx4QjtBQUFBO0FBQUE7OyJ9) + +```js +async function direct() {} + +const indirect = async () => {} +export { indirect } + +consume(direct, indirect) + +registerServerReference(direct, "direct"); +export { direct }; + +registerServerReference(indirect, "indirect"); +``` diff --git a/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/multiple-aliases.js b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/multiple-aliases.js new file mode 100644 index 000000000..babb53a27 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/multiple-aliases.js @@ -0,0 +1,7 @@ +'use server' + +async function value() { + return 'multiple aliases called' +} + +export { value as first, value as second } diff --git a/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/multiple-aliases.js.map.snap.md b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/multiple-aliases.js.map.snap.md new file mode 100644 index 000000000..101621d42 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/multiple-aliases.js.map.snap.md @@ -0,0 +1,25 @@ +## wrap-export + +```txt +(0:0) "'use server'\n" --> (0:0) "'use server'\n" +(2:0) "async function value() {\n" --> (2:0) "async function value() {\n" +(3:0) " return 'multiple aliases called'\n" --> (3:0) " return 'multiple aliases called'\n" +(4:0) "}\n" --> (4:0) "}\n" +[unmapped] --> (7:0) ";\n" +[unmapped] --> (8:0) "const $$wrap_value = /* #__PURE__ */ registerServerReference(value, \"first\");\n" +[unmapped] --> (9:0) "export { $$wrap_value as first };\n" +[unmapped] --> (10:0) "const $$wrap_value = /* #__PURE__ */ registerServerReference(value, \"second\");\n" +[unmapped] --> (11:0) "export { $$wrap_value as second };\n" +``` + +## module-export-effect + +```txt +(0:0) "'use server'\n" --> (0:0) "'use server'\n" +(2:0) "async function value() {\n" --> (2:0) "async function value() {\n" +(3:0) " return 'multiple aliases called'\n" --> (3:0) " return 'multiple aliases called'\n" +(4:0) "}\n" --> (4:0) "}\n" +(6:0) "export { value as first, value as second }\n" --> (6:0) "export { value as first, value as second }\n" +[unmapped] --> (8:0) "registerServerReference(value, \"first\");\n" +[unmapped] --> (9:0) "registerServerReference(value, \"second\");" +``` diff --git a/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/multiple-aliases.js.snap.md b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/multiple-aliases.js.snap.md new file mode 100644 index 000000000..88cc01b87 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/multiple-aliases.js.snap.md @@ -0,0 +1,55 @@ +## Input + +```js +'use server' + +async function value() { + return 'multiple aliases called' +} + +export { value as first, value as second } +``` + +## wrap-export + +**Status:** transformed + +**References:** first, second + +[Source map visualization](https://evanw.github.io/source-map-visualization/#MzA2ACd1c2Ugc2VydmVyJwoKYXN5bmMgZnVuY3Rpb24gdmFsdWUoKSB7CiAgcmV0dXJuICdtdWx0aXBsZSBhbGlhc2VzIGNhbGxlZCcKfQoKCjsKY29uc3QgJCR3cmFwX3ZhbHVlID0gLyogI19fUFVSRV9fICovIHJlZ2lzdGVyU2VydmVyUmVmZXJlbmNlKHZhbHVlLCAiZmlyc3QiKTsKZXhwb3J0IHsgJCR3cmFwX3ZhbHVlIGFzIGZpcnN0IH07CmNvbnN0ICQkd3JhcF92YWx1ZSA9IC8qICNfX1BVUkVfXyAqLyByZWdpc3RlclNlcnZlclJlZmVyZW5jZSh2YWx1ZSwgInNlY29uZCIpOwpleHBvcnQgeyAkJHdyYXBfdmFsdWUgYXMgc2Vjb25kIH07CjM0MgB7InZlcnNpb24iOjMsInNvdXJjZXMiOlsiIl0sInNvdXJjZXNDb250ZW50IjpbIid1c2Ugc2VydmVyJ1xuXG5hc3luYyBmdW5jdGlvbiB2YWx1ZSgpIHtcbiAgcmV0dXJuICdtdWx0aXBsZSBhbGlhc2VzIGNhbGxlZCdcbn1cblxuZXhwb3J0IHsgdmFsdWUgYXMgZmlyc3QsIHZhbHVlIGFzIHNlY29uZCB9XG4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsQ0FBQyxHQUFHLENBQUMsTUFBTTs7QUFFWCxLQUFLLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7QUFDdkIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsTUFBTTtBQUNqQzs7Ozs7Ozs7In0=) + +```js +'use server' + +async function value() { + return 'multiple aliases called' +} + + +; +const $$wrap_value = /* #__PURE__ */ registerServerReference(value, "first"); +export { $$wrap_value as first }; +const $$wrap_value = /* #__PURE__ */ registerServerReference(value, "second"); +export { $$wrap_value as second }; +``` + +## module-export-effect + +**Status:** transformed + +**References:** first, second + +[Source map visualization](https://evanw.github.io/source-map-visualization/#MjAzACd1c2Ugc2VydmVyJwoKYXN5bmMgZnVuY3Rpb24gdmFsdWUoKSB7CiAgcmV0dXJuICdtdWx0aXBsZSBhbGlhc2VzIGNhbGxlZCcKfQoKZXhwb3J0IHsgdmFsdWUgYXMgZmlyc3QsIHZhbHVlIGFzIHNlY29uZCB9CgpyZWdpc3RlclNlcnZlclJlZmVyZW5jZSh2YWx1ZSwgImZpcnN0Iik7CnJlZ2lzdGVyU2VydmVyUmVmZXJlbmNlKHZhbHVlLCAic2Vjb25kIik7NDI4AHsidmVyc2lvbiI6Mywic291cmNlcyI6WyIiXSwic291cmNlc0NvbnRlbnQiOlsiJ3VzZSBzZXJ2ZXInXG5cbmFzeW5jIGZ1bmN0aW9uIHZhbHVlKCkge1xuICByZXR1cm4gJ211bHRpcGxlIGFsaWFzZXMgY2FsbGVkJ1xufVxuXG5leHBvcnQgeyB2YWx1ZSBhcyBmaXJzdCwgdmFsdWUgYXMgc2Vjb25kIH1cbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxDQUFDLEdBQUcsQ0FBQyxNQUFNOztBQUVYLEtBQUssQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztBQUN2QixDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxNQUFNO0FBQ2pDOztBQUVBLE1BQU0sQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLE1BQU0sQ0FBQzs7OyJ9) + +```js +'use server' + +async function value() { + return 'multiple aliases called' +} + +export { value as first, value as second } + +registerServerReference(value, "first"); +registerServerReference(value, "second"); +``` diff --git a/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/recursive.js b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/recursive.js new file mode 100644 index 000000000..f9953526b --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/recursive.js @@ -0,0 +1,6 @@ +'use server' + +export function recursive(depth) { + if (depth > 0) return recursive(depth - 1) + return recursive.marker +} diff --git a/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/recursive.js.map.snap.md b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/recursive.js.map.snap.md new file mode 100644 index 000000000..608b8a776 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/recursive.js.map.snap.md @@ -0,0 +1,24 @@ +## wrap-export + +```txt +(0:0) "'use server'\n" --> (0:0) "'use server'\n" +(2:7) "function recursive(depth) {\n" --> (2:0) "function recursive(depth) {\n" +(3:0) " if (depth > 0) return recursive(depth - 1)\n" --> (3:0) " if (depth > 0) return recursive(depth - 1)\n" +(4:0) " return recursive.marker\n" --> (4:0) " return recursive.marker\n" +(5:0) "}\n" --> (5:0) "}\n" +(2:0) "export function recursive(depth) {\n" --> (6:0) "recursive = /* #__PURE__ */ registerServerReference(recursive, \"recursive\");\n" +(2:0) "export function recursive(depth) {\n" --> (7:0) "export { recursive };\n" +``` + +## module-export-effect + +```txt +(0:0) "'use server'\n" --> (0:0) "'use server'\n" +(2:7) "function recursive(depth) {\n" --> (2:0) "function recursive(depth) {\n" +(3:0) " if (depth > 0) return recursive(depth - 1)\n" --> (3:0) " if (depth > 0) return recursive(depth - 1)\n" +(4:0) " return recursive.marker\n" --> (4:0) " return recursive.marker\n" +(5:0) "}\n" --> (5:0) "}\n" +(2:0) "export function recursive(depth) {\n" --> (6:0) "\n" +(2:0) "export function recursive(depth) {\n" --> (7:0) "registerServerReference(recursive, \"recursive\");\n" +(2:0) "export function recursive(depth) {\n" --> (8:0) "export { recursive };\n" +``` diff --git a/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/recursive.js.snap.md b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/recursive.js.snap.md new file mode 100644 index 000000000..5248f4be4 --- /dev/null +++ b/packages/plugin-rsc/src/transforms/fixtures/source-map/wrap-export/recursive.js.snap.md @@ -0,0 +1,49 @@ +## Input + +```js +'use server' + +export function recursive(depth) { + if (depth > 0) return recursive(depth - 1) + return recursive.marker +} +``` + +## wrap-export + +**Status:** transformed + +**References:** recursive + +[Source map visualization](https://evanw.github.io/source-map-visualization/#MjE0ACd1c2Ugc2VydmVyJwoKZnVuY3Rpb24gcmVjdXJzaXZlKGRlcHRoKSB7CiAgaWYgKGRlcHRoID4gMCkgcmV0dXJuIHJlY3Vyc2l2ZShkZXB0aCAtIDEpCiAgcmV0dXJuIHJlY3Vyc2l2ZS5tYXJrZXIKfQpyZWN1cnNpdmUgPSAvKiAjX19QVVJFX18gKi8gcmVnaXN0ZXJTZXJ2ZXJSZWZlcmVuY2UocmVjdXJzaXZlLCAicmVjdXJzaXZlIik7CmV4cG9ydCB7IHJlY3Vyc2l2ZSB9Owo0MzIAeyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHNlcnZlcidcblxuZXhwb3J0IGZ1bmN0aW9uIHJlY3Vyc2l2ZShkZXB0aCkge1xuICBpZiAoZGVwdGggPiAwKSByZXR1cm4gcmVjdXJzaXZlKGRlcHRoIC0gMSlcbiAgcmV0dXJuIHJlY3Vyc2l2ZS5tYXJrZXJcbn1cbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxDQUFDLEdBQUcsQ0FBQyxNQUFNOztBQUVKLFFBQVEsQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7QUFDakMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDM0MsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUM7QUFDbkI7QUFIQTtBQUFBOyJ9) + +```js +'use server' + +function recursive(depth) { + if (depth > 0) return recursive(depth - 1) + return recursive.marker +} +recursive = /* #__PURE__ */ registerServerReference(recursive, "recursive"); +export { recursive }; +``` + +## module-export-effect + +**Status:** transformed + +**References:** recursive + +[Source map visualization](https://evanw.github.io/source-map-visualization/#MTg3ACd1c2Ugc2VydmVyJwoKZnVuY3Rpb24gcmVjdXJzaXZlKGRlcHRoKSB7CiAgaWYgKGRlcHRoID4gMCkgcmV0dXJuIHJlY3Vyc2l2ZShkZXB0aCAtIDEpCiAgcmV0dXJuIHJlY3Vyc2l2ZS5tYXJrZXIKfQoKcmVnaXN0ZXJTZXJ2ZXJSZWZlcmVuY2UocmVjdXJzaXZlLCAicmVjdXJzaXZlIik7CmV4cG9ydCB7IHJlY3Vyc2l2ZSB9Owo0MzcAeyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHNlcnZlcidcblxuZXhwb3J0IGZ1bmN0aW9uIHJlY3Vyc2l2ZShkZXB0aCkge1xuICBpZiAoZGVwdGggPiAwKSByZXR1cm4gcmVjdXJzaXZlKGRlcHRoIC0gMSlcbiAgcmV0dXJuIHJlY3Vyc2l2ZS5tYXJrZXJcbn1cbiJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxDQUFDLEdBQUcsQ0FBQyxNQUFNOztBQUVKLFFBQVEsQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7QUFDakMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7QUFDM0MsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUM7QUFDbkI7QUFIQTtBQUFBO0FBQUE7In0=) + +```js +'use server' + +function recursive(depth) { + if (depth > 0) return recursive(depth - 1) + return recursive.marker +} + +registerServerReference(recursive, "recursive"); +export { recursive }; +``` diff --git a/packages/plugin-rsc/src/transforms/module-export-effect.test.ts b/packages/plugin-rsc/src/transforms/module-export-effect.test.ts index cace6e975..93642475d 100644 --- a/packages/plugin-rsc/src/transforms/module-export-effect.test.ts +++ b/packages/plugin-rsc/src/transforms/module-export-effect.test.ts @@ -243,7 +243,9 @@ export default async function Page() {} for (const [input, expected] of examples) { const result = await transform(input) - expect(result.references.map((context) => context.meta)).toEqual(expected) + expect( + result.references.map(({ meta: { valueNode: _, ...meta } }) => meta), + ).toEqual(expected) } }) }) diff --git a/packages/plugin-rsc/src/transforms/module-export-scan.test.ts b/packages/plugin-rsc/src/transforms/module-export-scan.test.ts index aae47872b..c38487d9b 100644 --- a/packages/plugin-rsc/src/transforms/module-export-scan.test.ts +++ b/packages/plugin-rsc/src/transforms/module-export-scan.test.ts @@ -26,6 +26,7 @@ export * from './all' meta: { localName: 'action', isFunction: true, + valueNode: { type: 'FunctionDeclaration' }, }, }, ], @@ -42,6 +43,7 @@ export * from './all' meta: { localName: 'loader', isFunction: true, + valueNode: { type: 'ArrowFunctionExpression' }, }, }, ], @@ -54,6 +56,7 @@ export * from './all' meta: { localName: 'value', isFunction: false, + valueNode: { type: 'Literal' }, }, }, ], @@ -71,6 +74,7 @@ export * from './all' meta: { localName: 'item', isFunction: undefined, + valueNode: { type: 'Identifier', name: 'source' }, }, }, ], @@ -91,7 +95,10 @@ export * from './all' type: 'default', kind: 'identifier', localName: undefined, - meta: { defaultExportIdentifierName: 'action' }, + meta: { + defaultExportIdentifierName: 'action', + valueNode: { type: 'Identifier', name: 'action' }, + }, }) expect(groups[6]).toMatchObject({ type: 'export-all' }) }) diff --git a/packages/plugin-rsc/src/transforms/module-export-scan.ts b/packages/plugin-rsc/src/transforms/module-export-scan.ts index c3b8ccf2a..30e5d19fa 100644 --- a/packages/plugin-rsc/src/transforms/module-export-scan.ts +++ b/packages/plugin-rsc/src/transforms/module-export-scan.ts @@ -15,6 +15,16 @@ import type { ESTree } from 'vite' import { extractNames } from './utils' export type ModuleExportMeta = { + /** + * The source node that evaluates to the exported value when directly + * available. + * + * - The declaration for a function or class export. + * - The initializer for a variable export. + * - The declaration expression for a default export. + * - `undefined` for export specifiers and re-exports. + */ + valueNode?: Node | ExportDefaultDeclaration['declaration'] /** * The local declaration name when statically available. * @@ -141,6 +151,7 @@ export function scanModuleExports( meta: { localName: name, isFunction, + valueNode: declarator.init ?? undefined, }, })), } @@ -162,6 +173,7 @@ export function scanModuleExports( meta: { localName: name, isFunction: getIsFunction(node.declaration), + valueNode: node.declaration, }, }, ], @@ -211,15 +223,22 @@ export function scanModuleExports( meta = { localName: node.declaration.id.name, isFunction: getIsFunction(node.declaration), + valueNode: node.declaration, } } else if (node.declaration.type === 'Identifier') { kind = 'identifier' - meta = { defaultExportIdentifierName: node.declaration.name } + meta = { + defaultExportIdentifierName: node.declaration.name, + valueNode: node.declaration, + } } else { // export default function () {} // export default () => {} kind = 'other' - meta = { isFunction: getIsFunction(node.declaration) } + meta = { + isFunction: getIsFunction(node.declaration), + valueNode: node.declaration, + } } groups.push({ type: 'default', kind, node, localName, meta }) } diff --git a/packages/plugin-rsc/src/transforms/wrap-export.test.ts b/packages/plugin-rsc/src/transforms/wrap-export.test.ts index 69a726c0d..b2e349c4f 100644 --- a/packages/plugin-rsc/src/transforms/wrap-export.test.ts +++ b/packages/plugin-rsc/src/transforms/wrap-export.test.ts @@ -259,11 +259,10 @@ export default function d() {} }) expect(result).toMatchInlineSnapshot(` " - let a = 0; + export const a = 0; let b = function() {} let c = () => {} function d() {} - export { a }; b = /* #__PURE__ */ $$wrap(b, "", "b"); export { b }; c = /* #__PURE__ */ $$wrap(c, "", "c"); @@ -311,6 +310,32 @@ export default Page; `) }) + test('filter value node', async () => { + const input = `\ +export const action = async () => {} +export const metadata = {} +export const tags = [] +` + const ast = await parseAstAsync(input) + const result = transformWrapExport(input, ast, { + runtime: (value, name) => `$$wrap(${value}, ${JSON.stringify(name)})`, + rejectNonAsyncFunction: true, + filter: (_name, meta) => + meta.valueNode?.type !== 'ObjectExpression' && + meta.valueNode?.type !== 'ArrayExpression', + }) + + expect(result.exportNames).toEqual(['action']) + expect(result.output.toString()).toMatchInlineSnapshot(` + "let action = async () => {} + export const metadata = {} + export const tags = [] + action = /* #__PURE__ */ $$wrap(action, \"action\"); + export { action }; + " + `) + }) + test('filtered exports are not validated or reported', async () => { const input = ` export const revalidate = 1; @@ -325,9 +350,8 @@ export default async function Page() {} expect(result.exportNames).toEqual(['default']) expect(result.output.toString()).toMatchInlineSnapshot(` " - let revalidate = 1; + export const revalidate = 1; async function Page() {} - export { revalidate }; ; const $$wrap_Page = /* #__PURE__ */ $$wrap(Page, "default"); export { $$wrap_Page as default }; @@ -465,7 +489,8 @@ export default cached; const ast = await parseAstAsync(input) transformWrapExport(input, ast, { runtime(value, _name, meta) { - actual.push(meta) + const { valueNode: _, ...rest } = meta + actual.push(rest) return value }, }) diff --git a/packages/plugin-rsc/src/transforms/wrap-export.ts b/packages/plugin-rsc/src/transforms/wrap-export.ts index aaa9fcee8..fe7b608d4 100644 --- a/packages/plugin-rsc/src/transforms/wrap-export.ts +++ b/packages/plugin-rsc/src/transforms/wrap-export.ts @@ -9,6 +9,8 @@ import { import { validateNonAsyncFunction } from './utils' type ExportMeta = { + /** The directly exported declaration or value expression, when available. */ + valueNode?: ModuleExportMeta['valueNode'] /** * The local declaration name when statically available. * @@ -133,6 +135,13 @@ export function transformWrapExport( } wrapSimple(group.node.start, group.declaration.start, group.exports) } else if (group.type === 'variable-declaration') { + const shouldWrap = group.declarators.some((declarator) => + declarator.exports.some(({ exportName, meta }) => + filter(exportName, getExportMeta(meta)), + ), + ) + if (!shouldWrap) continue + if (group.declaration.kind === 'const') { output.update( group.declaration.start, From b809b88cde02b592fbe830197663bd81313d1e14 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:27:09 +0900 Subject: [PATCH 2/5] refactor(rsc): unify module export metadata Co-authored-by: OpenCode --- .../transforms/module-export-effect.test.ts | 32 ++++----- .../src/transforms/module-export-scan.test.ts | 8 +-- .../src/transforms/module-export-scan.ts | 8 +-- .../plugin-rsc/src/transforms/wrap-export.ts | 67 ++++--------------- 4 files changed, 38 insertions(+), 77 deletions(-) diff --git a/packages/plugin-rsc/src/transforms/module-export-effect.test.ts b/packages/plugin-rsc/src/transforms/module-export-effect.test.ts index 93642475d..2ad104d7d 100644 --- a/packages/plugin-rsc/src/transforms/module-export-effect.test.ts +++ b/packages/plugin-rsc/src/transforms/module-export-effect.test.ts @@ -126,7 +126,7 @@ export default async function Page() {} `export function Fn() {}`, [ { - localName: 'Fn', + declName: 'Fn', isFunction: true, }, ], @@ -135,7 +135,7 @@ export default async function Page() {} `export class Cls {}`, [ { - localName: 'Cls', + declName: 'Cls', isFunction: false, }, ], @@ -144,7 +144,7 @@ export default async function Page() {} `export const Arrow = () => {}`, [ { - localName: 'Arrow', + declName: 'Arrow', isFunction: true, }, ], @@ -153,7 +153,7 @@ export default async function Page() {} `export const FnExpression = function () {}`, [ { - localName: 'FnExpression', + declName: 'FnExpression', isFunction: true, }, ], @@ -162,7 +162,7 @@ export default async function Page() {} `export const Literal = 1`, [ { - localName: 'Literal', + declName: 'Literal', isFunction: false, }, ], @@ -171,7 +171,7 @@ export default async function Page() {} `export const ObjectValue = {}`, [ { - localName: 'ObjectValue', + declName: 'ObjectValue', isFunction: false, }, ], @@ -180,7 +180,7 @@ export default async function Page() {} `export const ArrayValue = []`, [ { - localName: 'ArrayValue', + declName: 'ArrayValue', isFunction: false, }, ], @@ -189,33 +189,33 @@ export default async function Page() {} `export const ClassValue = class {}`, [ { - localName: 'ClassValue', + declName: 'ClassValue', isFunction: false, }, ], ], - [`export const Unknown = getValue()`, [{ localName: 'Unknown' }]], - [`export const { id } = getValue()`, [{ localName: 'id' }]], - [`export const [a, b] = []`, [{ localName: 'a' }, { localName: 'b' }]], + [`export const Unknown = getValue()`, [{ declName: 'Unknown' }]], + [`export const { id } = getValue()`, [{ declName: 'id' }]], + [`export const [a, b] = []`, [{ declName: 'a' }, { declName: 'b' }]], [ `export const MultiFn = () => {}, MultiValue = 1, MultiUnknown = getValue()`, [ { - localName: 'MultiFn', + declName: 'MultiFn', isFunction: true, }, { - localName: 'MultiValue', + declName: 'MultiValue', isFunction: false, }, - { localName: 'MultiUnknown' }, + { declName: 'MultiUnknown' }, ], ], [ `export default function Page() {}`, [ { - localName: 'Page', + declName: 'Page', isFunction: true, }, ], @@ -225,7 +225,7 @@ export default async function Page() {} `export default class Page {}`, [ { - localName: 'Page', + declName: 'Page', isFunction: false, }, ], diff --git a/packages/plugin-rsc/src/transforms/module-export-scan.test.ts b/packages/plugin-rsc/src/transforms/module-export-scan.test.ts index c38487d9b..c40a3c915 100644 --- a/packages/plugin-rsc/src/transforms/module-export-scan.test.ts +++ b/packages/plugin-rsc/src/transforms/module-export-scan.test.ts @@ -24,7 +24,7 @@ export * from './all' localName: 'action', exportName: 'action', meta: { - localName: 'action', + declName: 'action', isFunction: true, valueNode: { type: 'FunctionDeclaration' }, }, @@ -41,7 +41,7 @@ export * from './all' localName: 'loader', exportName: 'loader', meta: { - localName: 'loader', + declName: 'loader', isFunction: true, valueNode: { type: 'ArrowFunctionExpression' }, }, @@ -54,7 +54,7 @@ export * from './all' localName: 'value', exportName: 'value', meta: { - localName: 'value', + declName: 'value', isFunction: false, valueNode: { type: 'Literal' }, }, @@ -72,7 +72,7 @@ export * from './all' localName: 'item', exportName: 'item', meta: { - localName: 'item', + declName: 'item', isFunction: undefined, valueNode: { type: 'Identifier', name: 'source' }, }, diff --git a/packages/plugin-rsc/src/transforms/module-export-scan.ts b/packages/plugin-rsc/src/transforms/module-export-scan.ts index 30e5d19fa..73f506c3b 100644 --- a/packages/plugin-rsc/src/transforms/module-export-scan.ts +++ b/packages/plugin-rsc/src/transforms/module-export-scan.ts @@ -33,7 +33,7 @@ export type ModuleExportMeta = { * - `undefined` for `export default () => {}` * - `undefined` for `export { Page }` */ - localName?: string + declName?: string /** * Whether the exported value is statically known to be a function. * @@ -149,7 +149,7 @@ export function scanModuleExports( localName: name, exportName: name, meta: { - localName: name, + declName: name, isFunction, valueNode: declarator.init ?? undefined, }, @@ -171,7 +171,7 @@ export function scanModuleExports( localName: name, exportName: name, meta: { - localName: name, + declName: name, isFunction: getIsFunction(node.declaration), valueNode: node.declaration, }, @@ -221,7 +221,7 @@ export function scanModuleExports( kind = 'named-declaration' localName = node.declaration.id.name meta = { - localName: node.declaration.id.name, + declName: node.declaration.id.name, isFunction: getIsFunction(node.declaration), valueNode: node.declaration, } diff --git a/packages/plugin-rsc/src/transforms/wrap-export.ts b/packages/plugin-rsc/src/transforms/wrap-export.ts index fe7b608d4..5d65b2338 100644 --- a/packages/plugin-rsc/src/transforms/wrap-export.ts +++ b/packages/plugin-rsc/src/transforms/wrap-export.ts @@ -8,46 +8,13 @@ import { } from './module-export-scan' import { validateNonAsyncFunction } from './utils' -type ExportMeta = { - /** The directly exported declaration or value expression, when available. */ - valueNode?: ModuleExportMeta['valueNode'] - /** - * The local declaration name when statically available. - * - * - `"Page"` for `export function Page() {}` - * - `"Page"` for `export const Page = () => {}` - * - `undefined` for `export default () => {}` - * - `undefined` for `export { Page }` - */ - declName?: string - /** - * Whether the exported value is statically known to be a function. - * - * - `true` for `export const Page = () => {}` - * - `false` for `export const value = 1` - * - `undefined` for `export const value = getValue()` - * - `undefined` for `export default Page` - */ - isFunction?: boolean - /** - * The local identifier referenced by a default export. - * The RSC CSS transform uses this to detect a component by its capitalized - * local name and preserve that name on the generated wrapper. - * - * - `"Page"` for `const Page = () => {}; export default Page` - * - `undefined` for `export default function Page() {}` - * - `undefined` for `export default () => {}` - */ - defaultExportIdentifierName?: string -} - export type TransformWrapExportFilter = ( name: string, - meta: ExportMeta, + meta: ModuleExportMeta, ) => boolean export type TransformWrapExportOptions = { - runtime: (value: string, name: string, meta: ExportMeta) => string + runtime: (value: string, name: string, meta: ModuleExportMeta) => string ignoreExportAllDeclaration?: boolean rejectNonAsyncFunction?: boolean filter?: TransformWrapExportFilter @@ -72,11 +39,9 @@ export function transformWrapExport( exports: ModuleExportEntry[], ) { const filteredExports = exports.map((item) => { - const meta = getExportMeta(item.meta) return { ...item, - meta, - shouldWrap: filter(item.exportName, meta), + shouldWrap: filter(item.exportName, item.meta), } }) exportNames.push( @@ -110,7 +75,11 @@ export function transformWrapExport( output.move(start, end, input.length) } - function wrapExport(name: string, exportName: string, meta: ExportMeta = {}) { + function wrapExport( + name: string, + exportName: string, + meta: ModuleExportMeta = {}, + ) { if (!filter(exportName, meta)) { toAppend.push(`export { ${name} as ${exportName} }`) return @@ -130,14 +99,14 @@ export function transformWrapExport( for (const group of scanModuleExports(viteAst)) { if (group.type === 'declaration') { const [entry] = group.exports - if (filter(entry.exportName, getExportMeta(entry.meta))) { + if (filter(entry.exportName, entry.meta)) { validateNonAsyncFunction(options, group.declaration) } wrapSimple(group.node.start, group.declaration.start, group.exports) } else if (group.type === 'variable-declaration') { const shouldWrap = group.declarators.some((declarator) => declarator.exports.some(({ exportName, meta }) => - filter(exportName, getExportMeta(meta)), + filter(exportName, meta), ), ) if (!shouldWrap) continue @@ -155,7 +124,7 @@ export function transformWrapExport( if ( declarator.node.init && declarator.exports.some(({ exportName, meta }) => - filter(exportName, getExportMeta(meta)), + filter(exportName, meta), ) ) { validateNonAsyncFunction(options, declarator.node.init) @@ -179,7 +148,7 @@ export function transformWrapExport( wrapExport( `$$import_${entry.localName}`, entry.exportName, - getExportMeta(entry.meta), + entry.meta, ) } } else { @@ -192,11 +161,7 @@ export function transformWrapExport( { pos: entry.node.exported.start }, ) } - wrapExport( - entry.localName, - entry.exportName, - getExportMeta(entry.meta), - ) + wrapExport(entry.localName, entry.exportName, entry.meta) } } } else if (group.type === 'export-all') { @@ -232,7 +197,7 @@ export function transformWrapExport( 'const $$default = ', ) } - const meta = getExportMeta(group.meta) + const meta = group.meta if (filter('default', meta)) { validateNonAsyncFunction(options, group.node.declaration) } @@ -246,7 +211,3 @@ export function transformWrapExport( return { exportNames, output } } - -function getExportMeta({ localName, ...meta }: ModuleExportMeta): ExportMeta { - return { ...meta, ...(localName && { declName: localName }) } -} From bfe0f7caa108949ff88844ed08aa67024ca3de7f Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:29:11 +0900 Subject: [PATCH 3/5] refactor(rsc): defer filtered declaration preservation Co-authored-by: OpenCode --- .../plugin-rsc/src/transforms/wrap-export.test.ts | 14 +++++++++----- packages/plugin-rsc/src/transforms/wrap-export.ts | 7 ------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/packages/plugin-rsc/src/transforms/wrap-export.test.ts b/packages/plugin-rsc/src/transforms/wrap-export.test.ts index b2e349c4f..976d511a9 100644 --- a/packages/plugin-rsc/src/transforms/wrap-export.test.ts +++ b/packages/plugin-rsc/src/transforms/wrap-export.test.ts @@ -259,10 +259,11 @@ export default function d() {} }) expect(result).toMatchInlineSnapshot(` " - export const a = 0; + let a = 0; let b = function() {} let c = () => {} function d() {} + export { a }; b = /* #__PURE__ */ $$wrap(b, "", "b"); export { b }; c = /* #__PURE__ */ $$wrap(c, "", "c"); @@ -328,10 +329,12 @@ export const tags = [] expect(result.exportNames).toEqual(['action']) expect(result.output.toString()).toMatchInlineSnapshot(` "let action = async () => {} - export const metadata = {} - export const tags = [] - action = /* #__PURE__ */ $$wrap(action, \"action\"); + let metadata = {} + let tags = [] + action = /* #__PURE__ */ $$wrap(action, "action"); export { action }; + export { metadata }; + export { tags }; " `) }) @@ -350,8 +353,9 @@ export default async function Page() {} expect(result.exportNames).toEqual(['default']) expect(result.output.toString()).toMatchInlineSnapshot(` " - export const revalidate = 1; + let revalidate = 1; async function Page() {} + export { revalidate }; ; const $$wrap_Page = /* #__PURE__ */ $$wrap(Page, "default"); export { $$wrap_Page as default }; diff --git a/packages/plugin-rsc/src/transforms/wrap-export.ts b/packages/plugin-rsc/src/transforms/wrap-export.ts index 5d65b2338..25b0320d4 100644 --- a/packages/plugin-rsc/src/transforms/wrap-export.ts +++ b/packages/plugin-rsc/src/transforms/wrap-export.ts @@ -104,13 +104,6 @@ export function transformWrapExport( } wrapSimple(group.node.start, group.declaration.start, group.exports) } else if (group.type === 'variable-declaration') { - const shouldWrap = group.declarators.some((declarator) => - declarator.exports.some(({ exportName, meta }) => - filter(exportName, meta), - ), - ) - if (!shouldWrap) continue - if (group.declaration.kind === 'const') { output.update( group.declaration.start, From 3ecc96742e1b7f2c74a911d3db523c156cb230c6 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:30:46 +0900 Subject: [PATCH 4/5] docs(rsc): note export metadata cleanup Co-authored-by: OpenCode --- packages/plugin-rsc/src/transforms/module-export-scan.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/plugin-rsc/src/transforms/module-export-scan.ts b/packages/plugin-rsc/src/transforms/module-export-scan.ts index 73f506c3b..ce7b9dbea 100644 --- a/packages/plugin-rsc/src/transforms/module-export-scan.ts +++ b/packages/plugin-rsc/src/transforms/module-export-scan.ts @@ -25,6 +25,8 @@ export type ModuleExportMeta = { * - `undefined` for export specifiers and re-exports. */ valueNode?: Node | ExportDefaultDeclaration['declaration'] + // TODO: followings are used only for internal `transformRscCssExport`. + // should probably simplify to use `valueNode` directly and remove these. /** * The local declaration name when statically available. * From f224ca4432871419b13a8461132d53f3beb3e5c0 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:32:00 +0900 Subject: [PATCH 5/5] docs(rsc): explain callable cache export filter Co-authored-by: OpenCode --- .../examples/use-cache-callable/callable-cache-plugin.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/plugin-rsc/examples/use-cache-callable/callable-cache-plugin.ts b/packages/plugin-rsc/examples/use-cache-callable/callable-cache-plugin.ts index b2b6da65e..a9f4728f7 100644 --- a/packages/plugin-rsc/examples/use-cache-callable/callable-cache-plugin.ts +++ b/packages/plugin-rsc/examples/use-cache-callable/callable-cache-plugin.ts @@ -37,8 +37,10 @@ export function callableCachePlugin(): Plugin { const result = hasDirective(ast.body, directive) ? transformWrapExport(code, ast, { runtime, - // Next.js permits object and array exports for metadata and - // viewport values in "use cache" modules. + // Next.js calls rejecting primitive literals while permitting + // objects and arrays arbitrary, but keeps the latter for metadata + // and viewport exports. + // https://github.com/vercel/next.js/blob/aae4179ac628e55483b62cd023a7e1827dcef122/crates/next-custom-transforms/src/transforms/server_actions.rs#L1914-L1919 filter: (_name, meta) => meta.valueNode?.type !== 'ObjectExpression' && meta.valueNode?.type !== 'ArrayExpression',