diff --git a/packages/next/src/client/components/navigation.ts b/packages/next/src/client/components/navigation.ts index 523870d9908ec..c4f977a2338a1 100644 --- a/packages/next/src/client/components/navigation.ts +++ b/packages/next/src/client/components/navigation.ts @@ -1,5 +1,3 @@ -// useLayoutSegments() // Only the segments for the current place. ['children', 'dashboard', 'children', 'integrations'] -> /dashboard/integrations (/dashboard/layout.js would get ['children', 'dashboard', 'children', 'integrations']) - import { useContext, useMemo } from 'react' import type { FlightRouterState } from '../../server/app-render/types' import { diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts index 85ecd19b0165f..7a3d9caaedefa 100644 --- a/packages/next/src/server/base-server.ts +++ b/packages/next/src/server/base-server.ts @@ -189,6 +189,8 @@ export type RequestContext = { renderOpts: RenderOptsPartial } +export type FallbackMode = false | undefined | 'blocking' | 'static' + export class NoFallbackError extends Error {} // Internal wrapper around build errors at development @@ -1439,7 +1441,8 @@ export default abstract class Server { getRequestMeta(req, '_nextRewroteUrl') || urlPathname let staticPaths: string[] | undefined - let fallbackMode: false | undefined | 'blocking' | 'static' + + let fallbackMode: FallbackMode if (isAppPath) { const pathsResult = await this.getStaticPaths({ diff --git a/packages/next/src/server/dev/next-dev-server.ts b/packages/next/src/server/dev/next-dev-server.ts index ee5a1b419ca90..97d482249f4be 100644 --- a/packages/next/src/server/dev/next-dev-server.ts +++ b/packages/next/src/server/dev/next-dev-server.ts @@ -7,7 +7,7 @@ import type { ParsedUrl } from '../../shared/lib/router/utils/parse-url' import type { ParsedUrlQuery } from 'querystring' import type { UrlWithParsedQuery } from 'url' import type { BaseNextRequest, BaseNextResponse } from '../base-http' -import type { MiddlewareRoutingItem } from '../base-server' +import type { FallbackMode, MiddlewareRoutingItem } from '../base-server' import type { FunctionComponent } from 'react' import type { RouteMatch } from '../future/route-matches/route-match' @@ -694,7 +694,7 @@ export default class DevServer extends Server { } const value: { staticPaths: string[] - fallbackMode: 'blocking' | 'static' | false | undefined + fallbackMode: FallbackMode } = { staticPaths, fallbackMode: diff --git a/packages/next/src/server/lib/router-server.ts b/packages/next/src/server/lib/router-server.ts index 93e8e2cc83138..5a82d8c1e8614 100644 --- a/packages/next/src/server/lib/router-server.ts +++ b/packages/next/src/server/lib/router-server.ts @@ -48,12 +48,10 @@ export type RenderWorker = InstanceType< propagateServerField: typeof import('./render-server').propagateServerField } -const nextDeleteCacheCallbacks: Array<(filePaths: string[]) => Promise> = - [] -const nextDeleteAppClientCacheCallbacks: Array<() => Promise> = [] -const nextClearModuleContextCallbacks: Array< - (targetPath: string) => Promise -> = [] +export interface RenderWorkers { + app?: Awaited> + pages?: Awaited> +} export async function initialize(opts: { dir: string @@ -95,6 +93,8 @@ export async function initialize(opts: { minimalMode: opts.minimalMode, }) + const renderWorkers: RenderWorkers = {} + let devInstance: | UnwrapPromise< ReturnType @@ -110,8 +110,11 @@ export async function initialize(opts: { !!config.experimental.appDir ) - const { setupDev } = await require('./router-utils/setup-dev') + const { setupDev } = + (await require('./router-utils/setup-dev')) as typeof import('./router-utils/setup-dev') devInstance = await setupDev({ + // Passed here but the initialization of this object happens below, doing the initialization before the setupDev call breaks. + renderWorkers, appDir, pagesDir, telemetry, @@ -123,22 +126,6 @@ export async function initialize(opts: { }) } - const renderWorkerOpts: Parameters[0] = { - port: opts.port, - dir: opts.dir, - workerType: 'render', - hostname: opts.hostname, - minimalMode: opts.minimalMode, - dev: !!opts.dev, - isNodeDebugging: !!opts.isNodeDebugging, - serverFields: devInstance?.serverFields || {}, - experimentalTestProxy: !!opts.experimentalTestProxy, - } - const renderWorkers: { - app?: RenderWorker - pages?: RenderWorker - } = {} - const { ipcPort, ipcValidationKey } = await createIpcServer({ async ensurePage( match: Parameters< @@ -200,16 +187,14 @@ export async function initialize(opts: { const { initialEnv } = require('@next/env') as typeof import('@next/env') - if (!!config.experimental.appDir) { - renderWorkers.app = await createWorker( - ipcPort, - ipcValidationKey, - opts.isNodeDebugging, - 'app', - config, - initialEnv - ) - } + renderWorkers.app = await createWorker( + ipcPort, + ipcValidationKey, + opts.isNodeDebugging, + 'app', + config, + initialEnv + ) renderWorkers.pages = await createWorker( ipcPort, ipcValidationKey, @@ -219,6 +204,18 @@ export async function initialize(opts: { initialEnv ) + const renderWorkerOpts: Parameters[0] = { + port: opts.port, + dir: opts.dir, + workerType: 'render', + hostname: opts.hostname, + minimalMode: opts.minimalMode, + dev: !!opts.dev, + isNodeDebugging: !!opts.isNodeDebugging, + serverFields: devInstance?.serverFields || {}, + experimentalTestProxy: !!opts.experimentalTestProxy, + } + // pre-initialize workers const initialized = { app: await renderWorkers.app?.initialize(renderWorkerOpts), @@ -226,51 +223,51 @@ export async function initialize(opts: { } if (devInstance) { - Object.assign(devInstance.renderWorkers, renderWorkers) - - nextDeleteCacheCallbacks.push((filePaths: string[]) => - Promise.all([ - renderWorkers.pages?.deleteCache(filePaths), - renderWorkers.app?.deleteCache(filePaths), - ]) - ) - nextDeleteAppClientCacheCallbacks.push(() => - Promise.all([ - renderWorkers.pages?.deleteAppClientCache(), - renderWorkers.app?.deleteAppClientCache(), - ]) - ) - nextClearModuleContextCallbacks.push((targetPath: string) => - Promise.all([ - renderWorkers.pages?.clearModuleContext(targetPath), - renderWorkers.app?.clearModuleContext(targetPath), - ]) - ) + const originalNextDeleteCache = (global as any)._nextDeleteCache ;(global as any)._nextDeleteCache = async (filePaths: string[]) => { - for (const cb of nextDeleteCacheCallbacks) { - try { - await cb(filePaths) - } catch (err) { - console.error(err) - } + // Multiple instances of Next.js can be instantiated, since this is a global we have to call the original if it exists. + if (originalNextDeleteCache) { + await originalNextDeleteCache(filePaths) + } + try { + await Promise.all([ + renderWorkers.pages?.deleteCache(filePaths), + renderWorkers.app?.deleteCache(filePaths), + ]) + } catch (err) { + console.error(err) } } + const originalNextDeleteAppClientCache = (global as any) + ._nextDeleteAppClientCache ;(global as any)._nextDeleteAppClientCache = async () => { - for (const cb of nextDeleteAppClientCacheCallbacks) { - try { - await cb() - } catch (err) { - console.error(err) - } + // Multiple instances of Next.js can be instantiated, since this is a global we have to call the original if it exists. + if (originalNextDeleteAppClientCache) { + await originalNextDeleteAppClientCache() + } + try { + await Promise.all([ + renderWorkers.pages?.deleteAppClientCache(), + renderWorkers.app?.deleteAppClientCache(), + ]) + } catch (err) { + console.error(err) } } + const originalNextClearModuleContext = (global as any) + ._nextClearModuleContext ;(global as any)._nextClearModuleContext = async (targetPath: string) => { - for (const cb of nextClearModuleContextCallbacks) { - try { - await cb(targetPath) - } catch (err) { - console.error(err) - } + // Multiple instances of Next.js can be instantiated, since this is a global we have to call the original if it exists. + if (originalNextClearModuleContext) { + await originalNextClearModuleContext() + } + try { + await Promise.all([ + renderWorkers.pages?.clearModuleContext(targetPath), + renderWorkers.app?.clearModuleContext(targetPath), + ]) + } catch (err) { + console.error(err) } } } diff --git a/packages/next/src/server/lib/router-utils/setup-dev.ts b/packages/next/src/server/lib/router-utils/setup-dev.ts index 90b59c614879d..a87ae086c4507 100644 --- a/packages/next/src/server/lib/router-utils/setup-dev.ts +++ b/packages/next/src/server/lib/router-utils/setup-dev.ts @@ -85,8 +85,10 @@ import { srcEmptySsgManifest } from '../../../build/webpack/plugins/build-manife import { PropagateToWorkersField } from './types' import { MiddlewareManifest } from '../../../build/webpack/plugins/middleware-plugin' import { devPageFiles } from '../../../build/webpack/plugins/next-types-plugin/shared' +import type { RenderWorkers } from '../router-server' type SetupOpts = { + renderWorkers: RenderWorkers dir: string turbo?: boolean appDir?: string @@ -133,14 +135,9 @@ async function startWatcher(opts: SetupOpts) { appDir ) - const renderWorkers: { - app?: import('../router-server').RenderWorker - pages?: import('../router-server').RenderWorker - } = {} - async function propagateToWorkers(field: PropagateToWorkersField, args: any) { - await renderWorkers.app?.propagateServerField(field, args) - await renderWorkers.pages?.propagateServerField(field, args) + await opts.renderWorkers.app?.propagateServerField(field, args) + await opts.renderWorkers.pages?.propagateServerField(field, args) } let hotReloader: InstanceType @@ -1437,9 +1434,7 @@ async function startWatcher(opts: SetupOpts) { return { serverFields, - hotReloader, - renderWorkers, requestHandler, logErrorWithOriginalStack,