mirror of
https://github.com/vercel/next.js.git
synced 2026-09-20 02:25:18 +08:00
9b8a7a1055
1. Align the code highlight marker color with the issue severity 2. Make the issue title colored 3. Prefix issues with `Warning` or `Error` <img width="1383" height="862" alt="Bildschirmfoto 2026-06-16 um 18 59 03" src="https://github.com/user-attachments/assets/f98c606d-4ea8-40c2-836d-e395f1df904c" /> <img width="1039" height="450" alt="Bildschirmfoto 2026-06-16 um 19 03 39" src="https://github.com/user-attachments/assets/2747f914-0587-4178-a87d-344f24715c96" /> <details> <summary>Old</summary> <img width="1267" height="745" alt="Bildschirmfoto 2026-06-16 um 17 04 35" src="https://github.com/user-attachments/assets/837739ff-82f7-4ea2-8767-ca7d866a8570" /> </details>
438 lines
14 KiB
TypeScript
438 lines
14 KiB
TypeScript
/* eslint-env jest */
|
||
import { nextTestSetup } from 'e2e-utils'
|
||
import { createSandbox } from 'development-sandbox'
|
||
import { outdent } from 'outdent'
|
||
|
||
const isRspack = process.env.NEXT_RSPACK !== undefined
|
||
|
||
const initialFiles = new Map([
|
||
['app/_.js', ''], // app dir need to exists, otherwise the SWC RSC checks will not run
|
||
[
|
||
'pages/index.js',
|
||
outdent`
|
||
import Comp from '../components/Comp'
|
||
|
||
export default function Page() { return <Comp /> }
|
||
`,
|
||
],
|
||
[
|
||
'components/Comp.js',
|
||
outdent`
|
||
export default function Comp() {
|
||
return <p>Hello world</p>
|
||
}
|
||
`,
|
||
],
|
||
])
|
||
|
||
describe('Error Overlay for server components compiler errors in pages', () => {
|
||
const { next } = nextTestSetup({
|
||
files: {},
|
||
skipStart: true,
|
||
})
|
||
|
||
test("importing 'next/headers' in pages", async () => {
|
||
await using sandbox = await createSandbox(next, initialFiles)
|
||
const { session } = sandbox
|
||
|
||
await session.patch(
|
||
'components/Comp.js',
|
||
outdent`
|
||
import { cookies } from 'next/headers'
|
||
|
||
export default function Page() {
|
||
return <p>hello world</p>
|
||
}
|
||
`
|
||
)
|
||
|
||
await session.waitForRedbox()
|
||
await expect(session.getRedboxSource()).resolves.toMatch(
|
||
/This API is only available in Server Components/
|
||
)
|
||
|
||
if (process.env.IS_TURBOPACK_TEST) {
|
||
expect(next.normalizeTestDirContent(await session.getRedboxSource()))
|
||
.toMatchInlineSnapshot(`
|
||
"./components/Comp.js (1:1)
|
||
Error: You're importing a module that depends on "next/headers". This API is only available in Server Components in the App Router, but you are using it in the Pages Router.
|
||
Learn more: https://nextjs.org/docs/app/building-your-application/rendering/server-components
|
||
> 1 | import { cookies } from 'next/headers'
|
||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
2 |
|
||
3 | export default function Page() {
|
||
4 | return <p>hello world</p>
|
||
|
||
Ecmascript file had an error
|
||
|
||
Import traces:
|
||
Browser:
|
||
./components/Comp.js
|
||
./pages/index.js
|
||
|
||
SSR:
|
||
./components/Comp.js
|
||
./pages/index.js"
|
||
`)
|
||
} else if (isRspack) {
|
||
expect(
|
||
takeUpToString(
|
||
next.normalizeTestDirContent(await session.getRedboxSource()),
|
||
'----'
|
||
)
|
||
).toMatchInlineSnapshot(`
|
||
"./components/Comp.js
|
||
╰─▶ × Error: x You're importing a module that depends on "next/headers". This API is only available in Server Components in the App Router, but you are using it in the Pages Router.
|
||
│ | Learn more: https://nextjs.org/docs/app/building-your-application/rendering/server-components
|
||
│ |
|
||
│
|
||
│ ,-[1:1]
|
||
│ 1 | import { cookies } from 'next/headers'
|
||
│ : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
│ 2 |
|
||
│ 3 | export default function Page() {
|
||
│ 4 | return <p>hello world</p>
|
||
│ \`----"
|
||
`)
|
||
} else {
|
||
expect(next.normalizeTestDirContent(await session.getRedboxSource()))
|
||
.toMatchInlineSnapshot(`
|
||
"./components/Comp.js
|
||
Error: x You're importing a module that depends on "next/headers". This API is only available in Server Components in the App Router, but you are using it in the Pages Router.
|
||
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/server-components
|
||
|
|
||
|
||
,-[1:1]
|
||
1 | import { cookies } from 'next/headers'
|
||
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
2 |
|
||
3 | export default function Page() {
|
||
4 | return <p>hello world</p>
|
||
\`----
|
||
|
||
Import trace for requested module:
|
||
./components/Comp.js
|
||
./pages/index.js"
|
||
`)
|
||
}
|
||
})
|
||
|
||
test("importing 'server-only' in pages", async () => {
|
||
await using sandbox = await createSandbox(next, initialFiles)
|
||
const { session } = sandbox
|
||
|
||
await next.patchFile(
|
||
'components/Comp.js',
|
||
outdent`
|
||
import 'server-only'
|
||
|
||
export default function Page() {
|
||
return 'hello world'
|
||
}
|
||
`
|
||
)
|
||
|
||
await session.waitForRedbox()
|
||
await expect(session.getRedboxSource()).resolves.toMatch(
|
||
/This API is only available in Server Components/
|
||
)
|
||
|
||
if (process.env.IS_TURBOPACK_TEST) {
|
||
expect(next.normalizeTestDirContent(await session.getRedboxSource()))
|
||
.toMatchInlineSnapshot(`
|
||
"./components/Comp.js (1:1)
|
||
Error: You're importing a module that depends on "server-only". This API is only available in Server Components in the App Router, but you are using it in the Pages Router.
|
||
Learn more: https://nextjs.org/docs/app/building-your-application/rendering/server-components
|
||
> 1 | import 'server-only'
|
||
| ^^^^^^^^^^^^^^^^^^^^
|
||
2 |
|
||
3 | export default function Page() {
|
||
4 | return 'hello world'
|
||
|
||
Ecmascript file had an error
|
||
|
||
Import traces:
|
||
Browser:
|
||
./components/Comp.js
|
||
./pages/index.js
|
||
|
||
SSR:
|
||
./components/Comp.js
|
||
./pages/index.js"
|
||
`)
|
||
} else if (isRspack) {
|
||
expect(
|
||
takeUpToString(
|
||
next.normalizeTestDirContent(await session.getRedboxSource()),
|
||
'----'
|
||
)
|
||
).toMatchInlineSnapshot(`
|
||
"./components/Comp.js
|
||
╰─▶ × Error: x You're importing a module that depends on "server-only". This API is only available in Server Components in the App Router, but you are using it in the Pages Router.
|
||
│ | Learn more: https://nextjs.org/docs/app/building-your-application/rendering/server-components
|
||
│ |
|
||
│
|
||
│ ,-[1:1]
|
||
│ 1 | import 'server-only'
|
||
│ : ^^^^^^^^^^^^^^^^^^^^
|
||
│ 2 |
|
||
│ 3 | export default function Page() {
|
||
│ 4 | return 'hello world'
|
||
│ \`----"
|
||
`)
|
||
} else {
|
||
expect(
|
||
takeUpToString(
|
||
next.normalizeTestDirContent(await session.getRedboxSource()),
|
||
'Import trace for requested module:'
|
||
)
|
||
).toMatchInlineSnapshot(`
|
||
"./components/Comp.js
|
||
Error: x You're importing a module that depends on "server-only". This API is only available in Server Components in the App Router, but you are using it in the Pages Router.
|
||
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/server-components
|
||
|
|
||
|
||
,-[1:1]
|
||
1 | import 'server-only'
|
||
: ^^^^^^^^^^^^^^^^^^^^
|
||
2 |
|
||
3 | export default function Page() {
|
||
4 | return 'hello world'
|
||
\`----
|
||
|
||
Import trace for requested module:"
|
||
`)
|
||
}
|
||
})
|
||
|
||
test("importing after from 'next/server' in pages", async () => {
|
||
await using sandbox = await createSandbox(next, initialFiles)
|
||
const { session } = sandbox
|
||
|
||
await next.patchFile(
|
||
'components/Comp.js',
|
||
outdent`
|
||
import { after } from 'next/server'
|
||
|
||
export default function Page() {
|
||
return 'hello world'
|
||
}
|
||
`
|
||
)
|
||
|
||
await session.waitForRedbox()
|
||
await expect(session.getRedboxSource()).resolves.toMatch(
|
||
/This API is only available in Server Components/
|
||
)
|
||
|
||
if (process.env.IS_TURBOPACK_TEST) {
|
||
expect(next.normalizeTestDirContent(await session.getRedboxSource()))
|
||
.toMatchInlineSnapshot(`
|
||
"./components/Comp.js (1:10)
|
||
Error: You're importing a module that depends on "after". This API is only available in Server Components in the App Router, but you are using it in the Pages Router.
|
||
Learn more: https://nextjs.org/docs/app/building-your-application/rendering/server-components
|
||
> 1 | import { after } from 'next/server'
|
||
| ^^^^^
|
||
2 |
|
||
3 | export default function Page() {
|
||
4 | return 'hello world'
|
||
|
||
Ecmascript file had an error
|
||
|
||
Import traces:
|
||
Browser:
|
||
./components/Comp.js
|
||
./pages/index.js
|
||
|
||
SSR:
|
||
./components/Comp.js
|
||
./pages/index.js"
|
||
`)
|
||
} else if (isRspack) {
|
||
expect(
|
||
takeUpToString(
|
||
next.normalizeTestDirContent(await session.getRedboxSource()),
|
||
'----'
|
||
)
|
||
).toMatchInlineSnapshot(`
|
||
"./components/Comp.js
|
||
╰─▶ × Error: x You're importing a module that depends on "after". This API is only available in Server Components in the App Router, but you are using it in the Pages Router.
|
||
│ | Learn more: https://nextjs.org/docs/app/building-your-application/rendering/server-components
|
||
│ |
|
||
│
|
||
│ ,-[1:1]
|
||
│ 1 | import { after } from 'next/server'
|
||
│ : ^^^^^
|
||
│ 2 |
|
||
│ 3 | export default function Page() {
|
||
│ 4 | return 'hello world'
|
||
│ \`----"
|
||
`)
|
||
} else {
|
||
expect(
|
||
takeUpToString(
|
||
next.normalizeTestDirContent(await session.getRedboxSource()),
|
||
'Import trace for requested module:'
|
||
)
|
||
).toMatchInlineSnapshot(`
|
||
"./components/Comp.js
|
||
Error: x You're importing a module that depends on "after". This API is only available in Server Components in the App Router, but you are using it in the Pages Router.
|
||
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/server-components
|
||
|
|
||
|
||
,-[1:1]
|
||
1 | import { after } from 'next/server'
|
||
: ^^^^^
|
||
2 |
|
||
3 | export default function Page() {
|
||
4 | return 'hello world'
|
||
\`----
|
||
|
||
Import trace for requested module:"
|
||
`)
|
||
}
|
||
})
|
||
|
||
test("importing 'next/root-params' in pages", async () => {
|
||
const files = new Map([
|
||
...initialFiles,
|
||
[
|
||
'components/Comp.js',
|
||
outdent`
|
||
import { foo } from 'next/root-params'
|
||
|
||
export default function Page() {
|
||
return 'hello world'
|
||
}
|
||
`,
|
||
],
|
||
])
|
||
await using sandbox = await createSandbox(next, files)
|
||
const { session } = sandbox
|
||
|
||
await session.waitForRedbox()
|
||
await expect(session.getRedboxSource()).resolves.toMatch(
|
||
/This API is only available in Server Components/
|
||
)
|
||
|
||
if (process.env.IS_TURBOPACK_TEST) {
|
||
expect(next.normalizeTestDirContent(await session.getRedboxSource()))
|
||
.toMatchInlineSnapshot(`
|
||
"./components/Comp.js (1:1)
|
||
Error: You're importing a module that depends on "next/root-params". This API is only available in Server Components in the App Router, but you are using it in the Pages Router.
|
||
Learn more: https://nextjs.org/docs/app/building-your-application/rendering/server-components
|
||
> 1 | import { foo } from 'next/root-params'
|
||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
2 |
|
||
3 | export default function Page() {
|
||
4 | return 'hello world'
|
||
|
||
Ecmascript file had an error
|
||
|
||
Import traces:
|
||
Browser:
|
||
./components/Comp.js
|
||
./pages/index.js
|
||
|
||
SSR:
|
||
./components/Comp.js
|
||
./pages/index.js"
|
||
`)
|
||
} else if (isRspack) {
|
||
expect(
|
||
takeUpToString(
|
||
next.normalizeTestDirContent(await session.getRedboxSource()),
|
||
'----'
|
||
)
|
||
).toMatchInlineSnapshot(`
|
||
"./components/Comp.js
|
||
╰─▶ × Error: x You're importing a module that depends on "next/root-params". This API is only available in Server Components in the App Router, but you are using it in the Pages Router.
|
||
│ | Learn more: https://nextjs.org/docs/app/building-your-application/rendering/server-components
|
||
│ |
|
||
│
|
||
│ ,-[1:1]
|
||
│ 1 | import { foo } from 'next/root-params'
|
||
│ : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
│ 2 |
|
||
│ 3 | export default function Page() {
|
||
│ 4 | return 'hello world'
|
||
│ \`----"
|
||
`)
|
||
} else {
|
||
expect(
|
||
takeUpToString(
|
||
next.normalizeTestDirContent(await session.getRedboxSource()),
|
||
'Import trace for requested module:'
|
||
)
|
||
).toMatchInlineSnapshot(`
|
||
"./components/Comp.js
|
||
Error: x You're importing a module that depends on "next/root-params". This API is only available in Server Components in the App Router, but you are using it in the Pages Router.
|
||
| Learn more: https://nextjs.org/docs/app/building-your-application/rendering/server-components
|
||
|
|
||
|
||
,-[1:1]
|
||
1 | import { foo } from 'next/root-params'
|
||
: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
2 |
|
||
3 | export default function Page() {
|
||
4 | return 'hello world'
|
||
\`----"
|
||
`)
|
||
}
|
||
})
|
||
|
||
describe("importing 'next/cache' APIs in pages", () => {
|
||
test.each([
|
||
'revalidatePath',
|
||
'revalidateTag',
|
||
'cacheLife',
|
||
'cacheTag',
|
||
'revalidatePath',
|
||
'revalidateTag',
|
||
])('%s is not allowed', async (api) => {
|
||
await using sandbox = await createSandbox(next, initialFiles)
|
||
const { session } = sandbox
|
||
|
||
await next.patchFile(
|
||
'components/Comp.js',
|
||
outdent`
|
||
import { ${api} } from 'next/cache'
|
||
|
||
export default function Page() {
|
||
return 'hello world'
|
||
}
|
||
`
|
||
)
|
||
|
||
await session.waitForRedbox()
|
||
await expect(session.getRedboxSource()).resolves.toMatch(
|
||
`You're importing a module that depends on "${api}". This API is only available in Server Components in the App Router, but you are using it in the Pages Router.`
|
||
)
|
||
})
|
||
|
||
test.each([
|
||
'unstable_cache', // useless in client, but doesn't technically error
|
||
'unstable_noStore', // no-op in client, but allowed for legacy reasons
|
||
])('%s is allowed', async (api) => {
|
||
await using sandbox = await createSandbox(next, initialFiles)
|
||
const { session } = sandbox
|
||
|
||
await next.patchFile(
|
||
'components/Comp.js',
|
||
outdent`
|
||
import { ${api} } from 'next/cache'
|
||
|
||
export default function Page() {
|
||
return 'hello world'
|
||
}
|
||
`
|
||
)
|
||
|
||
await session.waitForNoRedbox()
|
||
})
|
||
})
|
||
})
|
||
|
||
const takeUpToString = (text: string, str: string): string =>
|
||
text.includes(str) ? text.slice(0, text.indexOf(str) + str.length) : text
|