Skip to content

Commit a69b5a4

Browse files
unstubbableForsakenHarmony
authored andcommitted
Clean up require.cache handling (#68743)
Follow-up from #68535. Since the `require.cache` handling is not specific for Webpack, we move the helper functions out of `webpack/plugins/nextjs-require-cache-hot-reloader.ts`. In addition, when deleting an entry from the `require.cache`, while cleaning up its references in the `children` of parent modules, we now consider all entries in the `require.cache` instead of relying on a list of "origin modules". This list had entries that were not needed, most notably the server runtime bundles (the experimental runtimes were even missing), and at least one (`node-manifest-loader`) was also missing. This has a negligible effect on performance. Handling 1200 `require.cache` entries takes 0.3ms on an M2. The approach was also previously aligned with @sokra.
1 parent 99982fc commit a69b5a4

File tree

6 files changed

+61
-81
lines changed

6 files changed

+61
-81
lines changed

packages/next/src/build/webpack/plugins/nextjs-require-cache-hot-reloader.ts

Lines changed: 1 addition & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,12 @@
11
import type { webpack } from 'next/dist/compiled/webpack/webpack'
2+
import { deleteCache } from '../../../server/dev/require-cache'
23
import { clearModuleContext } from '../../../server/web/sandbox'
3-
import { realpathSync } from '../../../lib/realpath'
44
import path from 'path'
5-
import isError from '../../../lib/is-error'
6-
import { clearManifestCache } from '../../../server/load-manifest'
75

86
type Compiler = webpack.Compiler
97
type WebpackPluginInstance = webpack.WebpackPluginInstance
108

11-
const originModules = [
12-
require.resolve('../../../server/require'),
13-
require.resolve('../../../server/load-components'),
14-
require.resolve('../../../server/next-server'),
15-
require.resolve('next/dist/compiled/next-server/app-page.runtime.dev.js'),
16-
require.resolve('next/dist/compiled/next-server/app-route.runtime.dev.js'),
17-
require.resolve('next/dist/compiled/next-server/pages.runtime.dev.js'),
18-
require.resolve('next/dist/compiled/next-server/pages-api.runtime.dev.js'),
19-
]
20-
219
const RUNTIME_NAMES = ['webpack-runtime', 'webpack-api-runtime']
22-
23-
function deleteFromRequireCache(filePath: string) {
24-
try {
25-
filePath = realpathSync(filePath)
26-
} catch (e) {
27-
if (isError(e) && e.code !== 'ENOENT') throw e
28-
}
29-
const mod = require.cache[filePath]
30-
if (mod) {
31-
// remove the child reference from the originModules
32-
for (const originModule of originModules) {
33-
const parent = require.cache[originModule]
34-
if (parent) {
35-
const idx = parent.children.indexOf(mod)
36-
if (idx >= 0) parent.children.splice(idx, 1)
37-
}
38-
}
39-
// remove parent references from external modules
40-
for (const child of mod.children) {
41-
child.parent = null
42-
}
43-
delete require.cache[filePath]
44-
return true
45-
}
46-
return false
47-
}
48-
49-
export function deleteAppClientCache() {
50-
deleteFromRequireCache(
51-
require.resolve('next/dist/compiled/next-server/app-page.runtime.dev.js')
52-
)
53-
deleteFromRequireCache(
54-
require.resolve(
55-
'next/dist/compiled/next-server/app-page-experimental.runtime.dev.js'
56-
)
57-
)
58-
}
59-
60-
export function deleteCache(filePath: string) {
61-
// try to clear it from the fs cache
62-
clearManifestCache(filePath)
63-
64-
deleteFromRequireCache(filePath)
65-
}
66-
6710
const PLUGIN_NAME = 'NextJsRequireCacheHotReloader'
6811

6912
// This plugin flushes require.cache after emitting the files. Providing 'hot reloading' of server files.

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,7 @@ import { BLOCKED_PAGES } from '../../shared/lib/constants'
3131
import { getOverlayMiddleware } from '../../client/components/react-dev-overlay/server/middleware-turbopack'
3232
import { PageNotFoundError } from '../../shared/lib/utils'
3333
import { debounce } from '../utils'
34-
import {
35-
deleteAppClientCache,
36-
deleteCache,
37-
} from '../../build/webpack/plugins/nextjs-require-cache-hot-reloader'
34+
import { deleteAppClientCache, deleteCache } from './require-cache'
3835
import {
3936
clearAllModuleContexts,
4037
clearModuleContext,
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import isError from '../../lib/is-error'
2+
import { realpathSync } from '../../lib/realpath'
3+
import { clearManifestCache } from '../load-manifest'
4+
5+
function deleteFromRequireCache(filePath: string) {
6+
try {
7+
filePath = realpathSync(filePath)
8+
} catch (e) {
9+
if (isError(e) && e.code !== 'ENOENT') throw e
10+
}
11+
const mod = require.cache[filePath]
12+
if (mod) {
13+
// remove the child reference from all parent modules
14+
for (const parent of Object.values(require.cache)) {
15+
if (parent?.children) {
16+
const idx = parent.children.indexOf(mod)
17+
if (idx >= 0) parent.children.splice(idx, 1)
18+
}
19+
}
20+
// remove parent references from external modules
21+
for (const child of mod.children) {
22+
child.parent = null
23+
}
24+
delete require.cache[filePath]
25+
return true
26+
}
27+
return false
28+
}
29+
30+
export function deleteAppClientCache() {
31+
deleteFromRequireCache(
32+
require.resolve('next/dist/compiled/next-server/app-page.runtime.dev.js')
33+
)
34+
deleteFromRequireCache(
35+
require.resolve(
36+
'next/dist/compiled/next-server/app-page-experimental.runtime.dev.js'
37+
)
38+
)
39+
}
40+
41+
export function deleteCache(filePath: string) {
42+
// try to clear it from the fs cache
43+
clearManifestCache(filePath)
44+
45+
deleteFromRequireCache(filePath)
46+
}

packages/next/src/server/dev/turbopack/manifest-loader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
import { join, posix } from 'path'
2828
import { readFile, writeFile } from 'fs/promises'
2929
import type { SetupOpts } from '../../lib/router-utils/setup-dev-bundler'
30-
import { deleteCache } from '../../../build/webpack/plugins/nextjs-require-cache-hot-reloader'
30+
import { deleteCache } from '../require-cache'
3131
import { writeFileAtomic } from '../../../lib/fs/write-atomic'
3232
import { isInterceptionRouteRewrite } from '../../../lib/generate-interception-routes-rewrites'
3333
import {

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

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,9 @@ let initializations: Record<
2020
> = {}
2121

2222
let sandboxContext: undefined | typeof import('../web/sandbox/context')
23-
let requireCacheHotReloader:
24-
| undefined
25-
| typeof import('../../build/webpack/plugins/nextjs-require-cache-hot-reloader')
2623

2724
if (process.env.NODE_ENV !== 'production') {
2825
sandboxContext = require('../web/sandbox/context')
29-
requireCacheHotReloader = require('../../build/webpack/plugins/nextjs-require-cache-hot-reloader')
3026
}
3127

3228
export function clearAllModuleContexts() {
@@ -37,14 +33,17 @@ export function clearModuleContext(target: string) {
3733
return sandboxContext?.clearModuleContext(target)
3834
}
3935

40-
export function deleteAppClientCache() {
41-
return requireCacheHotReloader?.deleteAppClientCache()
42-
}
43-
44-
export function deleteCache(filePaths: string[]) {
45-
for (const filePath of filePaths) {
46-
requireCacheHotReloader?.deleteCache(filePath)
36+
export async function getServerField(
37+
dir: string,
38+
field: PropagateToWorkersField
39+
) {
40+
const initialization = await initializations[dir]
41+
if (!initialization) {
42+
throw new Error('Invariant cant propagate server field, no app initialized')
4743
}
44+
const { app } = initialization
45+
let appField = (app as any).server
46+
return appField[field]
4847
}
4948

5049
export async function propagateServerField(
@@ -77,7 +76,6 @@ async function initializeImpl(opts: {
7776
dev: boolean
7877
minimalMode?: boolean
7978
hostname?: string
80-
isNodeDebugging: boolean
8179
keepAliveTimeout?: number
8280
serverFields?: any
8381
server?: any
@@ -87,6 +85,7 @@ async function initializeImpl(opts: {
8785
_ipcKey?: string
8886
bundlerService: DevBundlerService | undefined
8987
startServerSpan: Span | undefined
88+
quiet?: boolean
9089
}) {
9190
const type = process.env.__NEXT_PRIVATE_RENDER_WORKER
9291
if (type) {
@@ -102,7 +101,6 @@ async function initializeImpl(opts: {
102101
customServer: false,
103102
httpServer: opts.server,
104103
port: opts.port,
105-
isNodeDebugging: opts.isNodeDebugging,
106104
})
107105
requestHandler = app.getRequestHandler()
108106
upgradeHandler = app.getUpgradeHandler()

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,7 @@ const isNextFont = (pathname: string | null) =>
4747

4848
export type RenderServer = Pick<
4949
typeof import('./render-server'),
50-
| 'initialize'
51-
| 'deleteCache'
52-
| 'clearModuleContext'
53-
| 'deleteAppClientCache'
54-
| 'propagateServerField'
50+
'initialize' | 'clearModuleContext' | 'propagateServerField'
5551
>
5652

5753
export interface LazyRenderServerInstance {

0 commit comments

Comments
 (0)