Skip to content

Commit 7eea785

Browse files
committed
Replace server fields with typed dev state
1 parent a83f2fe commit 7eea785

12 files changed

Lines changed: 171 additions & 183 deletions

packages/next/errors.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1322,5 +1322,7 @@
13221322
"1321": "\\`partialPrefetching\\` requires \\`cacheComponents\\` to be enabled. Please update your %s accordingly.",
13231323
"1322": "Route \"%s\" cannot use \\`export const prefetch = ...\\` without enabling \\`cacheComponents\\`.",
13241324
"1323": "\"prefetch\" is a route segment config and can only be used when the segment is a Server Component module. Remove the \"use client\" directive from \"%s\" to use this API.",
1325-
"1324": "Invariant: dev virtual filesystem item was not handled by the development bundler"
1325+
"1324": "Invariant: dev virtual filesystem item was not handled by the development bundler",
1326+
"1325": "Invariant: dev server state can only be updated in dev",
1327+
"1326": "Invariant: no app initialized"
13261328
}

packages/next/src/server/base-server.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,15 @@ export interface Options {
248248
* The HTTP Server that Next.js is running behind
249249
*/
250250
httpServer?: HTTPServer
251+
/**
252+
* The router server handler used for internal requests.
253+
*
254+
* @internal
255+
*/
256+
routerServerHandler?: (
257+
req: IncomingMessage,
258+
res: HTTPServerResponse
259+
) => Promise<void> | void
251260
}
252261

253262
export type RenderOpts = PagesRenderOptsPartial & AppRenderOptsPartial
@@ -341,7 +350,7 @@ export default abstract class Server<
341350
protected readonly minimalMode: boolean
342351
protected readonly renderOpts: BaseRenderOpts
343352
protected readonly serverOptions: Readonly<ServerOptions>
344-
protected readonly appPathRoutes?: Record<string, string[]>
353+
protected appPathRoutes?: Record<string, string[]>
345354
protected readonly clientReferenceManifest?: DeepReadonly<ClientReferenceManifest>
346355
protected interceptionRoutePatterns: RegExp[]
347356
protected nextFontManifest?: DeepReadonly<NextFontManifest>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { ProxyMatcher } from '../../build/analysis/get-page-static-info'
2+
3+
export type DevServerState = {
4+
actualMiddlewareFile?: string
5+
actualInstrumentationHookFile?: string
6+
appPathRoutes?: Record<string, string[]>
7+
middleware?: {
8+
page: string
9+
matchers?: ProxyMatcher[]
10+
}
11+
}
12+
13+
export type DevServerStateUpdate = Partial<DevServerState>

packages/next/src/server/dev/hot-reloader-turbopack.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ import {
6262
type StartChangeSubscription,
6363
} from './turbopack-utils'
6464
import {
65-
propagateServerField,
66-
type ServerFields,
6765
type SetupOpts,
66+
updateDevServerState,
6867
} from '../lib/router-utils/setup-dev-bundler'
68+
import type { DevServerState } from './dev-server-state'
6969
import { TurbopackManifestLoader } from '../../shared/lib/turbopack/manifest-loader'
7070
import { findPagePathData } from './on-demand-entry-handler'
7171
import type { RouteDefinition } from '../route-definitions/route-definition'
@@ -306,7 +306,7 @@ function getSourceMapFromTurbopack(
306306

307307
export async function createHotReloaderTurbopack(
308308
opts: SetupOpts & { isSrcDir: boolean },
309-
serverFields: ServerFields,
309+
devServerState: DevServerState,
310310
distDir: string,
311311
resetFetch: () => void,
312312
lockfile: Lockfile | undefined,
@@ -943,14 +943,17 @@ export async function createHotReloaderTurbopack(
943943
...clientsByHtmlRequestId.values(),
944944
],
945945
clientStates,
946-
serverFields,
947946

948947
hooks: {
949948
handleWrittenEndpoint: (id, result, forceDeleteCache) => {
950949
currentWrittenEntrypoints.set(id, result)
951950
return clearRequireCache(id, result, { force: forceDeleteCache })
952951
},
953-
propagateServerField: propagateServerField.bind(null, opts),
952+
updateDevServerState: updateDevServerState.bind(
953+
null,
954+
opts,
955+
devServerState
956+
),
954957
sendHmr,
955958
startBuilding,
956959
subscribeToChanges: subscribeToClientChanges,

packages/next/src/server/dev/next-dev-server.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ import type { PrerenderManifest } from '../../build'
7979
import { getRouteRegex } from '../../shared/lib/router/utils/route-regex'
8080
import type { PrerenderedRoute } from '../../build/static-paths/types'
8181
import { HMR_MESSAGE_SENT_TO_BROWSER } from './hot-reloader-types'
82+
import type { DevServerStateUpdate } from './dev-server-state'
8283

8384
// Load ReactDevOverlay only when needed
8485
let PagesDevOverlayBridgeImpl: PagesDevOverlayBridgeType
@@ -226,6 +227,27 @@ export default class DevServer extends Server {
226227
return this.serverComponentsHmrCache
227228
}
228229

230+
public override updateDevServerState(update: DevServerStateUpdate): void {
231+
if ('actualMiddlewareFile' in update) {
232+
this.actualMiddlewareFile = update.actualMiddlewareFile
233+
}
234+
if ('actualInstrumentationHookFile' in update) {
235+
this.actualInstrumentationHookFile = update.actualInstrumentationHookFile
236+
}
237+
if ('middleware' in update) {
238+
this.middleware = update.middleware
239+
? {
240+
...update.middleware,
241+
match: getMiddlewareRouteMatcher(update.middleware.matchers || []),
242+
}
243+
: undefined
244+
}
245+
if ('appPathRoutes' in update) {
246+
this.appPathRoutes = update.appPathRoutes
247+
this.interceptionRoutePatterns = this.getinterceptionRoutePatterns()
248+
}
249+
}
250+
229251
protected getBuildId(): string {
230252
return 'development'
231253
}
@@ -594,13 +616,6 @@ export default class DevServer extends Server {
594616
}
595617

596618
protected async getMiddleware() {
597-
// We need to populate the match
598-
// field as it isn't serializable
599-
if (this.middleware?.match === null) {
600-
this.middleware.match = getMiddlewareRouteMatcher(
601-
this.middleware.matchers || []
602-
)
603-
}
604619
return this.middleware
605620
}
606621

packages/next/src/server/dev/turbopack-utils.ts

Lines changed: 19 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import type {
2-
ServerFields,
3-
SetupOpts,
4-
} from '../lib/router-utils/setup-dev-bundler'
1+
import type { SetupOpts } from '../lib/router-utils/setup-dev-bundler'
2+
import type { DevServerStateUpdate } from './dev-server-state'
53
import type {
64
Issue,
75
TurbopackResult,
@@ -16,7 +14,6 @@ import {
1614
} from './hot-reloader-types'
1715
import * as Log from '../../build/output/log'
1816
import { warnAboutEdgeRuntime } from '../../build/warn-about-edge-runtime'
19-
import type { PropagateToWorkersField } from '../lib/router-utils/types'
2017
import type { TurbopackManifestLoader } from '../../shared/lib/turbopack/manifest-loader'
2118
import type { AppRoute, Entrypoints, PageRoute } from '../../build/swc/types'
2219
import {
@@ -547,10 +544,7 @@ export function hasEntrypointForKey(
547544
// hooks only used by the dev server.
548545
type HandleEntrypointsHooks = {
549546
handleWrittenEndpoint: HandleWrittenEndpoint
550-
propagateServerField: (
551-
field: PropagateToWorkersField,
552-
args: any
553-
) => Promise<void>
547+
updateDevServerState: (update: DevServerStateUpdate) => Promise<void>
554548
sendHmr: SendHmr
555549
startBuilding: StartBuilding
556550
subscribeToChanges: StartChangeSubscription
@@ -563,7 +557,6 @@ type HandleEntrypointsDevOpts = {
563557
changeSubscriptions: ChangeSubscriptions
564558
clients: Array<ws>
565559
clientStates: ClientStateMap
566-
serverFields: ServerFields
567560

568561
hooks: HandleEntrypointsHooks
569562
}
@@ -691,17 +684,13 @@ export async function handleEntrypoints({
691684
entrypoints: currentEntrypoints,
692685
})
693686

694-
dev.serverFields.actualInstrumentationHookFile = '/instrumentation'
695-
await dev.hooks.propagateServerField(
696-
'actualInstrumentationHookFile',
697-
dev.serverFields.actualInstrumentationHookFile
698-
)
687+
await dev.hooks.updateDevServerState({
688+
actualInstrumentationHookFile: '/instrumentation',
689+
})
699690
} else {
700-
dev.serverFields.actualInstrumentationHookFile = undefined
701-
await dev.hooks.propagateServerField(
702-
'actualInstrumentationHookFile',
703-
dev.serverFields.actualInstrumentationHookFile
704-
)
691+
await dev.hooks.updateDevServerState({
692+
actualInstrumentationHookFile: undefined,
693+
})
705694
}
706695

707696
if (middleware) {
@@ -726,11 +715,12 @@ export async function handleEntrypoints({
726715
manifestLoader.getMiddlewareManifest(key)?.middleware['/']
727716

728717
if (dev && middlewareConfig) {
729-
dev.serverFields.middleware = {
730-
match: null as any,
731-
page: '/',
732-
matchers: middlewareConfig.matchers,
733-
}
718+
await dev.hooks.updateDevServerState({
719+
middleware: {
720+
page: '/',
721+
matchers: middlewareConfig.matchers,
722+
},
723+
})
734724
}
735725
finishBuilding()
736726
}
@@ -748,14 +738,6 @@ export async function handleEntrypoints({
748738
true
749739
)
750740
await processMiddleware()
751-
await dev.hooks.propagateServerField(
752-
'actualMiddlewareFile',
753-
dev.serverFields.actualMiddlewareFile
754-
)
755-
await dev.hooks.propagateServerField(
756-
'middleware',
757-
dev.serverFields.middleware
758-
)
759741
manifestLoader.writeManifests({
760742
devRewrites,
761743
productionRewrites: undefined,
@@ -778,18 +760,11 @@ export async function handleEntrypoints({
778760
manifestLoader.deleteMiddlewareManifest(
779761
getEntryKey('root', 'server', 'middleware')
780762
)
781-
dev.serverFields.actualMiddlewareFile = undefined
782-
dev.serverFields.middleware = undefined
763+
await dev.hooks.updateDevServerState({
764+
actualMiddlewareFile: undefined,
765+
middleware: undefined,
766+
})
783767
}
784-
785-
await dev.hooks.propagateServerField(
786-
'actualMiddlewareFile',
787-
dev.serverFields.actualMiddlewareFile
788-
)
789-
await dev.hooks.propagateServerField(
790-
'middleware',
791-
dev.serverFields.middleware
792-
)
793768
}
794769

795770
async function handleEntrypointsDevCleanup({

packages/next/src/server/lib/render-server.ts

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import type { NextServer, RequestHandler, UpgradeHandler } from '../next'
22
import type { DevBundlerService } from './dev-bundler-service'
3-
import type { PropagateToWorkersField } from './router-utils/types'
3+
import type {
4+
DevServerState,
5+
DevServerStateUpdate,
6+
} from '../dev/dev-server-state'
7+
import type { WorkerRequestHandler } from './types'
48

59
import next from '../next'
610
import type { Span } from '../../trace'
@@ -43,44 +47,27 @@ export function clearModuleContext(target: string) {
4347
return sandboxContext?.clearModuleContext(target)
4448
}
4549

46-
export async function getServerField(
47-
dir: string,
48-
field: PropagateToWorkersField
49-
) {
50+
async function getInitialization(dir: string) {
5051
const initialization = await initializations[dir]
5152
if (!initialization) {
52-
throw new Error('Invariant cant propagate server field, no app initialized')
53+
throw new Error('Invariant: no app initialized')
5354
}
54-
const { server } = initialization
55-
let wrappedServer = server['server']! // NextServer.server is private
56-
return wrappedServer[field as keyof typeof wrappedServer]
55+
return initialization
5756
}
5857

59-
export async function propagateServerField(
58+
export async function updateDevServerState(
6059
dir: string,
61-
field: PropagateToWorkersField,
62-
value: any
60+
update: DevServerStateUpdate
6361
) {
64-
const initialization = await initializations[dir]
65-
if (!initialization) {
66-
throw new Error('Invariant cant propagate server field, no app initialized')
67-
}
62+
const initialization = await getInitialization(dir)
6863
const { server } = initialization
69-
let wrappedServer = server['server']
70-
const _field = field as keyof NonNullable<typeof wrappedServer>
71-
72-
if (wrappedServer) {
73-
if (typeof wrappedServer[_field] === 'function') {
74-
// @ts-expect-error
75-
await wrappedServer[_field].apply(
76-
wrappedServer,
77-
Array.isArray(value) ? value : []
78-
)
79-
} else {
80-
// @ts-expect-error
81-
wrappedServer[_field] = value
82-
}
83-
}
64+
await server.updateDevServerState(update)
65+
}
66+
67+
export async function reloadEnv(dir: string) {
68+
const initialization = await getInitialization(dir)
69+
const { server } = initialization
70+
await server.reloadEnv()
8471
}
8572

8673
async function initializeImpl(opts: {
@@ -90,7 +77,8 @@ async function initializeImpl(opts: {
9077
minimalMode?: boolean
9178
hostname?: string
9279
keepAliveTimeout?: number
93-
serverFields?: any
80+
devServerState?: DevServerState
81+
routerServerHandler?: WorkerRequestHandler
9482
server?: any
9583
experimentalTestProxy: boolean
9684
experimentalHttpsServer: boolean
@@ -112,8 +100,9 @@ async function initializeImpl(opts: {
112100
let requestHandler: RequestHandler
113101
let upgradeHandler: UpgradeHandler
114102

103+
const { devServerState, ...serverOptions } = opts
115104
const server = next({
116-
...opts,
105+
...serverOptions,
117106
hostname: opts.hostname || 'localhost',
118107
customServer: false,
119108
httpServer: opts.server,
@@ -169,7 +158,10 @@ async function initializeImpl(opts: {
169158
upgradeHandler = server.getUpgradeHandler()
170159
}
171160

172-
await server.prepare(opts.serverFields)
161+
if (devServerState) {
162+
await server.updateDevServerState(devServerState)
163+
}
164+
await server.prepare()
173165

174166
return {
175167
requestHandler,

0 commit comments

Comments
 (0)