feat(next): revise server component error message (#65468)

### What

Coming from internal feedback:
https://vercel.slack.com/archives/C046HAU4H7F/p1714858224393659
This commit is contained in:
OJ Kwon 2024-05-08 12:39:37 -07:00 committed by GitHub
parent a7ebbdef7e
commit 531348d864
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 65 additions and 49 deletions

View file

@ -175,7 +175,7 @@ jobs:
- run: turbo run get-test-timings -- --build ${{ github.sha }}
- run: /bin/bash -c "${{ inputs.afterBuild }}"
timeout-minutes: 15
timeout-minutes: 30
- name: Upload artifact
uses: actions/upload-artifact@v4

View file

@ -257,9 +257,9 @@ fn report_error(app_dir: &Option<PathBuf>, filepath: &str, error_kind: RSCErrorK
.unwrap_or_default();
let msg = if !is_app_dir {
format!("You're importing a component that needs {}. That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/getting-started/react-essentials#server-components\n\n", source)
format!("You're importing a component that needs \"{}\". That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/getting-started/react-essentials#server-components\n\n", source)
} else {
format!("You're importing a component that needs {}. That only works in a Server Component but one of its parents is marked with \"use client\", so it's a Client Component.\nLearn more: https://nextjs.org/docs/getting-started/react-essentials\n\n", source)
format!("You're importing a component that needs \"{}\". That only works in a Server Component but one of its parents is marked with \"use client\", so it's a Client Component.\nLearn more: https://nextjs.org/docs/getting-started/react-essentials\n\n", source)
};
(msg, span)
}
@ -267,7 +267,7 @@ fn report_error(app_dir: &Option<PathBuf>, filepath: &str, error_kind: RSCErrorK
let msg = if source == "Component" {
"Youre importing a class component. It only works in a Client Component but none of its parents are marked with \"use client\", so they're Server Components by default.\nLearn more: https://nextjs.org/docs/getting-started/react-essentials#client-components\n\n".to_string()
} else {
format!("You're importing a component that needs {}. It only works in a Client Component but none of its parents are marked with \"use client\", so they're Server Components by default.\nLearn more: https://nextjs.org/docs/getting-started/react-essentials\n\n", source)
format!("You're importing a component that needs `{}`. This React hook only works in a client component. To fix, mark the file (or its parent) with the `\"use client\"` directive.\n\n Learn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components\n\n", source)
};
(msg,span)

View file

@ -1,5 +1,5 @@
x You're importing a component that needs server-only. That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/getting-started/
x You're importing a component that needs "server-only". That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/getting-started/
| react-essentials#server-components
|
|

View file

@ -1,6 +1,7 @@
x You're importing a component that needs useState. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
x You're importing a component that needs `useState`. This React hook only works in a client component. To fix, mark the file (or its parent) with the `"use client"` directive.
|
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components
|
|
,-[input.js:1:1]
@ -8,8 +9,9 @@
: ^^^^^^^^
`----
x You're importing a component that needs createContext. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
x You're importing a component that needs `createContext`. This React hook only works in a client component. To fix, mark the file (or its parent) with the `"use client"` directive.
|
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components
|
|
,-[input.js:2:1]
@ -18,8 +20,9 @@
: ^^^^^^^^^^^^^
`----
x You're importing a component that needs useEffect. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
x You're importing a component that needs `useEffect`. This React hook only works in a client component. To fix, mark the file (or its parent) with the `"use client"` directive.
|
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components
|
|
,-[input.js:4:1]
@ -28,8 +31,9 @@
: ^^^^^^^^^
`----
x You're importing a component that needs useImperativeHandle. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
x You're importing a component that needs `useImperativeHandle`. This React hook only works in a client component. To fix, mark the file (or its parent) with the `"use client"` directive.
|
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components
|
|
,-[input.js:4:1]
@ -49,8 +53,9 @@
9 | createFactory,
`----
x You're importing a component that needs createFactory. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
x You're importing a component that needs `createFactory`. This React hook only works in a client component. To fix, mark the file (or its parent) with the `"use client"` directive.
|
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components
|
|
,-[input.js:8:1]
@ -60,8 +65,9 @@
10 | PureComponent,
`----
x You're importing a component that needs PureComponent. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
x You're importing a component that needs `PureComponent`. This React hook only works in a client component. To fix, mark the file (or its parent) with the `"use client"` directive.
|
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components
|
|
,-[input.js:9:1]
@ -71,8 +77,9 @@
11 | useDeferredValue,
`----
x You're importing a component that needs useDeferredValue. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
x You're importing a component that needs `useDeferredValue`. This React hook only works in a client component. To fix, mark the file (or its parent) with the `"use client"` directive.
|
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components
|
|
,-[input.js:10:1]
@ -82,8 +89,9 @@
12 | useInsertionEffect,
`----
x You're importing a component that needs useInsertionEffect. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
x You're importing a component that needs `useInsertionEffect`. This React hook only works in a client component. To fix, mark the file (or its parent) with the `"use client"` directive.
|
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components
|
|
,-[input.js:11:1]
@ -93,8 +101,9 @@
13 | useLayoutEffect,
`----
x You're importing a component that needs useLayoutEffect. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
x You're importing a component that needs `useLayoutEffect`. This React hook only works in a client component. To fix, mark the file (or its parent) with the `"use client"` directive.
|
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components
|
|
,-[input.js:12:1]
@ -104,8 +113,9 @@
14 | useReducer,
`----
x You're importing a component that needs useReducer. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
x You're importing a component that needs `useReducer`. This React hook only works in a client component. To fix, mark the file (or its parent) with the `"use client"` directive.
|
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components
|
|
,-[input.js:13:1]
@ -115,8 +125,9 @@
15 | useRef,
`----
x You're importing a component that needs useRef. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
x You're importing a component that needs `useRef`. This React hook only works in a client component. To fix, mark the file (or its parent) with the `"use client"` directive.
|
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components
|
|
,-[input.js:14:1]
@ -126,8 +137,9 @@
16 | useSyncExternalStore,
`----
x You're importing a component that needs useSyncExternalStore. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
x You're importing a component that needs `useSyncExternalStore`. This React hook only works in a client component. To fix, mark the file (or its parent) with the `"use client"` directive.
|
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components
|
|
,-[input.js:15:1]

View file

@ -1,6 +1,7 @@
x You're importing a component that needs flushSync. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
x You're importing a component that needs `flushSync`. This React hook only works in a client component. To fix, mark the file (or its parent) with the `"use client"` directive.
|
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components
|
|
,-[input.js:1:1]
@ -8,9 +9,9 @@
: ^^^^^^^^^
`----
x You're importing a component that needs unstable_batchedUpdates. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by
| default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
x You're importing a component that needs `unstable_batchedUpdates`. This React hook only works in a client component. To fix, mark the file (or its parent) with the `"use client"` directive.
|
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components
|
|
,-[input.js:1:1]
@ -18,8 +19,9 @@
: ^^^^^^^^^^^^^^^^^^^^^^^
`----
x You're importing a component that needs useActionState. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
x You're importing a component that needs `useActionState`. This React hook only works in a client component. To fix, mark the file (or its parent) with the `"use client"` directive.
|
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components
|
|
,-[input.js:2:1]
@ -28,8 +30,9 @@
: ^^^^^^^^^^^^^^
`----
x You're importing a component that needs useFormStatus. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
x You're importing a component that needs `useFormStatus`. This React hook only works in a client component. To fix, mark the file (or its parent) with the `"use client"` directive.
|
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components
|
|
,-[input.js:4:1]
@ -38,8 +41,9 @@
: ^^^^^^^^^^^^^
`----
x You're importing a component that needs useFormState. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.
| Learn more: https://nextjs.org/docs/getting-started/react-essentials
x You're importing a component that needs `useFormState`. This React hook only works in a client component. To fix, mark the file (or its parent) with the `"use client"` directive.
|
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components
|
|
,-[input.js:4:1]

View file

@ -34,7 +34,7 @@ function formatRSCErrorMessage(
} else {
formattedMessage = message.replace(
NEXT_RSC_ERR_REACT_API,
`\n\nYou're importing a component that needs $1. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.\nLearn more: https://nextjs.org/docs/getting-started/react-essentials\n\n`
`\n\nYou're importing a component that needs $1. This React hook only works in a client component. To fix, mark the file (or its parent) with the \`"use client"\` directive. \n\nLearn more: https://nextjs.org/docs/app/building-your-application/rendering/client-components\n\n`
)
}
formattedVerboseMessage =

View file

@ -277,7 +277,7 @@ describe('Error overlay - RSC build errors', () => {
// `Component` has a custom error message
api === 'Component'
? `Youre importing a class component. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.`
: `You're importing a component that needs ${api}. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default.`
: `You're importing a component that needs \`${api}\`. This React hook only works in a client component. To fix, mark the file (or its parent) with the \`"use client"\` directive.`
)
await cleanup()
@ -300,7 +300,7 @@ describe('Error overlay - RSC build errors', () => {
expect(await session.hasRedbox()).toBe(true)
expect(await session.getRedboxSource()).toInclude(
`You're importing a component that needs ${api}. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components`
`You're importing a component that needs \`${api}\`. This React hook only works in a client component. To fix, mark the file (or its parent) with the \`"use client"\` directive.`
)
await cleanup()
@ -326,7 +326,7 @@ describe('Error overlay - RSC build errors', () => {
expect(await session.hasRedbox()).toBe(true)
expect(await session.getRedboxSource()).toInclude(
`You're importing a component that needs server-only. That only works in a Server Component but one of its parents is marked with "use client", so it's a Client Component.`
`You're importing a component that needs "server-only". That only works in a Server Component but one of its parents is marked with "use client", so it's a Client Component.`
)
await cleanup()

View file

@ -550,7 +550,7 @@ describe('Error Overlay for server components', () => {
// So we need to check for the first part of the message.
const normalizedSource = await session.getRedboxSource()
expect(normalizedSource).toContain(
`You're importing a component that needs ${hook}. It only works in a Client Component but none of its parents are marked with "use client"`
`You're importing a component that needs \`${hook}\`. This React hook only works in a client component. To fix, mark the file (or its parent) with the \`"use client"\` directive.`
)
expect(normalizedSource).toContain(
`import { ${hook} } from 'next/navigation'`

View file

@ -65,14 +65,14 @@ describe('Error Overlay for server components compiler errors in pages', () => {
3 | export default function Page() {
4 | return <p>hello world</p>
You're importing a component that needs next/headers. That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/getting-started/react-essentials#server-components"
You're importing a component that needs "next/headers". That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/getting-started/react-essentials#server-components"
`)
} else {
expect(next.normalizeTestDirContent(await session.getRedboxSource()))
.toMatchInlineSnapshot(`
"./components/Comp.js
Error:
x You're importing a component that needs next/headers. That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/getting-started/
x You're importing a component that needs "next/headers". That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/getting-started/
| react-essentials#server-components
|
|
@ -124,14 +124,14 @@ describe('Error Overlay for server components compiler errors in pages', () => {
3 | export default function Page() {
4 | return 'hello world'
You're importing a component that needs server-only. That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/getting-started/react-essentials#server-components"
You're importing a component that needs "server-only". That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/getting-started/react-essentials#server-components"
`)
} else {
expect(next.normalizeTestDirContent(await session.getRedboxSource()))
.toMatchInlineSnapshot(`
"./components/Comp.js
Error:
x You're importing a component that needs server-only. That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/getting-started/
x You're importing a component that needs "server-only". That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/getting-started/
| react-essentials#server-components
|
|