-
-
Notifications
You must be signed in to change notification settings - Fork 129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
test: edge cases for server action #745
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. |
90a553c
to
40aff57
Compare
8fe2383
to
e1b49ca
Compare
e1b49ca
to
355e558
Compare
@@ -188,7 +198,46 @@ export async function renderRsc( | |||
) { | |||
// XXX This doesn't support streaming unlike busboy | |||
const formData = parseFormData(bodyStr, contentType); | |||
args = await decodeReply(formData); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are something wrong, formData could include server references which need webpack runtime @dai-shi
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright. It's reasonable that it requires the second argument moduleMap
.
const moduleMap = new Proxy({} as Record<string, ImportManifestEntry>, { | ||
get(_target, rsfId: string): ImportManifestEntry { | ||
const [fileId, name] = rsfId.split('#') as [string, string]; | ||
// fixme: race condition, server actions are not initialized in the first time |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As said, I think we should initialize all server actions before running RSC otherwise it will crash for the first time
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for next.js, they have a global server action map in the runtime
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still need to understand the issue, but it feels like we want lazy evaluation.
.then((m: any) => { | ||
moduleCache.set( | ||
fileId, | ||
Object.fromEntries(m['__waku_serverActions']), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks ugly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah... I hope we can improve it.
} | ||
return { | ||
id: fileId, | ||
chunks: [], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
chunks: [fileId]
is not necessary after reading the source code fo next.js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure? I mean waku's approach might be different from nextjs's one.
@@ -5,10 +5,25 @@ import { EXTENSIONS } from '../config.js'; | |||
import { extname } from '../utils/path.js'; | |||
import { parseOpts } from '../utils/swc.js'; | |||
|
|||
const collectExportNames = (mod: swc.Module) => { | |||
const collectExportNames = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here are other issues:
- non export sever action could cause runtime error
- nested server action could cause runtime error
const actionsMap = {
foo: () => {
"use server"
// ...
}
}
Those are exist in vercel/ai/rsc source so we need to support them
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I missed such use cases.
This pattern, inline "use server", is only allowed in the server files, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I made a test case #771, but it looks like it's transforming well.
Can you give me a failing case?
This is also tried:
const actionsMap = createActions({
foo: () => {
"use server"
// ...
}
})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR little mess up, but should separate into three parts:
- Test some RSC edge cases
- RSC transform plugin
- RSC module map resolution
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a good improvement.
If possible, we would like to split into multiple PRs and merge gradually.
(And, we definitely need unit tests for transformation. 😓 )
@@ -5,10 +5,25 @@ import { EXTENSIONS } from '../config.js'; | |||
import { extname } from '../utils/path.js'; | |||
import { parseOpts } from '../utils/swc.js'; | |||
|
|||
const collectExportNames = (mod: swc.Module) => { | |||
const collectExportNames = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I missed such use cases.
This pattern, inline "use server", is only allowed in the server files, right?
newCode += ` | ||
export ${name === 'default' ? name : `const ${name} =`} registerClientReference(() => { throw new Error('It is not possible to invoke a client function from the server: ${getClientId(id)}#${name}'); }, '${getClientId(id)}', '${name}'); | ||
`; | ||
} | ||
return newCode; | ||
} else if (hasUseServer) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we can remove this branch. It's for another use case unless I'm mistaken.
newCode += ` | ||
if (typeof ${name} === 'function') { | ||
registerServerReference(${name}, '${getServerId(id)}', '${name}'); | ||
} | ||
`; | ||
if (internal.has(name)) { | ||
newCode += `export { ${name} };\n`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't feel correct. Possible name collisions.
} | ||
> | ||
>, | ||
{} as Record<string, Record<string, ImportManifestEntry>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, but maybe we should have a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -12,6 +12,16 @@ import { parseFormData } from '../utils/form.js'; | |||
import { streamToString } from '../utils/stream.js'; | |||
import { decodeActionId } from '../renderers/utils.js'; | |||
|
|||
// HACK for react-server-dom-webpack without webpack |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like we should finish up #707 before this PR.
@@ -188,7 +198,46 @@ export async function renderRsc( | |||
) { | |||
// XXX This doesn't support streaming unlike busboy | |||
const formData = parseFormData(bodyStr, contentType); | |||
args = await decodeReply(formData); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright. It's reasonable that it requires the second argument moduleMap
.
const moduleMap = new Proxy({} as Record<string, ImportManifestEntry>, { | ||
get(_target, rsfId: string): ImportManifestEntry { | ||
const [fileId, name] = rsfId.split('#') as [string, string]; | ||
// fixme: race condition, server actions are not initialized in the first time |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still need to understand the issue, but it feels like we want lazy evaluation.
.then((m: any) => { | ||
moduleCache.set( | ||
fileId, | ||
Object.fromEntries(m['__waku_serverActions']), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah... I hope we can improve it.
} | ||
return { | ||
id: fileId, | ||
chunks: [], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure? I mean waku's approach might be different from nextjs's one.
It depends on what's the real behavior from webpack, I need double check that |
I reverted the src code, but why do all e2e tests fail... should only a new one fail?? |
Fix some RSC case, will fix #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 <daishi@axlight.com>
…f65/20240612/actions # Conflicts: # e2e/fixtures/rsc-basic/package.json # pnpm-lock.yaml
Fix some RSC case, will fix dai-shi#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 <daishi@axlight.com>
…hi#779) Hmm, this is going to be a big problem with ESM/CJS compat... Maybe related: dai-shi#421, dai-shi#428
- downgrade pnpm
I was hoping to use `-rc.0` for a while, but it has a bug and `-rc.<num>` has not been maintained. Let's use nightly rc version. close dai-shi#766
working on |
… himself65/20240612/actions # Conflicts: # e2e/fixtures/rsc-basic/package.json # pnpm-lock.yaml
PR is to complex to rebease/merge, create a new one #785 |
I was trying to make vercel-ai + waku, but found some bugs, let me see if I can reproduce here