Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor require hooks #48506

Merged
merged 15 commits into from
Apr 21, 2023
192 changes: 102 additions & 90 deletions packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,30 +280,10 @@ export default async function build(

const publicDir = path.join(dir, 'public')
const isAppDirEnabled = !!config.experimental.appDir
const initialRequireHookFilePath = require.resolve(
'next/dist/server/initialize-require-hook'
)
const content = await promises.readFile(
initialRequireHookFilePath,
'utf8'
)

if (isAppDirEnabled) {
process.env.NEXT_PREBUNDLED_REACT = '1'
}
await promises
.writeFile(
initialRequireHookFilePath,
content.replace(
/isPrebundled = (true|false)/,
`isPrebundled = ${isAppDirEnabled}`
)
)
.catch((err) => {
if (isAppDirEnabled) {
throw err
}
})

const { pagesDir, appDir } = findPagesDir(dir, isAppDirEnabled)
NextBuildContext.pagesDir = pagesDir
Expand Down Expand Up @@ -1067,10 +1047,9 @@ export default async function build(

const timeout = config.staticPageGenerationTimeout || 0
const sharedPool = config.experimental.sharedPool || false
const staticWorker = sharedPool
const staticWorkerPath = sharedPool
? require.resolve('./worker')
: require.resolve('./utils')
let infoPrinted = false

let appPathsManifest: Record<string, string> = {}
const appPathRoutes: Record<string, string> = {}
Expand Down Expand Up @@ -1111,69 +1090,89 @@ export default async function build(
4
)

const staticWorkers = new Worker(staticWorker, {
timeout: timeout * 1000,
onRestart: (method, [arg], attempts) => {
if (method === 'exportPage') {
const { path: pagePath } = arg
if (attempts >= 3) {
throw new Error(
`Static page generation for ${pagePath} is still timing out after 3 attempts. See more info here https://nextjs.org/docs/messages/static-page-generation-timeout`
function createStaticWorker(type: 'app' | 'pages') {
const numWorkersPerType = isAppDirEnabled
? Math.max(1, ~~(numWorkers / 2))
: numWorkers

let infoPrinted = false

return new Worker(staticWorkerPath, {
timeout: timeout * 1000,
onRestart: (method, [arg], attempts) => {
if (method === 'exportPage') {
const { path: pagePath } = arg
if (attempts >= 3) {
throw new Error(
`Static page generation for ${pagePath} is still timing out after 3 attempts. See more info here https://nextjs.org/docs/messages/static-page-generation-timeout`
)
}
Log.warn(
`Restarted static page generation for ${pagePath} because it took more than ${timeout} seconds`
)
} else {
const pagePath = arg
if (attempts >= 2) {
throw new Error(
`Collecting page data for ${pagePath} is still timing out after 2 attempts. See more info here https://nextjs.org/docs/messages/page-data-collection-timeout`
)
}
Log.warn(
`Restarted collecting page data for ${pagePath} because it took more than ${timeout} seconds`
)
}
Log.warn(
`Restarted static page generation for ${pagePath} because it took more than ${timeout} seconds`
)
} else {
const pagePath = arg
if (attempts >= 2) {
throw new Error(
`Collecting page data for ${pagePath} is still timing out after 2 attempts. See more info here https://nextjs.org/docs/messages/page-data-collection-timeout`
if (!infoPrinted) {
Log.warn(
'See more info here https://nextjs.org/docs/messages/static-page-generation-timeout'
)
infoPrinted = true
}
Log.warn(
`Restarted collecting page data for ${pagePath} because it took more than ${timeout} seconds`
)
}
if (!infoPrinted) {
Log.warn(
'See more info here https://nextjs.org/docs/messages/static-page-generation-timeout'
)
infoPrinted = true
}
},
numWorkers,
enableWorkerThreads: config.experimental.workerThreads,
computeWorkerKey(method, ...args) {
if (method === 'exportPage') {
const typedArgs = args as Parameters<
typeof import('./worker').exportPage
>
return typedArgs[0].pathMap.page
} else if (method === 'isPageStatic') {
const typedArgs = args as Parameters<
typeof import('./worker').isPageStatic
>
return typedArgs[0].originalAppPath || typedArgs[0].page
}
return method
},
exposedMethods: sharedPool
? [
'hasCustomGetInitialProps',
'isPageStatic',
'getNamedExports',
'exportPage',
]
: ['hasCustomGetInitialProps', 'isPageStatic', 'getNamedExports'],
}) as Worker &
Pick<
typeof import('./worker'),
| 'hasCustomGetInitialProps'
| 'isPageStatic'
| 'getNamedExports'
| 'exportPage'
>
},
numWorkers: numWorkersPerType,
forkOptions: {
env: {
...process.env,
NEXT_PREBUNDLED_REACT_WORKER: type === 'app' ? '1' : '',
__NEXT_PRIVATE_PREBUNDLED_REACT: type === 'app' ? '1' : '',
},
},
enableWorkerThreads: config.experimental.workerThreads,
computeWorkerKey(method, ...args) {
if (method === 'exportPage') {
const typedArgs = args as Parameters<
typeof import('./worker').exportPage
>
return typedArgs[0].pathMap.page
} else if (method === 'isPageStatic') {
const typedArgs = args as Parameters<
typeof import('./worker').isPageStatic
>
return typedArgs[0].originalAppPath || typedArgs[0].page
}
return method
},
exposedMethods: sharedPool
? [
'hasCustomGetInitialProps',
'isPageStatic',
'getNamedExports',
'exportPage',
]
: ['hasCustomGetInitialProps', 'isPageStatic', 'getNamedExports'],
}) as Worker &
Pick<
typeof import('./worker'),
| 'hasCustomGetInitialProps'
| 'isPageStatic'
| 'getNamedExports'
| 'exportPage'
>
}

const pagesStaticWorkers = createStaticWorker('pages')
const appStaticWorkers = isAppDirEnabled
? createStaticWorker('app')
: undefined

const analysisBegin = process.hrtime()
const staticCheckSpan = nextBuildSpan.traceChild('static-check')
Expand All @@ -1195,7 +1194,7 @@ export default async function build(
nonStaticErrorPageSpan.traceAsyncFn(
async () =>
hasCustomErrorPage &&
(await staticWorkers.hasCustomGetInitialProps(
(await pagesStaticWorkers.hasCustomGetInitialProps(
'/_error',
distDir,
runtimeEnvConfig,
Expand All @@ -1206,7 +1205,7 @@ export default async function build(
const errorPageStaticResult = nonStaticErrorPageSpan.traceAsyncFn(
async () =>
hasCustomErrorPage &&
staticWorkers.isPageStatic({
pagesStaticWorkers.isPageStatic({
page: '/_error',
distDir,
configFileName,
Expand All @@ -1222,14 +1221,14 @@ export default async function build(
const appPageToCheck = '/_app'

const customAppGetInitialPropsPromise =
staticWorkers.hasCustomGetInitialProps(
pagesStaticWorkers.hasCustomGetInitialProps(
appPageToCheck,
distDir,
runtimeEnvConfig,
true
)

const namedExportsPromise = staticWorkers.getNamedExports(
const namedExportsPromise = pagesStaticWorkers.getNamedExports(
appPageToCheck,
distDir,
runtimeEnvConfig
Expand Down Expand Up @@ -1385,7 +1384,11 @@ export default async function build(
checkPageSpan.traceChild('is-page-static')
let workerResult = await isPageStaticSpan.traceAsyncFn(
() => {
return staticWorkers.isPageStatic({
return (
pageType === 'app'
? appStaticWorkers
: pagesStaticWorkers
)!.isPageStatic({
page,
originalAppPath,
distDir,
Expand Down Expand Up @@ -1621,7 +1624,11 @@ export default async function build(
hasNonStaticErrorPage: nonStaticErrorPage,
}

if (!sharedPool) staticWorkers.end()
if (!sharedPool) {
pagesStaticWorkers.end()
appStaticWorkers?.end()
}

return returnValue
})

Expand Down Expand Up @@ -2290,12 +2297,16 @@ export default async function build(
pages: combinedPages,
outdir: path.join(distDir, 'export'),
statusMessage: 'Generating static pages',
exportAppPageWorker: sharedPool
? appStaticWorkers?.exportPage.bind(appStaticWorkers)
: undefined,
exportPageWorker: sharedPool
? staticWorkers.exportPage.bind(staticWorkers)
? pagesStaticWorkers.exportPage.bind(pagesStaticWorkers)
: undefined,
endWorker: sharedPool
? async () => {
await staticWorkers.end()
await pagesStaticWorkers.end()
await appStaticWorkers?.end()
}
: undefined,
}
Expand Down Expand Up @@ -2723,7 +2734,8 @@ export default async function build(
}

// ensure the worker is not left hanging
staticWorkers.close()
pagesStaticWorkers.close()
appStaticWorkers?.close()

const analysisEnd = process.hrtime(analysisBegin)
telemetry.record(
Expand Down
10 changes: 1 addition & 9 deletions packages/next/src/build/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { MiddlewareManifest } from './webpack/plugins/middleware-plugin'
import type { AppRouteUserlandModule } from '../server/future/route-modules/app-route/module'
import type { StaticGenerationAsyncStorage } from '../client/components/static-generation-async-storage'

import '../server/require-hook'
import '../server/node-polyfill-fetch'
import chalk from 'next/dist/compiled/chalk'
import getGzipSize from 'next/dist/compiled/gzip-size'
Expand Down Expand Up @@ -52,21 +53,12 @@ import { Sema } from 'next/dist/compiled/async-sema'
import { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-path'
import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path'
import { getRuntimeContext } from '../server/web/sandbox'
import {
loadRequireHook,
overrideBuiltInReactPackages,
} from './webpack/require-hook'
import { isClientReference } from '../lib/client-reference'
import { StaticGenerationAsyncStorageWrapper } from '../server/async-storage/static-generation-async-storage-wrapper'
import { IncrementalCache } from '../server/lib/incremental-cache'
import { patchFetch } from '../server/lib/patch-fetch'
import { nodeFs } from '../server/lib/node-fs-methods'

loadRequireHook()
if (process.env.NEXT_PREBUNDLED_REACT) {
overrideBuiltInReactPackages()
}

// expose AsyncLocalStorage on global for react usage
const { AsyncLocalStorage } = require('async_hooks')
;(globalThis as any).AsyncLocalStorage = AsyncLocalStorage
Expand Down
Loading