fix react-server-dom-webpack cache invalidation (#55287)

### What?
Webpack wrapped the external import in a new module, making `require.cache` invalidation impossible.

This also adds a basic fallback manifest for turbopack.

Closes WEB-1522
This commit is contained in:
Leah 2023-09-13 19:20:48 +02:00 committed by GitHub
parent fe797c1074
commit d64bc4c619
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 57 additions and 32 deletions

15
.github/CODEOWNERS vendored
View file

@ -28,10 +28,11 @@
# Tooling & Telemetry
/packages/next/src/build/ @timneutkens @ijjk @shuding @huozhi @vercel/web-tooling
/packages/next/src/telemetry/ @timneutkens @ijjk @shuding @padmaia
/packages/next-swc/ @timneutkens @ijjk @shuding @huozhi @vercel/web-tooling
Cargo.toml @timneutkens @ijjk @shuding @huozhi @vercel/web-tooling
Cargo.lock @timneutkens @ijjk @shuding @huozhi @vercel/web-tooling
/.cargo/config.toml @timneutkens @ijjk @shuding @huozhi @vercel/web-tooling
/.config/nextest.toml @timneutkens @ijjk @shuding @huozhi @vercel/web-tooling
/packages/next/src/build/ @timneutkens @ijjk @shuding @huozhi @vercel/web-tooling
/packages/next/src/server/lib/router-utils/setup-dev.ts @timneutkens @ijjk @shuding @huozhi @feedthejim @ztanner @wyattjoh @vercel/web-tooling
/packages/next/src/telemetry/ @timneutkens @ijjk @shuding @padmaia
/packages/next-swc/ @timneutkens @ijjk @shuding @huozhi @vercel/web-tooling
Cargo.toml @timneutkens @ijjk @shuding @huozhi @vercel/web-tooling
Cargo.lock @timneutkens @ijjk @shuding @huozhi @vercel/web-tooling
/.cargo/config.toml @timneutkens @ijjk @shuding @huozhi @vercel/web-tooling
/.config/nextest.toml @timneutkens @ijjk @shuding @huozhi @vercel/web-tooling

View file

@ -21,9 +21,10 @@ export function useFlightResponse(
return flightResponseRef.current
}
// react-server-dom-webpack/client.edge must not be hoisted for require cache clearing to work correctly
const {
createFromReadableStream,
} = require(`react-server-dom-webpack/client.edge`)
const { createFromReadableStream } = process.env.NEXT_MINIMAL
? // @ts-ignore
__non_webpack_require__(`react-server-dom-webpack/client.edge`)
: require(`react-server-dom-webpack/client.edge`)
const [renderStream, forwardStream] = req.tee()
const res = createFromReadableStream(renderStream, {

View file

@ -103,7 +103,10 @@ import {
TurboPackConnectedAction,
} from '../../dev/hot-reloader-types'
import { debounce } from '../../utils'
import { deleteCache } from '../../../build/webpack/plugins/nextjs-require-cache-hot-reloader'
import {
deleteAppClientCache,
deleteCache,
} from '../../../build/webpack/plugins/nextjs-require-cache-hot-reloader'
import { normalizeMetadataRoute } from '../../../lib/metadata/get-metadata-route'
const wsServer = new ws.Server({ noServer: true })
@ -311,20 +314,18 @@ async function startWatcher(opts: SetupOpts) {
async function processResult(
result: TurbopackResult<WrittenEndpoint>
): Promise<TurbopackResult<WrittenEndpoint>> {
for (const file of result.serverPaths
.map((p) => path.join(distDir, p))
.concat([
// We need to clear the chunk cache in react
require.resolve(
'next/dist/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-client.edge.development.js'
),
// And this redirecting module as well
require.resolve(
'next/dist/compiled/react-server-dom-webpack/client.edge.js'
),
])) {
const hasAppPaths = result.serverPaths.some((p) =>
p.startsWith('server/app')
)
if (hasAppPaths) {
deleteAppClientCache()
}
for (const file of result.serverPaths.map((p) => path.join(distDir, p))) {
deleteCache(file)
}
return result
}
@ -337,6 +338,7 @@ async function startWatcher(opts: SetupOpts) {
moduleTrace?: Array<{ moduleName: string }>
stack?: string
}
const errors = new Map<string, HmrError>()
for (const [, issueMap] of issues) {
for (const [key, issue] of issueMap) {
@ -380,8 +382,6 @@ async function startWatcher(opts: SetupOpts) {
sendHmrDebounce()
}
const clearCache = (filePath: string) => deleteCache(filePath)
async function loadPartialManifest<T>(
name: string,
pageName: string,
@ -482,6 +482,7 @@ async function startWatcher(opts: SetupOpts) {
if (payload) sendHmr('endpoint-change', page, payload)
}
}
function clearChangeSubscription(page: string) {
const subscription = changeSubscriptions.get(page)
if (subscription) {
@ -583,6 +584,7 @@ async function startWatcher(opts: SetupOpts) {
currentEntriesHandlingResolve = undefined
}
}
handleEntries().catch((err) => {
console.error(err)
process.exit(1)
@ -665,7 +667,7 @@ async function startWatcher(opts: SetupOpts) {
async function writeBuildManifest(): Promise<void> {
const buildManifest = mergeBuildManifests(buildManifests.values())
const buildManifestPath = path.join(distDir, BUILD_MANIFEST)
await clearCache(buildManifestPath)
deleteCache(buildManifestPath)
await writeFile(
buildManifestPath,
JSON.stringify(buildManifest, null, 2),
@ -696,12 +698,30 @@ async function startWatcher(opts: SetupOpts) {
)
}
async function writeFallbackBuildManifest(): Promise<void> {
const fallbackBuildManifest = mergeBuildManifests(
[buildManifests.get('_app'), buildManifests.get('_error')].filter(
Boolean
) as BuildManifest[]
)
const fallbackBuildManifestPath = path.join(
distDir,
`fallback-${BUILD_MANIFEST}`
)
deleteCache(fallbackBuildManifestPath)
await writeFile(
fallbackBuildManifestPath,
JSON.stringify(fallbackBuildManifest, null, 2),
'utf-8'
)
}
async function writeAppBuildManifest(): Promise<void> {
const appBuildManifest = mergeAppBuildManifests(
appBuildManifests.values()
)
const appBuildManifestPath = path.join(distDir, APP_BUILD_MANIFEST)
await clearCache(appBuildManifestPath)
deleteCache(appBuildManifestPath)
await writeFile(
appBuildManifestPath,
JSON.stringify(appBuildManifest, null, 2),
@ -712,7 +732,7 @@ async function startWatcher(opts: SetupOpts) {
async function writePagesManifest(): Promise<void> {
const pagesManifest = mergePagesManifests(pagesManifests.values())
const pagesManifestPath = path.join(distDir, 'server', PAGES_MANIFEST)
await clearCache(pagesManifestPath)
deleteCache(pagesManifestPath)
await writeFile(
pagesManifestPath,
JSON.stringify(pagesManifest, null, 2),
@ -727,7 +747,7 @@ async function startWatcher(opts: SetupOpts) {
'server',
APP_PATHS_MANIFEST
)
await clearCache(appPathsManifestPath)
deleteCache(appPathsManifestPath)
await writeFile(
appPathsManifestPath,
JSON.stringify(appPathsManifest, null, 2),
@ -743,7 +763,7 @@ async function startWatcher(opts: SetupOpts) {
distDir,
'server/middleware-manifest.json'
)
await clearCache(middlewareManifestPath)
deleteCache(middlewareManifestPath)
await writeFile(
middlewareManifestPath,
JSON.stringify(middlewareManifest, null, 2),
@ -759,7 +779,7 @@ async function startWatcher(opts: SetupOpts) {
'server',
NEXT_FONT_MANIFEST + '.json'
)
await clearCache(fontManifestPath)
deleteCache(fontManifestPath)
await writeFile(
fontManifestPath,
JSON.stringify(
@ -780,7 +800,7 @@ async function startWatcher(opts: SetupOpts) {
distDir,
'react-loadable-manifest.json'
)
await clearCache(loadableManifestPath)
deleteCache(loadableManifestPath)
await writeFile(
loadableManifestPath,
JSON.stringify({}, null, 2),
@ -847,6 +867,7 @@ async function startWatcher(opts: SetupOpts) {
await currentEntriesHandling
await writeBuildManifest()
await writeAppBuildManifest()
await writeFallbackBuildManifest()
await writePagesManifest()
await writeAppPathsManifest()
await writeMiddlewareManifest()
@ -1008,6 +1029,7 @@ async function startWatcher(opts: SetupOpts) {
await loadPagesManifest('_error')
await writeBuildManifest()
await writeFallbackBuildManifest()
await writePagesManifest()
await writeMiddlewareManifest()
await writeOtherManifests()
@ -1118,6 +1140,7 @@ async function startWatcher(opts: SetupOpts) {
}
await writeBuildManifest()
await writeFallbackBuildManifest()
await writePagesManifest()
await writeMiddlewareManifest()
await writeOtherManifests()