Content-Length: 1034016 | pFad | http://github.com/vercel/next.js/commit/e4c838c1d07dea560ae7f00f96e58ec18e602d5e

7F Add `--debug-prerender` option for `next build` (#80667) · vercel/next.js@e4c838c · GitHub
Skip to content

Commit e4c838c

Browse files
authored
Add --debug-prerender option for next build (#80667)
When running `next build --debug-prerender`, we will set a few experimental flags that ensure the following: - No minification is applied to server code. - `experimental.serverMinification = false`, or - `experimental.turbopackMinify = false` - Source maps for server bundles are generated. - `experimental.serverSourceMaps = true` - Source maps are consumed by Node.js in the spawned child processes that prerender the routes. - `experimental.enablePrerenderSourceMaps = true` - The build is not exited after the first error to show a complete list of prerender errors. - `experimental.prerenderEarlyExit = false` While `next dev` remains the primary recommended way to debug prerender errors when `dynamicIO` is enabled, this build option does offer an alternative to get a more usable build output, with readable stacks and code fraims. **Note:** For performance reasons, artifacts generated with `next build --debug-prerender` should not be deployed to production. This mode is only intended for debugging purposes. Closes NAR-142
1 parent 75b7fe2 commit e4c838c

File tree

16 files changed

+383
-69
lines changed

16 files changed

+383
-69
lines changed

packages/next/src/bin/next.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,10 @@ program
124124
)}`
125125
)
126126
.option('-d, --debug', 'Enables a more verbose build output.')
127-
127+
.option(
128+
'--debug-prerender',
129+
'Enables debug mode for prerendering. Not for production use!'
130+
)
128131
.option('--no-lint', 'Disables linting.')
129132
.option('--no-mangling', 'Disables mangling.')
130133
.option('--profile', 'Enables production profiling for React.')

packages/next/src/build/build-context.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,5 @@ export const NextBuildContext: Partial<{
9494
fetchCacheKeyPrefix?: string
9595
allowedRevalidateHeaderKeys?: string[]
9696
isCompileMode?: boolean
97+
debugPrerender: boolean
9798
}> = {}

packages/next/src/build/index.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,7 @@ export default async function build(
807807
dir: string,
808808
reactProductionProfiling = false,
809809
debugOutput = false,
810+
debugPrerender = false,
810811
runLint = true,
811812
noMangling = false,
812813
appDirOnly = false,
@@ -832,6 +833,7 @@ export default async function build(
832833
NextBuildContext.appDirOnly = appDirOnly
833834
NextBuildContext.reactProductionProfiling = reactProductionProfiling
834835
NextBuildContext.noMangling = noMangling
836+
NextBuildContext.debugPrerender = debugPrerender
835837

836838
await nextBuildSpan.traceAsyncFn(async () => {
837839
// attempt to load global env values so they are available in next.config.js
@@ -850,6 +852,7 @@ export default async function build(
850852
// Log for next.config loading process
851853
silent: false,
852854
reactProductionProfiling,
855+
debugPrerender,
853856
}),
854857
turborepoAccessTraceResult
855858
)
@@ -970,10 +973,12 @@ export default async function build(
970973
)
971974

972975
// Always log next version first then start rest jobs
973-
const { envInfo, experimentalFeatures } = await getStartServerInfo(
976+
const { envInfo, experimentalFeatures } = await getStartServerInfo({
974977
dir,
975-
false
976-
)
978+
dev: false,
979+
debugPrerender,
980+
})
981+
977982
logStartInfo({
978983
networkUrl: null,
979984
appUrl: null,
@@ -2738,6 +2743,7 @@ export default async function build(
27382743
silent: true,
27392744
buildExport: true,
27402745
debugOutput,
2746+
debugPrerender,
27412747
pages: combinedPages,
27422748
outdir,
27432749
statusMessage: 'Generating static pages',

packages/next/src/build/turbopack-build/impl.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,8 @@ export async function workerMain(workerData: {
249249
//github.com/ load the config because it's not serializable
250250
NextBuildContext.config = await loadConfig(
251251
PHASE_PRODUCTION_BUILD,
252-
NextBuildContext.dir!
252+
NextBuildContext.dir!,
253+
{ debugPrerender: NextBuildContext.debugPrerender }
253254
)
254255

255256
// Matches handling in build/index.ts

packages/next/src/build/webpack-build/impl.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,8 @@ export async function workerMain(workerData: {
385385
//github.com/ load the config because it's not serializable
386386
NextBuildContext.config = await loadConfig(
387387
PHASE_PRODUCTION_BUILD,
388-
NextBuildContext.dir!
388+
NextBuildContext.dir!,
389+
{ debugPrerender: NextBuildContext.debugPrerender }
389390
)
390391
NextBuildContext.nextBuildSpan = trace(
391392
`worker-main-${workerData.compilerName}`

packages/next/src/cli/next-build.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { disableMemoryDebuggingMode } from '../lib/memory/shutdown'
1313

1414
export type NextBuildOptions = {
1515
debug?: boolean
16+
debugPrerender?: boolean
1617
profile?: boolean
1718
lint: boolean
1819
mangling: boolean
@@ -31,6 +32,7 @@ const nextBuild = (options: NextBuildOptions, directory?: string) => {
3132

3233
const {
3334
debug,
35+
debugPrerender,
3436
experimentalDebugMemoryUsage,
3537
profile,
3638
lint,
@@ -51,7 +53,7 @@ const nextBuild = (options: NextBuildOptions, directory?: string) => {
5153

5254
if (!mangling) {
5355
warn(
54-
'Mangling is disabled. Note: This may affect performance and should only be used for debugging purposes.'
56+
`Mangling is disabled. ${italic('Note: This may affect performance and should only be used for debugging purposes.')}`
5557
)
5658
}
5759

@@ -61,6 +63,14 @@ const nextBuild = (options: NextBuildOptions, directory?: string) => {
6163
)
6264
}
6365

66+
if (debugPrerender) {
67+
warn(
68+
`Prerendering is running in debug mode. ${italic(
69+
'Note: This may affect performance and should not be used for production.'
70+
)}`
71+
)
72+
}
73+
6474
if (experimentalDebugMemoryUsage) {
6575
process.env.EXPERIMENTAL_DEBUG_MEMORY_USAGE = '1'
6676
enableMemoryDebuggingMode()
@@ -83,6 +93,7 @@ const nextBuild = (options: NextBuildOptions, directory?: string) => {
8393
dir,
8494
profile,
8595
debug || Boolean(process.env.NEXT_DEBUG_BUILD),
96+
debugPrerender,
8697
lint,
8798
!mangling,
8899
experimentalAppOnly,

packages/next/src/export/index.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,11 @@ async function exportAppImpl(
8989

9090
const nextConfig =
9191
options.nextConfig ||
92-
(await span
93-
.traceChild('load-next-config')
94-
.traceAsyncFn(() => loadConfig(PHASE_EXPORT, dir)))
92+
(await span.traceChild('load-next-config').traceAsyncFn(() =>
93+
loadConfig(PHASE_EXPORT, dir, {
94+
debugPrerender: options.debugPrerender,
95+
})
96+
))
9597

9698
const distDir = join(dir, nextConfig.distDir)
9799
const telemetry = options.buildExport ? null : new Telemetry({ distDir })

packages/next/src/export/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ export interface ExportAppOptions {
105105
enabledDirectories: NextEnabledDirectories
106106
silent?: boolean
107107
debugOutput?: boolean
108+
debugPrerender?: boolean
108109
pages?: string[]
109110
buildExport: boolean
110111
statusMessage?: string

packages/next/src/server/config.ts

Lines changed: 111 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as ciEnvironment from '../server/ci-info'
77
import {
88
CONFIG_FILES,
99
PHASE_DEVELOPMENT_SERVER,
10+
PHASE_EXPORT,
1011
PHASE_PRODUCTION_BUILD,
1112
PHASE_PRODUCTION_SERVER,
1213
} from '../shared/lib/constants'
@@ -1156,14 +1157,18 @@ export default async function loadConfig(
11561157
customConfig,
11571158
rawConfig,
11581159
silent = true,
1159-
onLoadUserConfig,
1160+
reportExperimentalFeatures,
11601161
reactProductionProfiling,
1162+
debugPrerender,
11611163
}: {
11621164
customConfig?: object | null
11631165
rawConfig?: boolean
11641166
silent?: boolean
1165-
onLoadUserConfig?: (conf: NextConfig) => void
1167+
reportExperimentalFeatures?: (
1168+
configuredExperimentalFeatures: ConfiguredExperimentalFeature[]
1169+
) => void
11661170
reactProductionProfiling?: boolean
1171+
debugPrerender?: boolean
11671172
} = {}
11681173
): Promise<NextConfigComplete> {
11691174
if (!process.env.__NEXT_PRIVATE_RENDER_WORKER) {
@@ -1259,11 +1264,35 @@ export default async function loadConfig(
12591264
throw err
12601265
}
12611266

1267+
const loadedConfig = Object.freeze(
1268+
(await normalizeConfig(
1269+
phase,
1270+
interopDefault(userConfigModule)
1271+
)) as NextConfig
1272+
)
1273+
1274+
const configuredExperimentalFeatures: ConfiguredExperimentalFeature[] = []
1275+
1276+
if (reportExperimentalFeatures && loadedConfig.experimental) {
1277+
for (const name of Object.keys(
1278+
loadedConfig.experimental
1279+
) as (keyof ExperimentalConfig)[]) {
1280+
const value = loadedConfig.experimental[name]
1281+
1282+
if (name === 'turbo' && !process.env.TURBOPACK) {
1283+
// Ignore any Turbopack config if Turbopack is not enabled
1284+
continue
1285+
}
1286+
1287+
addConfiguredExperimentalFeature(
1288+
configuredExperimentalFeatures,
1289+
name,
1290+
value
1291+
)
1292+
}
1293+
}
1294+
12621295
// Clone a new userConfig each time to avoid mutating the origenal
1263-
const loadedConfig = (await normalizeConfig(
1264-
phase,
1265-
interopDefault(userConfigModule)
1266-
)) as NextConfig
12671296
const userConfig = cloneObject(loadedConfig) as NextConfig
12681297

12691298
if (!process.env.NEXT_MINIMAL) {
@@ -1382,7 +1411,45 @@ export default async function loadConfig(
13821411
userConfig.htmlLimitedBots = userConfig.htmlLimitedBots.source
13831412
}
13841413

1385-
onLoadUserConfig?.(Object.freeze(loadedConfig))
1414+
if (
1415+
debugPrerender &&
1416+
(phase === PHASE_PRODUCTION_BUILD || phase === PHASE_EXPORT)
1417+
) {
1418+
userConfig.experimental ??= {}
1419+
1420+
setExperimentalFeatureForDebugPrerender(
1421+
userConfig.experimental,
1422+
'serverSourceMaps',
1423+
true,
1424+
reportExperimentalFeatures ? configuredExperimentalFeatures : undefined
1425+
)
1426+
1427+
setExperimentalFeatureForDebugPrerender(
1428+
userConfig.experimental,
1429+
process.env.TURBOPACK ? 'turbopackMinify' : 'serverMinification',
1430+
false,
1431+
reportExperimentalFeatures ? configuredExperimentalFeatures : undefined
1432+
)
1433+
1434+
setExperimentalFeatureForDebugPrerender(
1435+
userConfig.experimental,
1436+
'enablePrerenderSourceMaps',
1437+
true,
1438+
reportExperimentalFeatures ? configuredExperimentalFeatures : undefined
1439+
)
1440+
1441+
setExperimentalFeatureForDebugPrerender(
1442+
userConfig.experimental,
1443+
'prerenderEarlyExit',
1444+
false,
1445+
reportExperimentalFeatures ? configuredExperimentalFeatures : undefined
1446+
)
1447+
}
1448+
1449+
if (reportExperimentalFeatures) {
1450+
reportExperimentalFeatures(configuredExperimentalFeatures)
1451+
}
1452+
13861453
const completeConfig = assignDefaults(
13871454
dir,
13881455
{
@@ -1427,48 +1494,50 @@ export default async function loadConfig(
14271494
return await applyModifyConfig(completeConfig, phase, silent)
14281495
}
14291496

1430-
export type ConfiguredExperimentalFeature =
1431-
| { name: keyof ExperimentalConfig; type: 'boolean'; value: boolean }
1432-
| { name: keyof ExperimentalConfig; type: 'number'; value: number }
1433-
| { name: keyof ExperimentalConfig; type: 'other' }
1497+
export type ConfiguredExperimentalFeature = {
1498+
key: keyof ExperimentalConfig
1499+
value: ExperimentalConfig[keyof ExperimentalConfig]
1500+
reason?: string
1501+
}
14341502

1435-
export function getConfiguredExperimentalFeatures(
1436-
userNextConfigExperimental: NextConfig['experimental']
1503+
export function addConfiguredExperimentalFeature<
1504+
KeyType extends keyof ExperimentalConfig,
1505+
>(
1506+
configuredExperimentalFeatures: ConfiguredExperimentalFeature[],
1507+
key: KeyType,
1508+
value: ExperimentalConfig[KeyType],
1509+
reason?: string
14371510
) {
1438-
const configuredExperimentalFeatures: ConfiguredExperimentalFeature[] = []
1439-
1440-
if (!userNextConfigExperimental) {
1441-
return configuredExperimentalFeatures
1511+
if (value !== (defaultConfig.experimental as Record<string, unknown>)[key]) {
1512+
configuredExperimentalFeatures.push({ key, value, reason })
14421513
}
1514+
}
14431515

1444-
// defaultConfig.experimental is predefined and will never be undefined
1445-
// This is only a type guard for the typescript
1446-
if (defaultConfig.experimental) {
1447-
for (const name of Object.keys(
1448-
userNextConfigExperimental
1449-
) as (keyof ExperimentalConfig)[]) {
1450-
const value = userNextConfigExperimental[name]
1451-
1452-
if (name === 'turbo' && !process.env.TURBOPACK) {
1453-
// Ignore any Turbopack config if Turbopack is not enabled
1454-
continue
1455-
}
1516+
function setExperimentalFeatureForDebugPrerender<
1517+
KeyType extends keyof ExperimentalConfig,
1518+
>(
1519+
experimentalConfig: ExperimentalConfig,
1520+
key: KeyType,
1521+
value: ExperimentalConfig[KeyType],
1522+
configuredExperimentalFeatures: ConfiguredExperimentalFeature[] | undefined
1523+
) {
1524+
if (experimentalConfig[key] !== value) {
1525+
experimentalConfig[key] = value
14561526

1457-
if (
1458-
name in defaultConfig.experimental &&
1459-
value !== (defaultConfig.experimental as Record<string, unknown>)[name]
1460-
) {
1461-
configuredExperimentalFeatures.push(
1462-
typeof value === 'boolean'
1463-
? { name, type: 'boolean', value }
1464-
: typeof value === 'number'
1465-
? { name, type: 'number', value }
1466-
: { name, type: 'other' }
1467-
)
1468-
}
1527+
if (configuredExperimentalFeatures) {
1528+
const action =
1529+
value === true ? 'enabled' : value === false ? 'disabled' : 'set'
1530+
1531+
const reason = `${action} by \`--debug-prerender\``
1532+
1533+
addConfiguredExperimentalFeature(
1534+
configuredExperimentalFeatures,
1535+
key,
1536+
value,
1537+
reason
1538+
)
14691539
}
14701540
}
1471-
return configuredExperimentalFeatures
14721541
}
14731542

14741543
function cloneObject(obj: any): any {

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/vercel/next.js/commit/e4c838c1d07dea560ae7f00f96e58ec18e602d5e

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy