Skip to content

Commit

Permalink
misc: refactor build context/webpack build step
Browse files Browse the repository at this point in the history
  • Loading branch information
feedthejim committed Jan 31, 2023
1 parent e5a9d38 commit fb5149b
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 57 deletions.
50 changes: 50 additions & 0 deletions packages/next/src/build/build-context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { LoadedEnvFiles } from '@next/env'
import { Ora } from 'next/dist/compiled/ora'
import { Rewrite } from '../lib/load-custom-routes'
import { __ApiPreviewProps } from '../server/api-utils'
import { NextConfigComplete } from '../server/config-shared'
import { Span } from '../trace'
import { TelemetryPlugin } from './webpack/plugins/telemetry-plugin'

// a global object to store context for the current build
// this is used to pass data between different steps of the build without having
// to pass it through function arguments.
// Not exhaustive, but should be extended to as needed whilst refactoring
export const NextBuildContext: Partial<{
// core fields
dir: string
buildId: string
config: NextConfigComplete
appDir: string
pagesDir: string
rewrites: {
fallback: Rewrite[]
afterFiles: Rewrite[]
beforeFiles: Rewrite[]
}
loadedEnvFiles: LoadedEnvFiles
previewProps: __ApiPreviewProps
mappedPages:
| {
[page: string]: string
}
| undefined
mappedAppPages:
| {
[page: string]: string
}
| undefined
mappedRootPaths: {
[page: string]: string
}

// misc fields
telemetryPlugin: TelemetryPlugin
buildSpinner: Ora
nextBuildSpan: Span

// cli fields
reactProductionProfiling: boolean
noMangling: boolean
appDirOnly: boolean
}> = {}
55 changes: 18 additions & 37 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ import { generateBuildId } from './generate-build-id'
import { isWriteable } from './is-writeable'
import * as Log from './output/log'
import createSpinner from './spinner'
import { trace, flushAllTraces, setGlobal, Span } from '../trace'
import { trace, flushAllTraces, setGlobal } from '../trace'
import {
detectConflictingPaths,
computeFromManifest,
Expand All @@ -103,7 +103,6 @@ import { normalizeLocalePath } from '../shared/lib/i18n/normalize-locale-path'
import { NextConfigComplete } from '../server/config-shared'
import isError, { NextError } from '../lib/is-error'
import { isEdgeRuntime } from '../lib/is-edge-runtime'
import { TelemetryPlugin } from './webpack/plugins/telemetry-plugin'
import { MiddlewareManifest } from './webpack/plugins/middleware-plugin'
import { recursiveCopy } from '../lib/recursive-copy'
import { recursiveReadDir } from '../lib/recursive-readdir'
Expand All @@ -121,7 +120,7 @@ import { normalizeAppPath } from '../shared/lib/router/utils/app-paths'
import { AppBuildManifest } from './webpack/plugins/app-build-manifest-plugin'
import { RSC, RSC_VARY_HEADER } from '../client/components/app-router-headers'
import { webpackBuild } from './webpack-build'
import { Ora } from 'next/dist/compiled/ora'
import { NextBuildContext } from './build-context'

export type SsgRoute = {
initialRevalidateSeconds: number | false
Expand All @@ -144,13 +143,6 @@ export type PrerenderManifest = {
preview: __ApiPreviewProps
}

export const NextBuildContext: Partial<{
telemetryPlugin: TelemetryPlugin
buildSpinner: Ora
nextBuildSpan: Span
dir: string
}> = {}

/**
* typescript will be loaded in "next/lib/verifyTypeScriptSetup" and
* then passed to "next/lib/typescript/runTypeCheck" as a parameter.
Expand Down Expand Up @@ -252,33 +244,40 @@ export default async function build(
const nextBuildSpan = trace('next-build', undefined, {
version: process.env.__NEXT_VERSION as string,
})

NextBuildContext.nextBuildSpan = nextBuildSpan
NextBuildContext.dir = dir
NextBuildContext.appDirOnly = appDirOnly
NextBuildContext.reactProductionProfiling = reactProductionProfiling
NextBuildContext.noMangling = noMangling

const buildResult = await nextBuildSpan.traceAsyncFn(async () => {
// attempt to load global env values so they are available in next.config.js
const { loadedEnvFiles } = nextBuildSpan
.traceChild('load-dotenv')
.traceFn(() => loadEnvConfig(dir, false, Log))
NextBuildContext.loadedEnvFiles = loadedEnvFiles

const config: NextConfigComplete = await nextBuildSpan
.traceChild('load-next-config')
.traceAsyncFn(() => loadConfig(PHASE_PRODUCTION_BUILD, dir))
NextBuildContext.config = config

const distDir = path.join(dir, config.distDir)
setGlobal('phase', PHASE_PRODUCTION_BUILD)
setGlobal('distDir', distDir)

const { target } = config
const buildId: string = await nextBuildSpan
.traceChild('generate-buildid')
.traceAsyncFn(() => generateBuildId(config.generateBuildId, nanoid))
NextBuildContext.buildId = buildId

const customRoutes: CustomRoutes = await nextBuildSpan
.traceChild('load-custom-routes')
.traceAsyncFn(() => loadCustomRoutes(config))

const { headers, rewrites, redirects } = customRoutes
NextBuildContext.rewrites = rewrites

const cacheDir = path.join(distDir, 'cache')
if (ciEnvironment.isCI && !ciEnvironment.hasNextSupport) {
Expand Down Expand Up @@ -325,6 +324,9 @@ export default async function build(
})

const { pagesDir, appDir } = findPagesDir(dir, isAppDirEnabled)
NextBuildContext.pagesDir = pagesDir
NextBuildContext.appDir = appDir

const isSrcDir = path
.relative(dir, pagesDir || appDir || '')
.startsWith('src')
Expand Down Expand Up @@ -517,6 +519,7 @@ export default async function build(
previewModeSigningKey: crypto.randomBytes(32).toString('hex'),
previewModeEncryptionKey: crypto.randomBytes(32).toString('hex'),
}
NextBuildContext.previewProps = previewProps

const mappedPages = nextBuildSpan
.traceChild('create-pages-mapping')
Expand All @@ -529,6 +532,7 @@ export default async function build(
pagesDir,
})
)
NextBuildContext.mappedPages = mappedPages

let mappedAppPages: { [page: string]: string } | undefined
let denormalizedAppPages: string[] | undefined
Expand All @@ -545,6 +549,7 @@ export default async function build(
pagesDir: pagesDir,
})
)
NextBuildContext.mappedAppPages = mappedAppPages
}

let mappedRootPaths: { [page: string]: string } = {}
Expand All @@ -557,6 +562,7 @@ export default async function build(
pagesDir: pagesDir,
})
}
NextBuildContext.mappedRootPaths = mappedRootPaths

const pagesPageKeys = Object.keys(mappedPages)

Expand Down Expand Up @@ -920,32 +926,7 @@ export default async function build(
ignore: [] as string[],
}))

const webpackBuildDuration = await webpackBuild(
{
buildId,
config,
pagesDir,
reactProductionProfiling,
rewrites,
target,
appDir,
noMangling,
},
{
buildId,
config,
envFiles: loadedEnvFiles,
isDev: false,
pages: mappedPages,
pagesDir,
previewMode: previewProps,
rootDir: dir,
rootPaths: mappedRootPaths,
appDir,
appPaths: mappedAppPages,
pageExtensions: config.pageExtensions,
}
)
const webpackBuildDuration = await webpackBuild()

telemetry.record(
eventBuildCompleted(pagesPaths, {
Expand Down
59 changes: 39 additions & 20 deletions packages/next/src/build/webpack-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ import getBaseWebpackConfig from './webpack-config'
import { NextError } from '../lib/is-error'
import { injectedClientEntries } from './webpack/plugins/flight-client-entry-plugin'
import { TelemetryPlugin } from './webpack/plugins/telemetry-plugin'
import { Rewrite } from '../lib/load-custom-routes'
import { NextConfigComplete } from '../server/config-shared'
import { MiddlewareMatcher } from './analysis/get-page-static-info'
import { NextBuildContext } from '.'
import { NextBuildContext } from './build-context'

import { createEntrypoints } from './entries'

type CompilerResult = {
errors: webpack.StatsError[]
Expand All @@ -34,21 +33,7 @@ function isTelemetryPlugin(plugin: unknown): plugin is TelemetryPlugin {
return plugin instanceof TelemetryPlugin
}

export async function webpackBuild(commonWebpackOptions: {
buildId: string
config: NextConfigComplete
pagesDir: string | undefined
reactProductionProfiling: boolean
rewrites: {
fallback: Rewrite[]
afterFiles: Rewrite[]
beforeFiles: Rewrite[]
}
target: string
appDir: string | undefined
noMangling: boolean
middlewareMatchers: MiddlewareMatcher[] | undefined
}): Promise<number> {
export async function webpackBuild(): Promise<number> {
let result: CompilerResult | null = {
warnings: [],
errors: [],
Expand All @@ -58,30 +43,64 @@ export async function webpackBuild(commonWebpackOptions: {
const nextBuildSpan = NextBuildContext.nextBuildSpan!
const buildSpinner = NextBuildContext.buildSpinner
const dir = NextBuildContext.dir!

await (async () => {
// IIFE to isolate locals and avoid retaining memory too long
const runWebpackSpan = nextBuildSpan.traceChild('run-webpack-compiler')

const entrypoints = NextBuildContext.entrypoints!
const entrypoints = await nextBuildSpan
.traceChild('create-entrypoints')
.traceAsyncFn(() =>
createEntrypoints({
buildId: NextBuildContext.buildId!,
config: NextBuildContext.config!,
envFiles: NextBuildContext.loadedEnvFiles!,
isDev: false,
rootDir: dir,
pageExtensions: NextBuildContext.config!.pageExtensions!,
pagesDir: NextBuildContext.pagesDir!,
appDir: NextBuildContext.appDir!,
pages: NextBuildContext.mappedPages!,
appPaths: NextBuildContext.mappedAppPages!,
previewMode: NextBuildContext.previewProps!,
rootPaths: NextBuildContext.mappedRootPaths!,
})
)

const commonWebpackOptions = {
isServer: false,
buildId: NextBuildContext.buildId!,
config: NextBuildContext.config!,
target: NextBuildContext.config!.target!,
appDir: NextBuildContext.appDir!,
pagesDir: NextBuildContext.pagesDir!,
rewrites: NextBuildContext.rewrites!,
reactProductionProfiling: NextBuildContext.reactProductionProfiling!,
noMangling: NextBuildContext.noMangling!,
}

const configs = await runWebpackSpan
.traceChild('generate-webpack-config')
.traceAsyncFn(() =>
Promise.all([
getBaseWebpackConfig(dir, {
...commonWebpackOptions,
middlewareMatchers: entrypoints.middlewareMatchers,
runWebpackSpan,
compilerType: COMPILER_NAMES.client,
entrypoints: entrypoints.client,
}),
getBaseWebpackConfig(dir, {
...commonWebpackOptions,
runWebpackSpan,
middlewareMatchers: entrypoints.middlewareMatchers,
compilerType: COMPILER_NAMES.server,
entrypoints: entrypoints.server,
}),
getBaseWebpackConfig(dir, {
...commonWebpackOptions,
runWebpackSpan,
middlewareMatchers: entrypoints.middlewareMatchers,
compilerType: COMPILER_NAMES.edgeServer,
entrypoints: entrypoints.edgeServer,
}),
Expand Down

0 comments on commit fb5149b

Please sign in to comment.