Skip to content

Commit 1479a12

Browse files
himself65dai-shi
andauthored
fix: server action cases (wakujs#774)
Fix some RSC case, will fix wakujs#745. In the future, I will migrate the logic to https://github.com/himself65/react-server and keep same output Some issues still happening: 1. `export default async function () {"use server"}` cannot be RSCA 2. `export function a() { "use server" }` should throw error, because it's not async --------- Co-authored-by: daishi <[email protected]>
1 parent 7cead1e commit 1479a12

File tree

2 files changed

+229
-109
lines changed

2 files changed

+229
-109
lines changed

packages/waku/src/lib/plugins/vite-plugin-rsc-transform.ts

+112-89
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,18 @@ const createCallExpression = (
9191
span: { start: 0, end: 0, ctxt: 0 },
9292
});
9393

94-
const serverActionsInitCode = swc.parseSync(`
94+
const serverInitCode = swc.parseSync(`
9595
import { registerServerReference as __waku_registerServerReference } from 'react-server-dom-webpack/server.edge';
9696
`).body;
9797

98+
const findLastImportIndex = (mod: swc.Module) => {
99+
const lastImportIndex = mod.body.findIndex(
100+
(node) =>
101+
node.type !== 'ExpressionStatement' && node.type !== 'ImportDeclaration',
102+
);
103+
return lastImportIndex === -1 ? 0 : lastImportIndex;
104+
};
105+
98106
type FunctionWithBlockBody = (
99107
| swc.FunctionDeclaration
100108
| swc.FunctionExpression
@@ -323,62 +331,59 @@ const transformServerActions = (
323331
if (!serverActionIndex) {
324332
return;
325333
}
326-
const serverActionsCode = [...serverActionsInitCode];
327-
for (const [actionIndex, [actionFn, closureVars]] of serverActions) {
328-
if (actionFn.type === 'FunctionDeclaration') {
329-
const stmt1: swc.ExportDeclaration = {
330-
type: 'ExportDeclaration',
331-
declaration: prependArgsToFn(actionFn, closureVars),
332-
span: { start: 0, end: 0, ctxt: 0 },
333-
};
334-
const stmt2: swc.ExpressionStatement = {
335-
type: 'ExpressionStatement',
336-
expression: createCallExpression(
337-
createIdentifier('__waku_registerServerReference'),
338-
[
339-
createIdentifier(actionFn.identifier.value),
340-
createStringLiteral(getActionId()),
341-
createStringLiteral('__waku_action' + actionIndex),
342-
],
343-
),
344-
span: { start: 0, end: 0, ctxt: 0 },
345-
};
346-
serverActionsCode.push(stmt1, stmt2);
347-
} else {
348-
const stmt: swc.ExportDeclaration = {
349-
type: 'ExportDeclaration',
350-
declaration: {
351-
type: 'VariableDeclaration',
352-
kind: 'const',
353-
declare: false,
354-
declarations: [
355-
{
356-
type: 'VariableDeclarator',
357-
id: createIdentifier('__waku_action' + actionIndex),
358-
init: createCallExpression(
359-
createIdentifier('__waku_registerServerReference'),
360-
[
361-
prependArgsToFn(actionFn, closureVars),
362-
createStringLiteral(getActionId()),
363-
createStringLiteral('__waku_action' + actionIndex),
364-
],
365-
),
366-
definite: false,
367-
span: { start: 0, end: 0, ctxt: 0 },
368-
},
369-
],
334+
const serverActionsCode = Array.from(serverActions).flatMap(
335+
([actionIndex, [actionFn, closureVars]]) => {
336+
if (actionFn.type === 'FunctionDeclaration') {
337+
const stmt1: swc.ExportDeclaration = {
338+
type: 'ExportDeclaration',
339+
declaration: prependArgsToFn(actionFn, closureVars),
370340
span: { start: 0, end: 0, ctxt: 0 },
371-
},
372-
span: { start: 0, end: 0, ctxt: 0 },
373-
};
374-
serverActionsCode.push(stmt);
375-
}
376-
}
377-
const lastImportIndex = mod.body.findIndex(
378-
(node) =>
379-
node.type !== 'ExpressionStatement' && node.type !== 'ImportDeclaration',
341+
};
342+
const stmt2: swc.ExpressionStatement = {
343+
type: 'ExpressionStatement',
344+
expression: createCallExpression(
345+
createIdentifier('__waku_registerServerReference'),
346+
[
347+
createIdentifier(actionFn.identifier.value),
348+
createStringLiteral(getActionId()),
349+
createStringLiteral('__waku_action' + actionIndex),
350+
],
351+
),
352+
span: { start: 0, end: 0, ctxt: 0 },
353+
};
354+
return [stmt1, stmt2];
355+
} else {
356+
const stmt: swc.ExportDeclaration = {
357+
type: 'ExportDeclaration',
358+
declaration: {
359+
type: 'VariableDeclaration',
360+
kind: 'const',
361+
declare: false,
362+
declarations: [
363+
{
364+
type: 'VariableDeclarator',
365+
id: createIdentifier('__waku_action' + actionIndex),
366+
init: createCallExpression(
367+
createIdentifier('__waku_registerServerReference'),
368+
[
369+
prependArgsToFn(actionFn, closureVars),
370+
createStringLiteral(getActionId()),
371+
createStringLiteral('__waku_action' + actionIndex),
372+
],
373+
),
374+
definite: false,
375+
span: { start: 0, end: 0, ctxt: 0 },
376+
},
377+
],
378+
span: { start: 0, end: 0, ctxt: 0 },
379+
},
380+
span: { start: 0, end: 0, ctxt: 0 },
381+
};
382+
return [stmt];
383+
}
384+
},
380385
);
381-
mod.body.splice(lastImportIndex, 0, ...serverActionsCode);
386+
mod.body.splice(findLastImportIndex(mod), 0, ...serverActionsCode);
382387
return mod;
383388
};
384389

@@ -391,14 +396,18 @@ const transformServer = (
391396
const ext = extname(id);
392397
const mod = swc.parseSync(code, parseOpts(ext));
393398
let hasUseClient = false;
394-
let hasUseServer: swc.ExpressionStatement | undefined;
395-
for (const item of mod.body) {
399+
let hasUseServer = false;
400+
for (let i = 0; i < mod.body.length; ++i) {
401+
const item = mod.body[i]!;
396402
if (item.type === 'ExpressionStatement') {
397403
if (item.expression.type === 'StringLiteral') {
398404
if (item.expression.value === 'use client') {
399405
hasUseClient = true;
406+
break;
400407
} else if (item.expression.value === 'use server') {
401-
hasUseServer = item;
408+
hasUseServer = true;
409+
mod.body.splice(i, 1); // remove this directive
410+
break;
402411
}
403412
}
404413
} else {
@@ -417,42 +426,56 @@ export ${name === 'default' ? name : `const ${name} =`} registerClientReference(
417426
`;
418427
}
419428
return newCode;
420-
} else if (hasUseServer) {
421-
const useServerPosStart = hasUseServer.span.start - mod.span.start;
422-
const useServerPosEnd = hasUseServer.span.end - mod.span.start;
423-
const lastImportIndex = mod.body.findIndex(
424-
(node) =>
425-
node.type !== 'ExpressionStatement' &&
426-
node.type !== 'ImportDeclaration',
427-
);
428-
let lastImportPos =
429-
lastImportIndex === -1 ? 0 : mod.body[lastImportIndex]!.span.end;
430-
if (lastImportIndex < useServerPosEnd) {
431-
lastImportPos = useServerPosEnd;
432-
}
429+
}
430+
if (hasUseServer) {
433431
const exportNames = collectExportNames(mod);
434-
const newCode = [
435-
code.slice(0, useServerPosStart),
436-
code.slice(useServerPosEnd, lastImportPos),
437-
`
438-
import { registerServerReference as __waku_registerServerReference } from 'react-server-dom-webpack/server.edge';
439-
`,
440-
code.slice(lastImportPos),
441-
...[...exportNames].map(
442-
(name) => `
443-
if (typeof ${name} === 'function') {
444-
__waku_registerServerReference(${name}, '${getServerId(id)}', '${name}');
445-
}
446-
`,
447-
),
448-
].join('');
449-
return newCode;
432+
const serverActionsCode = Array.from(exportNames).map((name) => {
433+
const blockStmt: swc.BlockStatement = {
434+
type: 'BlockStatement',
435+
stmts: [
436+
{
437+
type: 'ExpressionStatement',
438+
expression: createCallExpression(
439+
createIdentifier('__waku_registerServerReference'),
440+
[
441+
createIdentifier(name),
442+
createStringLiteral(getServerId(id)),
443+
createStringLiteral(name),
444+
],
445+
),
446+
span: { start: 0, end: 0, ctxt: 0 },
447+
},
448+
],
449+
span: { start: 0, end: 0, ctxt: 0 },
450+
};
451+
const ifStmt: swc.IfStatement = {
452+
type: 'IfStatement',
453+
test: {
454+
type: 'BinaryExpression',
455+
operator: '===',
456+
left: {
457+
type: 'UnaryExpression',
458+
operator: 'typeof',
459+
argument: createIdentifier(name),
460+
span: { start: 0, end: 0, ctxt: 0 },
461+
},
462+
right: createStringLiteral('function'),
463+
span: { start: 0, end: 0, ctxt: 0 },
464+
},
465+
consequent: blockStmt,
466+
span: { start: 0, end: 0, ctxt: 0 },
467+
};
468+
return ifStmt;
469+
});
470+
mod.body.push(...serverActionsCode);
450471
}
451472
// transform server actions in server components
452473
const newMod =
453-
code.includes('use server') &&
454-
transformServerActions(mod, () => getServerId(id));
474+
(code.includes('use server') &&
475+
transformServerActions(mod, () => getServerId(id))) ||
476+
(hasUseServer && mod);
455477
if (newMod) {
478+
newMod.body.splice(findLastImportIndex(newMod), 0, ...serverInitCode);
456479
const newCode = swc.printSync(newMod).code;
457480
return newCode;
458481
}

0 commit comments

Comments
 (0)