Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion packages/next/src/bin/next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,10 @@ program
)}`
)
.option('-d, --debug', 'Enables a more verbose build output.')

.option(
'--debug-prerender',
'Enables debug mode for prerendering. Not for production use!'
)
.option('--no-lint', 'Disables linting.')
.option('--no-mangling', 'Disables mangling.')
.option('--profile', 'Enables production profiling for React.')
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/build/build-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,5 @@ export const NextBuildContext: Partial<{
fetchCacheKeyPrefix?: string
allowedRevalidateHeaderKeys?: string[]
isCompileMode?: boolean
debugPrerender: boolean
}> = {}
12 changes: 9 additions & 3 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,7 @@ export default async function build(
dir: string,
reactProductionProfiling = false,
debugOutput = false,
debugPrerender = false,
runLint = true,
noMangling = false,
appDirOnly = false,
Expand All @@ -832,6 +833,7 @@ export default async function build(
NextBuildContext.appDirOnly = appDirOnly
NextBuildContext.reactProductionProfiling = reactProductionProfiling
NextBuildContext.noMangling = noMangling
NextBuildContext.debugPrerender = debugPrerender

await nextBuildSpan.traceAsyncFn(async () => {
// attempt to load global env values so they are available in next.config.js
Expand All @@ -850,6 +852,7 @@ export default async function build(
// Log for next.config loading process
silent: false,
reactProductionProfiling,
debugPrerender,
}),
turborepoAccessTraceResult
)
Expand Down Expand Up @@ -970,10 +973,12 @@ export default async function build(
)

// Always log next version first then start rest jobs
const { envInfo, experimentalFeatures } = await getStartServerInfo(
const { envInfo, experimentalFeatures } = await getStartServerInfo({
dir,
false
)
dev: false,
debugPrerender,
})

logStartInfo({
networkUrl: null,
appUrl: null,
Expand Down Expand Up @@ -2738,6 +2743,7 @@ export default async function build(
silent: true,
buildExport: true,
debugOutput,
debugPrerender,
pages: combinedPages,
outdir,
statusMessage: 'Generating static pages',
Expand Down
3 changes: 2 additions & 1 deletion packages/next/src/build/turbopack-build/impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,8 @@ export async function workerMain(workerData: {
/// load the config because it's not serializable
NextBuildContext.config = await loadConfig(
PHASE_PRODUCTION_BUILD,
NextBuildContext.dir!
NextBuildContext.dir!,
{ debugPrerender: NextBuildContext.debugPrerender }
)

// Matches handling in build/index.ts
Expand Down
3 changes: 2 additions & 1 deletion packages/next/src/build/webpack-build/impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,8 @@ export async function workerMain(workerData: {
/// load the config because it's not serializable
NextBuildContext.config = await loadConfig(
PHASE_PRODUCTION_BUILD,
NextBuildContext.dir!
NextBuildContext.dir!,
{ debugPrerender: NextBuildContext.debugPrerender }
)
NextBuildContext.nextBuildSpan = trace(
`worker-main-${workerData.compilerName}`
Expand Down
13 changes: 12 additions & 1 deletion packages/next/src/cli/next-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { disableMemoryDebuggingMode } from '../lib/memory/shutdown'

export type NextBuildOptions = {
debug?: boolean
debugPrerender?: boolean
profile?: boolean
lint: boolean
mangling: boolean
Expand All @@ -31,6 +32,7 @@ const nextBuild = (options: NextBuildOptions, directory?: string) => {

const {
debug,
debugPrerender,
experimentalDebugMemoryUsage,
profile,
lint,
Expand All @@ -51,7 +53,7 @@ const nextBuild = (options: NextBuildOptions, directory?: string) => {

if (!mangling) {
warn(
'Mangling is disabled. Note: This may affect performance and should only be used for debugging purposes.'
`Mangling is disabled. ${italic('Note: This may affect performance and should only be used for debugging purposes.')}`
)
}

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

if (debugPrerender) {
warn(
`Prerendering is running in debug mode. ${italic(
'Note: This may affect performance and should not be used for production.'
)}`
)
}

if (experimentalDebugMemoryUsage) {
process.env.EXPERIMENTAL_DEBUG_MEMORY_USAGE = '1'
enableMemoryDebuggingMode()
Expand All @@ -83,6 +93,7 @@ const nextBuild = (options: NextBuildOptions, directory?: string) => {
dir,
profile,
debug || Boolean(process.env.NEXT_DEBUG_BUILD),
debugPrerender,
lint,
!mangling,
experimentalAppOnly,
Expand Down
8 changes: 5 additions & 3 deletions packages/next/src/export/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,11 @@ async function exportAppImpl(

const nextConfig =
options.nextConfig ||
(await span
.traceChild('load-next-config')
.traceAsyncFn(() => loadConfig(PHASE_EXPORT, dir)))
(await span.traceChild('load-next-config').traceAsyncFn(() =>
loadConfig(PHASE_EXPORT, dir, {
debugPrerender: options.debugPrerender,
})
))

const distDir = join(dir, nextConfig.distDir)
const telemetry = options.buildExport ? null : new Telemetry({ distDir })
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/export/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export interface ExportAppOptions {
enabledDirectories: NextEnabledDirectories
silent?: boolean
debugOutput?: boolean
debugPrerender?: boolean
pages?: string[]
buildExport: boolean
statusMessage?: string
Expand Down
153 changes: 111 additions & 42 deletions packages/next/src/server/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as ciEnvironment from '../server/ci-info'
import {
CONFIG_FILES,
PHASE_DEVELOPMENT_SERVER,
PHASE_EXPORT,
PHASE_PRODUCTION_BUILD,
PHASE_PRODUCTION_SERVER,
} from '../shared/lib/constants'
Expand Down Expand Up @@ -1156,14 +1157,18 @@ export default async function loadConfig(
customConfig,
rawConfig,
silent = true,
onLoadUserConfig,
reportExperimentalFeatures,
reactProductionProfiling,
debugPrerender,
}: {
customConfig?: object | null
rawConfig?: boolean
silent?: boolean
onLoadUserConfig?: (conf: NextConfig) => void
reportExperimentalFeatures?: (
configuredExperimentalFeatures: ConfiguredExperimentalFeature[]
) => void
reactProductionProfiling?: boolean
debugPrerender?: boolean
} = {}
): Promise<NextConfigComplete> {
if (!process.env.__NEXT_PRIVATE_RENDER_WORKER) {
Expand Down Expand Up @@ -1259,11 +1264,35 @@ export default async function loadConfig(
throw err
}

const loadedConfig = Object.freeze(
(await normalizeConfig(
phase,
interopDefault(userConfigModule)
)) as NextConfig
)

const configuredExperimentalFeatures: ConfiguredExperimentalFeature[] = []

if (reportExperimentalFeatures && loadedConfig.experimental) {
for (const name of Object.keys(
loadedConfig.experimental
) as (keyof ExperimentalConfig)[]) {
const value = loadedConfig.experimental[name]

if (name === 'turbo' && !process.env.TURBOPACK) {
// Ignore any Turbopack config if Turbopack is not enabled
continue
}

addConfiguredExperimentalFeature(
configuredExperimentalFeatures,
name,
value
)
}
}

// Clone a new userConfig each time to avoid mutating the original
const loadedConfig = (await normalizeConfig(
phase,
interopDefault(userConfigModule)
)) as NextConfig
const userConfig = cloneObject(loadedConfig) as NextConfig

if (!process.env.NEXT_MINIMAL) {
Expand Down Expand Up @@ -1382,7 +1411,45 @@ export default async function loadConfig(
userConfig.htmlLimitedBots = userConfig.htmlLimitedBots.source
}

onLoadUserConfig?.(Object.freeze(loadedConfig))
if (
debugPrerender &&
(phase === PHASE_PRODUCTION_BUILD || phase === PHASE_EXPORT)
) {
userConfig.experimental ??= {}
Comment thread
huozhi marked this conversation as resolved.
Outdated

setExperimentalFeatureForDebugPrerender(
userConfig.experimental,
'serverSourceMaps',
true,
reportExperimentalFeatures ? configuredExperimentalFeatures : undefined
)

setExperimentalFeatureForDebugPrerender(
userConfig.experimental,
process.env.TURBOPACK ? 'turbopackMinify' : 'serverMinification',
false,
reportExperimentalFeatures ? configuredExperimentalFeatures : undefined
)

setExperimentalFeatureForDebugPrerender(
userConfig.experimental,
'enablePrerenderSourceMaps',
true,
reportExperimentalFeatures ? configuredExperimentalFeatures : undefined
)

setExperimentalFeatureForDebugPrerender(
userConfig.experimental,
'prerenderEarlyExit',
false,
reportExperimentalFeatures ? configuredExperimentalFeatures : undefined
)
}

if (reportExperimentalFeatures) {
reportExperimentalFeatures(configuredExperimentalFeatures)
}

const completeConfig = assignDefaults(
dir,
{
Expand Down Expand Up @@ -1427,48 +1494,50 @@ export default async function loadConfig(
return await applyModifyConfig(completeConfig, phase, silent)
}

export type ConfiguredExperimentalFeature =
| { name: keyof ExperimentalConfig; type: 'boolean'; value: boolean }
| { name: keyof ExperimentalConfig; type: 'number'; value: number }
| { name: keyof ExperimentalConfig; type: 'other' }
export type ConfiguredExperimentalFeature = {
key: keyof ExperimentalConfig
value: ExperimentalConfig[keyof ExperimentalConfig]
reason?: string
}

export function getConfiguredExperimentalFeatures(
userNextConfigExperimental: NextConfig['experimental']
export function addConfiguredExperimentalFeature<
KeyType extends keyof ExperimentalConfig,
>(
configuredExperimentalFeatures: ConfiguredExperimentalFeature[],
key: KeyType,
value: ExperimentalConfig[KeyType],
reason?: string
) {
const configuredExperimentalFeatures: ConfiguredExperimentalFeature[] = []

if (!userNextConfigExperimental) {
return configuredExperimentalFeatures
if (value !== (defaultConfig.experimental as Record<string, unknown>)[key]) {
configuredExperimentalFeatures.push({ key, value, reason })
}
}

// defaultConfig.experimental is predefined and will never be undefined
// This is only a type guard for the typescript
if (defaultConfig.experimental) {
for (const name of Object.keys(
userNextConfigExperimental
) as (keyof ExperimentalConfig)[]) {
const value = userNextConfigExperimental[name]

if (name === 'turbo' && !process.env.TURBOPACK) {
// Ignore any Turbopack config if Turbopack is not enabled
continue
}
function setExperimentalFeatureForDebugPrerender<
KeyType extends keyof ExperimentalConfig,
>(
experimentalConfig: ExperimentalConfig,
key: KeyType,
value: ExperimentalConfig[KeyType],
configuredExperimentalFeatures: ConfiguredExperimentalFeature[] | undefined
) {
if (experimentalConfig[key] !== value) {
experimentalConfig[key] = value

if (
name in defaultConfig.experimental &&
value !== (defaultConfig.experimental as Record<string, unknown>)[name]
) {
configuredExperimentalFeatures.push(
typeof value === 'boolean'
? { name, type: 'boolean', value }
: typeof value === 'number'
? { name, type: 'number', value }
: { name, type: 'other' }
)
}
if (configuredExperimentalFeatures) {
const action =
value === true ? 'enabled' : value === false ? 'disabled' : 'set'

const reason = `${action} by \`--debug-prerender\``

addConfiguredExperimentalFeature(
configuredExperimentalFeatures,
key,
value,
reason
)
}
}
return configuredExperimentalFeatures
}

function cloneObject(obj: any): any {
Expand Down
Loading