Repository: dai-shi/waku
Pull request #979 (2024-10-20 04:20:52)

fix(rsc-transform): fix server function error in SSR
I didn't notice this case until reported.

vercel[bot]: [vc]: #uNU9LGfRp0McoSTT5kErwI0yYW3KRddEFPOFucrWi8M=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJ3YWt1IiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL2RhaXNoaXMtcHJvamVjdHMvd2FrdS8zMkV4Y3g1dDRHWVA3ZEt1QUgyZ0ZyRVZ6elJ1IiwicHJldmlld1VybCI6Indha3UtZ2l0LWZpeC10cmFuc2Zvcm1zZXJ2ZXItZnVuYy1kYWlzaGlzLXByb2plY3RzLnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiUEVORElORyJ9XX0=
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Updated (UTC) |
| :--- | :----- | :------ | :------ |
| **waku** | 🔄 Building ([Inspect](https://vercel.com/daishis-projects/waku/32Excx5t4GYP7dKuAH2gFrEVzzRu)) | [Visit Preview](https://waku-git-fix-transformserver-func-daishis-projects.vercel.app) | Oct 20, 2024 4:20am |


codesandbox-ci[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/dai-shi/waku/pr/979/builds/552824) or the icon next to each commit SHA.

Patch:
diff --git a/packages/waku/src/lib/plugins/vite-plugin-rsc-transform.ts b/packages/waku/src/lib/plugins/vite-plugin-rsc-transform.ts
index 0f84d4e4..6cff763d 100644
--- a/packages/waku/src/lib/plugins/vite-plugin-rsc-transform.ts
+++ b/packages/waku/src/lib/plugins/vite-plugin-rsc-transform.ts
@@ -71,6 +71,39 @@ export ${name === 'default' ? name : `const ${name} =`} createServerReference('$
   }
 };
 
+const transformClientForSSR = (code: string, id: string) => {
+  if (!code.includes('use server')) {
+    return;
+  }
+  const ext = extname(id);
+  const mod = swc.parseSync(code, parseOpts(ext));
+  let hasUseServer = false;
+  for (const item of mod.body) {
+    if (item.type === 'ExpressionStatement') {
+      if (
+        item.expression.type === 'StringLiteral' &&
+        item.expression.value === 'use server'
+      ) {
+        hasUseServer = true;
+      }
+    } else {
+      break;
+    }
+  }
+  if (hasUseServer) {
+    const exportNames = collectExportNames(mod);
+    let newCode = '';
+    for (const name of exportNames) {
+      newCode += `
+export ${name === 'default' ? name : `const ${name} =`} () => {
+  throw new Error('You cannot call server functions during SSR');
+};
+`;
+    }
+    return newCode;
+  }
+};
+
 export const createEmptySpan = (): swc.Span =>
   ({
     start: 0,
@@ -627,7 +660,7 @@ export function rscTransformPlugin(
       }
       if (opts.isClient) {
         if (options?.ssr) {
-          return;
+          return transformClientForSSR(code, id);
         }
         return transformClient(
           code,


Test patch:
diff --git a/packages/waku/tests/vite-plugin-rsc-transform-internals.test.ts b/packages/waku/tests/vite-plugin-rsc-transform-internals.test.ts
index 2efd9ac1..9d8b48a1 100644
--- a/packages/waku/tests/vite-plugin-rsc-transform-internals.test.ts
+++ b/packages/waku/tests/vite-plugin-rsc-transform-internals.test.ts
@@ -61,7 +61,7 @@ export default function App() {
     const code = `
 'use server';
 
-const privateFunction = () => "Secret";
+const privateFunction = () => 'Secret';
 
 export const log = async (mesg) => {
   console.log(mesg);
@@ -78,7 +78,7 @@ export default async function() {
     expect(await transform(code, '/src/App.tsx', { ssr: true }))
       .toMatchInlineSnapshot(`
         "import { registerServerReference as __waku_registerServerReference } from 'react-server-dom-webpack/server.edge';
-        const privateFunction = ()=>"Secret";
+        const privateFunction = ()=>'Secret';
         export const log = __waku_registerServerReference(async (mesg)=>{
             console.log(mesg);
         }, "/src/App.tsx", "log");
@@ -370,7 +370,7 @@ export const log = (mesg) => {
     const code = `
 'use server';
 
-const privateFunction = () => "Secret";
+const privateFunction = () => 'Secret';
 
 // const function expression
 export const log1 = async function(mesg) {
@@ -407,4 +407,26 @@ export default async function log4(mesg) {
       "
     `);
   });
+
+  test('top-level use server for SSR', async () => {
+    const code = `
+'use server';
+
+import { getEnv } from 'waku';
+
+const privateFunction = () => getEnv('SECRET');
+
+export async function log(mesg) {
+  console.log(mesg);
+}
+`;
+    expect(await transform(code, '/src/func.ts', { ssr: true }))
+      .toMatchInlineSnapshot(`
+      "
+      export const log = () => {
+        throw new Error('You cannot call server functions during SSR');
+      };
+      "
+    `);
+  });
 });