-
Notifications
You must be signed in to change notification settings - Fork 27.8k
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
Show a proper error if a server function's bound args cannot be serialized #73471
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
e5758d2
Log error if bound args fail to be encrypted
unstubbable 548ed94
Collect function declarations that server functions close over
unstubbable a2f1109
Use `ExprFactory` helper functions
unstubbable 46a7b23
Handle unknown errors in generated `catch`
unstubbable e020237
Re-throw error
unstubbable 55ba81f
Re-throw newly created error
unstubbable d767416
Revert changes in `bind_args_to_ref_expr`
unstubbable 4f40676
Handle errors when bound args cannot be serialized
unstubbable 6285e46
Add a unit test for closed-over functions
unstubbable ee3bfae
Add clarifying comments
unstubbable 1fbeaa7
Show an aggregated error
unstubbable a8327e0
Revert "Show an aggregated error"
unstubbable 5368118
Only report one server function error at a time, starting with the first
unstubbable File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
crates/next-custom-transforms/tests/fixture/server-actions/server/58/input.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
function createCachedFn(start) { | ||
function fn() { | ||
return start + Math.random() | ||
} | ||
|
||
return async () => { | ||
'use cache' | ||
return fn() | ||
} | ||
} | ||
|
||
function createServerAction(start) { | ||
function fn() { | ||
return start + Math.random() | ||
} | ||
|
||
return async () => { | ||
'use server' | ||
console.log(fn()) | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
crates/next-custom-transforms/tests/fixture/server-actions/server/58/output.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/* __next_internal_action_entry_do_not_use__ {"401c36b06e398c97abe5d5d7ae8c672bfddf4e1b91":"$$RSC_SERVER_ACTION_2","c03128060c414d59f8552e4788b846c0d2b7f74743":"$$RSC_SERVER_CACHE_0"} */ import { registerServerReference } from "private-next-rsc-server-reference"; | ||
import { encryptActionBoundArgs, decryptActionBoundArgs } from "private-next-rsc-action-encryption"; | ||
import { cache as $$cache__ } from "private-next-rsc-cache-wrapper"; | ||
export var $$RSC_SERVER_CACHE_0 = $$cache__("default", "c03128060c414d59f8552e4788b846c0d2b7f74743", 1, async function([$$ACTION_ARG_0]) { | ||
return $$ACTION_ARG_0(); | ||
}); | ||
function createCachedFn(start) { | ||
function fn() { | ||
return start + Math.random(); | ||
} | ||
return $$RSC_SERVER_REF_1.bind(null, encryptActionBoundArgs("c03128060c414d59f8552e4788b846c0d2b7f74743", [ | ||
fn | ||
])); | ||
} | ||
var $$RSC_SERVER_REF_1 = /*#__TURBOPACK_DISABLE_EXPORT_MERGING__*/ registerServerReference($$RSC_SERVER_CACHE_0, "c03128060c414d59f8552e4788b846c0d2b7f74743", null); | ||
export const /*#__TURBOPACK_DISABLE_EXPORT_MERGING__*/ $$RSC_SERVER_ACTION_2 = async function($$ACTION_CLOSURE_BOUND) { | ||
var [$$ACTION_ARG_0] = await decryptActionBoundArgs("401c36b06e398c97abe5d5d7ae8c672bfddf4e1b91", $$ACTION_CLOSURE_BOUND); | ||
console.log($$ACTION_ARG_0()); | ||
}; | ||
function createServerAction(start) { | ||
function fn() { | ||
return start + Math.random(); | ||
} | ||
return registerServerReference($$RSC_SERVER_ACTION_2, "401c36b06e398c97abe5d5d7ae8c672bfddf4e1b91", null).bind(null, encryptActionBoundArgs("401c36b06e398c97abe5d5d7ae8c672bfddf4e1b91", [ | ||
fn | ||
])); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
test/e2e/app-dir/use-cache-close-over-function/app/client/client.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
'use client' | ||
|
||
import { useActionState } from 'react' | ||
|
||
export function Client({ getValue }) { | ||
const [result, formAction] = useActionState(getValue, 0) | ||
|
||
return ( | ||
<form action={formAction}> | ||
<p>{result}</p> | ||
<button>Submit</button> | ||
</form> | ||
) | ||
} |
16 changes: 16 additions & 0 deletions
16
test/e2e/app-dir/use-cache-close-over-function/app/client/page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { Client } from './client' | ||
|
||
function createCachedFn(start: number) { | ||
function fn() { | ||
return start | ||
} | ||
|
||
return async () => { | ||
'use cache' | ||
return Math.random() + fn() | ||
} | ||
} | ||
|
||
export default async function Page() { | ||
return <Client getValue={createCachedFn(42)} /> | ||
} |
11 changes: 11 additions & 0 deletions
11
test/e2e/app-dir/use-cache-close-over-function/app/layout.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { Suspense } from 'react' | ||
|
||
export default function Root({ children }: { children: React.ReactNode }) { | ||
return ( | ||
<html> | ||
<body> | ||
<Suspense>{children}</Suspense> | ||
</body> | ||
</html> | ||
) | ||
} |
14 changes: 14 additions & 0 deletions
14
test/e2e/app-dir/use-cache-close-over-function/app/page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import Link from 'next/link' | ||
|
||
export default function Page() { | ||
return ( | ||
<> | ||
<p> | ||
<Link href="/client">Client</Link> | ||
</p> | ||
<p> | ||
<Link href="/server">Server</Link> | ||
</p> | ||
</> | ||
) | ||
} |
16 changes: 16 additions & 0 deletions
16
test/e2e/app-dir/use-cache-close-over-function/app/server/page.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
function createCachedFn(start: number) { | ||
function fn() { | ||
return start | ||
} | ||
|
||
return async () => { | ||
'use cache' | ||
return Math.random() + fn() | ||
} | ||
} | ||
|
||
const getCachedValue = createCachedFn(42) | ||
|
||
export default async function Page() { | ||
return <p>{getCachedValue()}</p> | ||
} |
11 changes: 11 additions & 0 deletions
11
test/e2e/app-dir/use-cache-close-over-function/next.config.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/** | ||
* @type {import('next').NextConfig} | ||
*/ | ||
const nextConfig = { | ||
experimental: { | ||
dynamicIO: true, | ||
serverSourceMaps: true, | ||
}, | ||
} | ||
|
||
module.exports = nextConfig |
88 changes: 88 additions & 0 deletions
88
test/e2e/app-dir/use-cache-close-over-function/use-cache-close-over-function.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import { nextTestSetup } from 'e2e-utils' | ||
import { | ||
assertHasRedbox, | ||
getRedboxDescription, | ||
getRedboxSource, | ||
openRedbox, | ||
} from 'next-test-utils' | ||
|
||
describe('use-cache-close-over-function', () => { | ||
const { next, isNextDev, skipped } = nextTestSetup({ | ||
files: __dirname, | ||
skipDeployment: true, | ||
skipStart: process.env.NEXT_TEST_MODE !== 'dev', | ||
}) | ||
|
||
if (skipped) { | ||
return | ||
} | ||
|
||
if (isNextDev) { | ||
it('should show an error toast for client-side usage', async () => { | ||
const browser = await next.browser('/client') | ||
|
||
await openRedbox(browser) | ||
|
||
const errorDescription = await getRedboxDescription(browser) | ||
const errorSource = await getRedboxSource(browser) | ||
|
||
expect(errorDescription).toMatchInlineSnapshot(` | ||
"[ Prerender ] Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server". Or maybe you meant to call this function rather than return it. | ||
[function fn] | ||
^^^^^^^^^^^" | ||
`) | ||
|
||
expect(errorSource).toMatchInlineSnapshot(` | ||
"app/client/page.tsx (8:3) @ createCachedFn | ||
|
||
6 | } | ||
7 | | ||
> 8 | return async () => { | ||
| ^ | ||
9 | 'use cache' | ||
10 | return Math.random() + fn() | ||
11 | }" | ||
`) | ||
}) | ||
|
||
it('should show the error overlay for server-side usage', async () => { | ||
const browser = await next.browser('/server') | ||
|
||
await assertHasRedbox(browser) | ||
|
||
const errorDescription = await getRedboxDescription(browser) | ||
const errorSource = await getRedboxSource(browser) | ||
|
||
expect(errorDescription).toMatchInlineSnapshot(` | ||
"[ Prerender ] Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server". Or maybe you meant to call this function rather than return it. | ||
[function fn] | ||
^^^^^^^^^^^" | ||
`) | ||
|
||
expect(errorSource).toMatchInlineSnapshot(` | ||
"app/server/page.tsx (6:3) @ createCachedFn | ||
|
||
4 | } | ||
5 | | ||
> 6 | return async () => { | ||
| ^ | ||
7 | 'use cache' | ||
8 | return Math.random() + fn() | ||
9 | }" | ||
`) | ||
}) | ||
} else { | ||
it('should fail the build with an error', async () => { | ||
const { cliOutput } = await next.build() | ||
|
||
expect(cliOutput).toInclude(` | ||
Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server". Or maybe you meant to call this function rather than return it. | ||
[function] | ||
^^^^^^^^`) | ||
|
||
expect(cliOutput).toMatch( | ||
/Error occurred prerendering page "\/(client|server)"/ | ||
) | ||
}) | ||
} | ||
}) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
nit: wouldn't a
"use cache"
function would also avoid this? i guess this message is coming from react which doesn't know about that directive, but maybe we need to postprocess the message to rephrase it?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, this message from React is a bit outdated. Not only regarding the directive, but also the "passed to Client Components" part. For
"use cache"
that's a server=>cache environment transition, and not the previously assumed server=>client transition.