Skip to content

Commit

Permalink
Clean up require.cache handling (#68743)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
unstubbable authored and ForsakenHarmony committed Aug 16, 2024
1 parent c7cae73 commit e3ca789
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 82 deletions.
Original file line number Diff line number Diff line change
@@ -1,69 +1,12 @@
import type { webpack } from 'next/dist/compiled/webpack/webpack'
import { deleteCache } from '../../../server/dev/require-cache'
import { clearModuleContext } from '../../../server/web/sandbox'
import { realpathSync } from '../../../lib/realpath'
import path from 'path'
import isError from '../../../lib/is-error'
import { clearManifestCache } from '../../../server/load-manifest'

type Compiler = webpack.Compiler
type WebpackPluginInstance = webpack.WebpackPluginInstance

const originModules = [
require.resolve('../../../server/require'),
require.resolve('../../../server/load-components'),
require.resolve('../../../server/next-server'),
require.resolve('next/dist/compiled/next-server/app-page.runtime.dev.js'),
require.resolve('next/dist/compiled/next-server/app-route.runtime.dev.js'),
require.resolve('next/dist/compiled/next-server/pages.runtime.dev.js'),
require.resolve('next/dist/compiled/next-server/pages-api.runtime.dev.js'),
]

const RUNTIME_NAMES = ['webpack-runtime', 'webpack-api-runtime']

function deleteFromRequireCache(filePath: string) {
try {
filePath = realpathSync(filePath)
} catch (e) {
if (isError(e) && e.code !== 'ENOENT') throw e
}
const mod = require.cache[filePath]
if (mod) {
// remove the child reference from the originModules
for (const originModule of originModules) {
const parent = require.cache[originModule]
if (parent) {
const idx = parent.children.indexOf(mod)
if (idx >= 0) parent.children.splice(idx, 1)
}
}
// remove parent references from external modules
for (const child of mod.children) {
child.parent = null
}
delete require.cache[filePath]
return true
}
return false
}

export function deleteAppClientCache() {
deleteFromRequireCache(
require.resolve('next/dist/compiled/next-server/app-page.runtime.dev.js')
)
deleteFromRequireCache(
require.resolve(
'next/dist/compiled/next-server/app-page-experimental.runtime.dev.js'
)
)
}

export function deleteCache(filePath: string) {
// try to clear it from the fs cache
clearManifestCache(filePath)

deleteFromRequireCache(filePath)
}

const PLUGIN_NAME = 'NextJsRequireCacheHotReloader'

// This plugin flushes require.cache after emitting the files. Providing 'hot reloading' of server files.
Expand Down
5 changes: 1 addition & 4 deletions packages/next/src/server/dev/hot-reloader-turbopack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,7 @@ import { BLOCKED_PAGES } from '../../shared/lib/constants'
import { getOverlayMiddleware } from '../../client/components/react-dev-overlay/server/middleware-turbopack'
import { PageNotFoundError } from '../../shared/lib/utils'
import { debounce } from '../utils'
import {
deleteAppClientCache,
deleteCache,
} from '../../build/webpack/plugins/nextjs-require-cache-hot-reloader'
import { deleteAppClientCache, deleteCache } from './require-cache'
import {
clearAllModuleContexts,
clearModuleContext,
Expand Down
46 changes: 46 additions & 0 deletions packages/next/src/server/dev/require-cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import isError from '../../lib/is-error'
import { realpathSync } from '../../lib/realpath'
import { clearManifestCache } from '../load-manifest'

function deleteFromRequireCache(filePath: string) {
try {
filePath = realpathSync(filePath)
} catch (e) {
if (isError(e) && e.code !== 'ENOENT') throw e
}
const mod = require.cache[filePath]
if (mod) {
// remove the child reference from all parent modules
for (const parent of Object.values(require.cache)) {
if (parent?.children) {
const idx = parent.children.indexOf(mod)
if (idx >= 0) parent.children.splice(idx, 1)
}
}
// remove parent references from external modules
for (const child of mod.children) {
child.parent = null
}
delete require.cache[filePath]
return true
}
return false
}

export function deleteAppClientCache() {
deleteFromRequireCache(
require.resolve('next/dist/compiled/next-server/app-page.runtime.dev.js')
)
deleteFromRequireCache(
require.resolve(
'next/dist/compiled/next-server/app-page-experimental.runtime.dev.js'
)
)
}

export function deleteCache(filePath: string) {
// try to clear it from the fs cache
clearManifestCache(filePath)

deleteFromRequireCache(filePath)
}
2 changes: 1 addition & 1 deletion packages/next/src/server/dev/turbopack/manifest-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
import { join, posix } from 'path'
import { readFile, writeFile } from 'fs/promises'
import type { SetupOpts } from '../../lib/router-utils/setup-dev-bundler'
import { deleteCache } from '../../../build/webpack/plugins/nextjs-require-cache-hot-reloader'
import { deleteCache } from '../require-cache'
import { writeFileAtomic } from '../../../lib/fs/write-atomic'
import { isInterceptionRouteRewrite } from '../../../lib/generate-interception-routes-rewrites'
import {
Expand Down
14 changes: 0 additions & 14 deletions packages/next/src/server/lib/render-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,9 @@ let initializations: Record<
> = {}

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

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

export function clearAllModuleContexts() {
Expand All @@ -37,16 +33,6 @@ export function clearModuleContext(target: string) {
return sandboxContext?.clearModuleContext(target)
}

export function deleteAppClientCache() {
return requireCacheHotReloader?.deleteAppClientCache()
}

export function deleteCache(filePaths: string[]) {
for (const filePath of filePaths) {
requireCacheHotReloader?.deleteCache(filePath)
}
}

export async function propagateServerField(
dir: string,
field: PropagateToWorkersField,
Expand Down
6 changes: 1 addition & 5 deletions packages/next/src/server/lib/router-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,7 @@ const isNextFont = (pathname: string | null) =>

export type RenderServer = Pick<
typeof import('./render-server'),
| 'initialize'
| 'deleteCache'
| 'clearModuleContext'
| 'deleteAppClientCache'
| 'propagateServerField'
'initialize' | 'clearModuleContext' | 'propagateServerField'
>

export interface LazyRenderServerInstance {
Expand Down

0 comments on commit e3ca789

Please sign in to comment.