Skip to content

Commit 6d536ed

Browse files
authored
Turbopack: improve errors/warnings for turbopack messages (#84552)
### What? * make the turbopack error not a really long line, which is annoying in the deployment build log * Show a `(invalid experimental key)` info in the list of experimental options when they are not supported.
1 parent b41b737 commit 6d536ed

File tree

10 files changed

+299
-206
lines changed

10 files changed

+299
-206
lines changed

packages/next/src/lib/turbopack-warning.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,15 +152,20 @@ export async function validateTurboNextConfig({
152152
if (process.env.TURBOPACK === 'auto' && hasWebpackConfig && !hasTurboConfig) {
153153
const configFile = rawNextConfig.configFileName ?? 'your Next config file'
154154
Log.error(
155-
`ERROR: This build is using Turbopack, with a \`webpack\` config and no \`turbopack\` config. This may be a mistake.
155+
`ERROR: This build is using Turbopack, with a \`webpack\` config and no \`turbopack\` config.
156+
This may be a mistake.
156157
157-
As of Next.js 16 turbopack is enabled by default and custom webpack configurations may need to be migrated to Turbopack.
158+
As of Next.js 16 Turbopack is enabled by default and
159+
custom webpack configurations may need to be migrated to Turbopack.
158160
159-
NOTE: your \`webpack\` config may have been added by a configuration plugin.
161+
NOTE: your \`webpack\` config may have been added by a configuration plugin.
160162
161-
To configure Turbopack, see https://nextjs.org/docs/app/api-reference/next-config-js/turbopack
163+
To configure Turbopack, see https://nextjs.org/docs/app/api-reference/next-config-js/turbopack
162164
163-
TIP: Many applications work fine under Turbopack with no configuration, if that is the case for you, you can silence this error by passing the \`--turbopack\` or \`--webpack\` flag explicitly or simply setting an empty turbopack config in ${configFile} (e.g. \`turbopack: {}\`).`
165+
TIP: Many applications work fine under Turbopack with no configuration,
166+
if that is the case for you, you can silence this error by passing the
167+
\`--turbopack\` or \`--webpack\` flag explicitly or simply setting an
168+
empty turbopack config in ${configFile} (e.g. \`turbopack: {}\`).`
164169
)
165170

166171
process.exit(1)

packages/next/src/server/config-schema.ts

Lines changed: 184 additions & 185 deletions
Large diffs are not rendered by default.

packages/next/src/server/config.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,18 @@ function normalizeNextConfigZodErrors(
5757
// We exit the build when encountering an error in the images config
5858
shouldExit = true
5959
}
60+
if (
61+
issue.code === 'unrecognized_keys' &&
62+
issue.path[0] === 'experimental' &&
63+
message.includes('turbopackPersistentCaching')
64+
) {
65+
// We exit the build when encountering an error in the turbopackPersistentCaching config
66+
shouldExit = true
67+
message +=
68+
"\nUse 'experimental.turbopackPersistentCachingForDev' instead."
69+
message +=
70+
'\nLearn more: https://nextjs.org/docs/app/api-reference/config/next-config-js/turbopackPersistentCaching'
71+
}
6072

6173
return message
6274
}),
@@ -1974,7 +1986,7 @@ async function validateConfigSchema(
19741986
)
19751987
// ident list item
19761988
for (const error of errorMessages) {
1977-
messages.push(` ${error}`)
1989+
messages.push(` ${error.split('\n').join('\n ')}`)
19781990
}
19791991

19801992
// error message footer

packages/next/src/server/lib/app-info-log.ts

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { loadEnvConfig } from '@next/env'
22
import * as Log from '../../build/output/log'
3-
import { bold, purple } from '../../lib/picocolors'
3+
import { bold, purple, strikethrough } from '../../lib/picocolors'
44
import {
55
PHASE_DEVELOPMENT_SERVER,
66
PHASE_PRODUCTION_BUILD,
77
} from '../../shared/lib/constants'
88
import loadConfig, { type ConfiguredExperimentalFeature } from '../config'
9+
import { experimentalSchema } from '../config-schema'
910

1011
export function logStartInfo({
1112
networkUrl,
@@ -47,21 +48,31 @@ export function logStartInfo({
4748
if (experimentalFeatures?.length) {
4849
Log.bootstrap(`- Experiments (use with caution):`)
4950
for (const exp of experimentalFeatures) {
50-
const symbol =
51-
typeof exp.value === 'boolean'
52-
? exp.value === true
53-
? bold('✓')
54-
: bold('⨯')
55-
: '·'
51+
const isValid = Object.prototype.hasOwnProperty.call(
52+
experimentalSchema,
53+
exp.key
54+
)
55+
if (isValid) {
56+
const symbol =
57+
typeof exp.value === 'boolean'
58+
? exp.value === true
59+
? bold('✓')
60+
: bold('⨯')
61+
: '·'
5662

57-
const suffix =
58-
typeof exp.value === 'number' || typeof exp.value === 'string'
59-
? `: ${JSON.stringify(exp.value)}`
60-
: ''
63+
const suffix =
64+
typeof exp.value === 'number' || typeof exp.value === 'string'
65+
? `: ${JSON.stringify(exp.value)}`
66+
: ''
6167

62-
const reason = exp.reason ? ` (${exp.reason})` : ''
68+
const reason = exp.reason ? ` (${exp.reason})` : ''
6369

64-
Log.bootstrap(` ${symbol} ${exp.key}${suffix}${reason}`)
70+
Log.bootstrap(` ${symbol} ${exp.key}${suffix}${reason}`)
71+
} else {
72+
Log.bootstrap(
73+
` ? ${strikethrough(exp.key)} (invalid experimental key)`
74+
)
75+
}
6576
}
6677
}
6778

test/e2e/config-turbopack/index.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
/* eslint-disable jest/no-standalone-expect */
22
import { nextTestSetup } from 'e2e-utils'
33

4-
const WARNING_MESSAGE =
5-
'ERROR: This build is using Turbopack, with a `webpack` config and no `turbopack` config. This may be a mistake'
4+
const WARNING_MESSAGE = `ERROR: This build is using Turbopack, with a \`webpack\` config and no \`turbopack\` config.`
65

76
const itif = (condition: boolean) => (condition ? it : it.skip)
87

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { ReactNode } from 'react'
2+
export default function Root({ children }: { children: ReactNode }) {
3+
return (
4+
<html>
5+
<body>{children}</body>
6+
</html>
7+
)
8+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function Page() {
2+
return (
3+
<>
4+
<p>hello world</p>
5+
</>
6+
)
7+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* @type {import('next').NextConfig}
3+
*/
4+
const nextConfig = {
5+
experimental: {
6+
turbopackPersistentCaching: true,
7+
},
8+
}
9+
10+
module.exports = nextConfig
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function Page() {
2+
return (
3+
<>
4+
<p>hello world</p>
5+
</>
6+
)
7+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { nextTestSetup } from 'e2e-utils'
2+
3+
describe('persistent-caching-migration', () => {
4+
const { skipped, next, isTurbopack, isNextStart } = nextTestSetup({
5+
files: __dirname,
6+
skipDeployment: true,
7+
skipStart: true,
8+
})
9+
10+
if (skipped) {
11+
return
12+
}
13+
14+
if (!isTurbopack) {
15+
it.skip('only for turbopack', () => {})
16+
return
17+
}
18+
19+
if (isNextStart) {
20+
it('error on old option', async () => {
21+
let { exitCode, cliOutput } = await next.build()
22+
expect(exitCode).toBe(1)
23+
expect(cliOutput).toContain(
24+
"Use 'experimental.turbopackPersistentCachingForDev' instead."
25+
)
26+
})
27+
} else {
28+
it('success on new option', async () => {
29+
await expect(next.start()).toReject()
30+
expect(next.cliOutput).toContain(
31+
"Use 'experimental.turbopackPersistentCachingForDev' instead."
32+
)
33+
})
34+
}
35+
})

0 commit comments

Comments
 (0)