diff --git a/packages/next/build/utils.ts b/packages/next/build/utils.ts index 51dc98dc76d0c..f3daa62c21abe 100644 --- a/packages/next/build/utils.ts +++ b/packages/next/build/utils.ts @@ -59,6 +59,10 @@ if (process.env.NEXT_PREBUNDLED_REACT) { overrideBuiltInReactPackages() } +// expose AsyncLocalStorage on global for react usage +const { AsyncLocalStorage } = require('async_hooks') +;(global as any).AsyncLocalStorage = AsyncLocalStorage + export type ROUTER_TYPE = 'pages' | 'app' const RESERVED_PAGE = /^\/(_app|_error|_document|api(\/|$))/ diff --git a/packages/next/client/components/request-async-storage.ts b/packages/next/client/components/request-async-storage.ts index 6b31ab2f371d1..d3d97e8c05ffc 100644 --- a/packages/next/client/components/request-async-storage.ts +++ b/packages/next/client/components/request-async-storage.ts @@ -13,6 +13,8 @@ export interface RequestStore { export let requestAsyncStorage: AsyncLocalStorage | RequestStore = {} as any -if (process.env.NEXT_RUNTIME !== 'edge' && typeof window === 'undefined') { - requestAsyncStorage = new (require('async_hooks').AsyncLocalStorage)() +// @ts-expect-error we provide this on global in +// the edge and node runtime +if (global.AsyncLocalStorage) { + requestAsyncStorage = new (global as any).AsyncLocalStorage() } diff --git a/packages/next/client/components/static-generation-async-storage.ts b/packages/next/client/components/static-generation-async-storage.ts index 36d4034b83b08..39be1e0eaa162 100644 --- a/packages/next/client/components/static-generation-async-storage.ts +++ b/packages/next/client/components/static-generation-async-storage.ts @@ -1,4 +1,19 @@ -export { - staticGenerationAsyncStorage, - StaticGenerationStore, -} from './static-generation-async-storage/storage.js' +import type { AsyncLocalStorage } from 'async_hooks' + +export interface StaticGenerationStore { + inUse?: boolean + pathname?: string + revalidate?: number + fetchRevalidate?: number + isStaticGeneration?: boolean +} + +export let staticGenerationAsyncStorage: + | AsyncLocalStorage + | StaticGenerationStore = {} + +// @ts-expect-error we provide this on global in +// the edge and node runtime +if (global.AsyncLocalStorage) { + staticGenerationAsyncStorage = new (global as any).AsyncLocalStorage() +} diff --git a/packages/next/client/components/static-generation-async-storage/package.json b/packages/next/client/components/static-generation-async-storage/package.json deleted file mode 100644 index e1bbf57be87ee..0000000000000 --- a/packages/next/client/components/static-generation-async-storage/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "browser": { - "./storage.js": "./storage-browser.js" - } -} diff --git a/packages/next/client/components/static-generation-async-storage/storage-browser.ts b/packages/next/client/components/static-generation-async-storage/storage-browser.ts deleted file mode 100644 index 3f9b9fe75d5b6..0000000000000 --- a/packages/next/client/components/static-generation-async-storage/storage-browser.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { AsyncLocalStorage } from 'async_hooks' - -export interface StaticGenerationStore { - inUse?: boolean - pathname?: string - revalidate?: number - fetchRevalidate?: number - isStaticGeneration?: boolean -} - -export let staticGenerationAsyncStorage: - | AsyncLocalStorage - | StaticGenerationStore = {} diff --git a/packages/next/client/components/static-generation-async-storage/storage.ts b/packages/next/client/components/static-generation-async-storage/storage.ts deleted file mode 100644 index f3abd89db0413..0000000000000 --- a/packages/next/client/components/static-generation-async-storage/storage.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { AsyncLocalStorage } from 'async_hooks' - -export interface StaticGenerationStore { - inUse?: boolean - pathname?: string - revalidate?: number - fetchRevalidate?: number - isStaticGeneration?: boolean -} - -export let staticGenerationAsyncStorage: - | AsyncLocalStorage - | StaticGenerationStore = {} - -if (process.env.NEXT_RUNTIME !== 'edge' && typeof window === 'undefined') { - staticGenerationAsyncStorage = - new (require('async_hooks').AsyncLocalStorage)() -} diff --git a/packages/next/export/worker.ts b/packages/next/export/worker.ts index e44dc99e6787e..64e738e5da2e5 100644 --- a/packages/next/export/worker.ts +++ b/packages/next/export/worker.ts @@ -100,6 +100,10 @@ interface RenderOpts { supportsDynamicHTML?: boolean } +// expose AsyncLocalStorage on global for react usage +const { AsyncLocalStorage } = require('async_hooks') +;(global as any).AsyncLocalStorage = AsyncLocalStorage + export default async function exportPage({ parentSpanId, path, diff --git a/packages/next/server/dev/static-paths-worker.ts b/packages/next/server/dev/static-paths-worker.ts index a6f4b2453b0b8..21368bc9e774a 100644 --- a/packages/next/server/dev/static-paths-worker.ts +++ b/packages/next/server/dev/static-paths-worker.ts @@ -22,6 +22,10 @@ if (process.env.NEXT_PREBUNDLED_REACT) { let workerWasUsed = false +// expose AsyncLocalStorage on global for react usage +const { AsyncLocalStorage } = require('async_hooks') +;(global as any).AsyncLocalStorage = AsyncLocalStorage + // we call getStaticPaths in a separate process to ensure // side-effects aren't relied on in dev that will break // during a production build diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 44974e620d6d6..bc4a2f90eaf76 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -256,11 +256,10 @@ export default class NextNodeServer extends BaseServer { }).catch(() => {}) } - if (this.nextConfig.experimental.appDir) { - // expose AsyncLocalStorage on global for react usage - const { AsyncLocalStorage } = require('async_hooks') - ;(global as any).AsyncLocalStorage = AsyncLocalStorage - } + // expose AsyncLocalStorage on global for react usage + const { AsyncLocalStorage } = require('async_hooks') + ;(global as any).AsyncLocalStorage = AsyncLocalStorage + // ensure options are set when loadConfig isn't called setHttpClientAndAgentOptions(this.nextConfig) } @@ -1775,7 +1774,7 @@ export default class NextNodeServer extends BaseServer { page: page, body: getRequestMeta(params.request, '__NEXT_CLONABLE_BODY'), }, - useCache: false, + useCache: !this.renderOpts.dev, onWarning: params.onWarning, }) @@ -2132,7 +2131,7 @@ export default class NextNodeServer extends BaseServer { }, body: getRequestMeta(params.req, '__NEXT_CLONABLE_BODY'), }, - useCache: false, + useCache: !this.renderOpts.dev, onWarning: params.onWarning, }) diff --git a/packages/next/server/web/sandbox/context.ts b/packages/next/server/web/sandbox/context.ts index da0d334f979c5..6f44c5883cabc 100644 --- a/packages/next/server/web/sandbox/context.ts +++ b/packages/next/server/web/sandbox/context.ts @@ -14,6 +14,7 @@ import { validateURL } from '../utils' import { pick } from '../../../lib/pick' import { fetchInlineAsset } from './fetch-inline-assets' import type { EdgeFunctionDefinition } from '../../../build/webpack/plugins/middleware-plugin' +import { UnwrapPromise } from '../../../lib/coalesced-function' const WEBPACK_HASH_REGEX = /__webpack_require__\.h = function\(\) \{ return "[0-9a-f]+"; \}/g @@ -319,8 +320,15 @@ interface ModuleContextOptions { edgeFunctionEntry: Pick } +const pendingModuleCaches = new Map>() + function getModuleContextShared(options: ModuleContextOptions) { - return createModuleContext(options) + let deferredModuleContext = pendingModuleCaches.get(options.moduleName) + if (!deferredModuleContext) { + deferredModuleContext = createModuleContext(options) + pendingModuleCaches.set(options.moduleName, deferredModuleContext) + } + return deferredModuleContext } /** @@ -335,9 +343,15 @@ export async function getModuleContext(options: ModuleContextOptions): Promise<{ paths: Map warnedEvals: Set }> { - let moduleContext = options.useCache - ? moduleContexts.get(options.moduleName) - : await getModuleContextShared(options) + let moduleContext: + | UnwrapPromise> + | undefined + + if (options.useCache) { + moduleContext = + moduleContexts.get(options.moduleName) || + (await getModuleContextShared(options)) + } if (!moduleContext) { moduleContext = await createModuleContext(options) diff --git a/packages/next/taskfile.js b/packages/next/taskfile.js index 6a42e3662120f..77debab26fe63 100644 --- a/packages/next/taskfile.js +++ b/packages/next/taskfile.js @@ -2108,11 +2108,7 @@ export async function compile(task, opts) { ], opts ) - await task.serial([ - 'ncc_react_refresh_utils', - 'ncc_next__react_dev_overlay', - 'copy_package_json', - ]) + await task.serial(['ncc_react_refresh_utils', 'ncc_next__react_dev_overlay']) } export async function bin(task, opts) { @@ -2199,29 +2195,6 @@ export async function nextbuildjest(task, opts) { notify('Compiled build/jest files') } -export async function copy_package_json(task, opts) { - await fs.copy( - join( - __dirname, - 'client/components/static-generation-async-storage/package.json' - ), - join( - __dirname, - 'dist/client/components/static-generation-async-storage/package.json' - ) - ) - await fs.copy( - join( - __dirname, - 'client/components/static-generation-async-storage/package.json' - ), - join( - __dirname, - 'dist/esm/client/components/static-generation-async-storage/package.json' - ) - ) -} - export async function client(task, opts) { await task .source(opts.src || 'client/**/*.+(js|ts|tsx)')