mirror of
https://github.com/hanzoai/next.js.git
synced 2026-10-30 14:46:38 +00:00
Closes PACK-5382
Previously, Turbopack validated every single module for route segment config, leading to false positives (on non-page files) and worse build performance. There were also three separate implementations of this logic.
Now, all of these cases are fatal errors, not just warnings.
Examples for errors
```
./bench/basic-app/app/export/inherit/page.tsx:1:28
Next.js can't recognize the exported `preferredRegion` field in route. It needs to be a static string or array of static strings.
> 1 | export { default, runtime, preferredRegion } from '../basic/foo'
| ^^^^^^^^^^^^^^^
The exported configuration object in a source file needs to have a very specific format from which some properties can be statically parsed at compiled-time.
https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config
```
This error message is slightly different now:
```
browser log: ./test/integration/app-dir-export/app/another/[slug]/page.js:6:8
Next.js can't recognize the exported `generateStaticParams` field in route. App pages cannot use both "use client" and export function "generateStaticParams()".
4 |
5 |
> 6 | export function generateStaticParams() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 7 | return [{ slug: 'first' }, { slug: 'second' }]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 8 | }
| ^^
9 |
10 | export default async function Page(props) {
11 | const params = await props.params
The exported configuration object in a source file needs to have a very specific format from which some properties can be statically parsed at compiled-time.
Import traces:
Client Component Browser:
./test/integration/app-dir-export/app/another/[slug]/page.js [Client Component Browser]
./test/integration/app-dir-export/app/another/[slug]/page.js [Server Component]
Client Component SSR:
./test/integration/app-dir-export/app/another/[slug]/page.js [Client Component SSR]
./test/integration/app-dir-export/app/another/[slug]/page.js [Server Component]
https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config
```
257 lines
8.4 KiB
TypeScript
257 lines
8.4 KiB
TypeScript
import { nextBuild } from 'next-test-utils'
|
|
import path from 'path'
|
|
|
|
describe('Exported runtimes value validation', () => {
|
|
test('fails to build on malformed input', async () => {
|
|
const result = await nextBuild(
|
|
path.resolve(__dirname, './invalid-runtime/app'),
|
|
undefined,
|
|
{ stdout: true, stderr: true }
|
|
)
|
|
expect(result).toMatchObject({
|
|
code: 1,
|
|
stderr: process.env.IS_TURBOPACK_TEST
|
|
? expect.stringContaining(
|
|
'runtime` has an invalid value: unknown variant `something-odd`, expected one of `nodejs`, `edge`, `experimental-edge`'
|
|
)
|
|
: expect.stringContaining(
|
|
`Invalid enum value. Expected 'edge' | 'experimental-edge' | 'nodejs', received 'something-odd'`
|
|
),
|
|
})
|
|
})
|
|
|
|
test('fails the build on invalid middleware matcher', async () => {
|
|
const result = await nextBuild(
|
|
path.resolve(__dirname, './invalid-middleware'),
|
|
undefined,
|
|
{ stdout: true, stderr: true }
|
|
)
|
|
|
|
// The build should fail to prevent unexpected behavior
|
|
expect(result.code).toBe(1)
|
|
|
|
// TODO: Turbopack matches the error message but omits the routing & error information
|
|
if (process.env.IS_TURBOPACK_TEST) {
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
"Next.js can't recognize the exported `config` field in route"
|
|
)
|
|
)
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
' Entry `matcher[1]` need to be static strings or static objects.'
|
|
)
|
|
)
|
|
} else {
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
'Next.js can\'t recognize the exported `config` field in route "/middleware"'
|
|
)
|
|
)
|
|
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
'Unknown identifier "dynamicPath" at "config.matcher[1]"'
|
|
)
|
|
)
|
|
}
|
|
})
|
|
|
|
test('fails the build on unrecognized runtimes value', async () => {
|
|
const result = await nextBuild(
|
|
path.resolve(__dirname, './unsupported-syntax/app'),
|
|
undefined,
|
|
{ stdout: true, stderr: true }
|
|
)
|
|
|
|
// The build should fail to prevent unexpected behavior
|
|
expect(result.code).toBe(1)
|
|
|
|
// Template Literal with Expressions
|
|
if (process.env.IS_TURBOPACK_TEST) {
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
"Next.js can't recognize the exported `config` field in route"
|
|
)
|
|
)
|
|
// TODO: Turbopack has this information in issue.detail but it's not logged to the user.
|
|
// expect(result.stderr).toEqual(
|
|
// expect.stringContaining(
|
|
// 'Unsupported template literal with expressions at "config.runtime".'
|
|
// )
|
|
// )
|
|
} else {
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
'Next.js can\'t recognize the exported `config` field in route "/template-literal-with-expressions"'
|
|
)
|
|
)
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
'Unsupported template literal with expressions at "config.runtime".'
|
|
)
|
|
)
|
|
}
|
|
|
|
// Binary Expression
|
|
if (process.env.IS_TURBOPACK_TEST) {
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
"Next.js can't recognize the exported `config` field in route"
|
|
)
|
|
)
|
|
// TODO: Turbopack has this information in issue.detail but it's not logged to the user.
|
|
// expect(result.stderr).toEqual(
|
|
// expect.stringContaining(
|
|
// 'Unsupported node type "BinaryExpression" at "config.runtime"'
|
|
// )
|
|
// )
|
|
} else {
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
'Next.js can\'t recognize the exported `config` field in route "/binary-expression"'
|
|
)
|
|
)
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
'Unsupported node type "BinaryExpression" at "config.runtime"'
|
|
)
|
|
)
|
|
}
|
|
|
|
// Spread Operator within Object Expression
|
|
if (process.env.IS_TURBOPACK_TEST) {
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
"Next.js can't recognize the exported `config` field in route"
|
|
)
|
|
)
|
|
// TODO: Turbopack has this information in issue.detail but it's not logged to the user.
|
|
// expect(result.stderr).toEqual(
|
|
// expect.stringContaining(
|
|
// 'Unsupported spread operator in the Object Expression at "config.runtime"'
|
|
// )
|
|
// )
|
|
} else {
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
'Next.js can\'t recognize the exported `config` field in route "/object-spread-operator"'
|
|
)
|
|
)
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
'Unsupported spread operator in the Object Expression at "config.runtime"'
|
|
)
|
|
)
|
|
}
|
|
|
|
// Spread Operator within Array Expression
|
|
if (process.env.IS_TURBOPACK_TEST) {
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
"Next.js can't recognize the exported `config` field in route"
|
|
)
|
|
)
|
|
// TODO: Turbopack has this information in issue.detail but it's not logged to the user.
|
|
// expect(result.stderr).toEqual(
|
|
// expect.stringContaining(
|
|
// 'Unsupported spread operator in the Array Expression at "config.runtime"'
|
|
// )
|
|
// )
|
|
} else {
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
'Next.js can\'t recognize the exported `config` field in route "/array-spread-operator"'
|
|
)
|
|
)
|
|
// ensure only 1 occurrence of the log
|
|
expect(
|
|
result.stderr.match(/field in route "\/array-spread-operator"/g)?.length
|
|
).toBe(1)
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
'Unsupported spread operator in the Array Expression at "config.runtime"'
|
|
)
|
|
)
|
|
}
|
|
|
|
// Unknown Identifier
|
|
if (process.env.IS_TURBOPACK_TEST) {
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
"Next.js can't recognize the exported `config` field in route"
|
|
)
|
|
)
|
|
// TODO: Turbopack has this information in issue.detail but it's not logged to the user.
|
|
// expect(result.stderr).toEqual(
|
|
// expect.stringContaining(
|
|
// 'Unknown identifier "runtime" at "config.runtime".'
|
|
// )
|
|
// )
|
|
} else {
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
'Next.js can\'t recognize the exported `config` field in route "/invalid-identifier"'
|
|
)
|
|
)
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
'Unknown identifier "runtime" at "config.runtime".'
|
|
)
|
|
)
|
|
}
|
|
|
|
// Unknown Expression Type
|
|
if (process.env.IS_TURBOPACK_TEST) {
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
"Next.js can't recognize the exported `config` field in route"
|
|
)
|
|
)
|
|
// TODO: Turbopack has this information in issue.detail but it's not logged to the user.
|
|
// expect(result.stderr).toEqual(
|
|
// expect.stringContaining(
|
|
// 'Unsupported node type "CallExpression" at "config.runtime"'
|
|
// )
|
|
// )
|
|
} else {
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
'Next.js can\'t recognize the exported `config` field in route "/unsupported-value-type"'
|
|
)
|
|
)
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
'Unsupported node type "CallExpression" at "config.runtime"'
|
|
)
|
|
)
|
|
}
|
|
|
|
// Unknown Object Key
|
|
if (process.env.IS_TURBOPACK_TEST) {
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
"Next.js can't recognize the exported `config` field in route"
|
|
)
|
|
)
|
|
// TODO: Turbopack has this information in issue.detail but it's not logged to the user.
|
|
// expect(result.stderr).toEqual(
|
|
// expect.stringContaining(
|
|
// 'Unsupported key type "Computed" in the Object Expression at "config.runtime"'
|
|
// )
|
|
// )
|
|
} else {
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
'Next.js can\'t recognize the exported `config` field in route "/unsupported-object-key"'
|
|
)
|
|
)
|
|
expect(result.stderr).toEqual(
|
|
expect.stringContaining(
|
|
'Unsupported key type "Computed" in the Object Expression at "config.runtime"'
|
|
)
|
|
)
|
|
}
|
|
})
|
|
})
|