From d64bc4c619fc5361d465a83cef6fd7110a279cb5 Mon Sep 17 00:00:00 2001 From: Leah Date: Wed, 13 Sep 2023 19:20:48 +0200 Subject: [PATCH] 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 --- .github/CODEOWNERS | 15 +++-- .../server/app-render/use-flight-response.tsx | 7 +- .../src/server/lib/router-utils/setup-dev.ts | 67 +++++++++++++------ 3 files changed, 57 insertions(+), 32 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 8bd62490a4608..679d4454792c1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -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 diff --git a/packages/next/src/server/app-render/use-flight-response.tsx b/packages/next/src/server/app-render/use-flight-response.tsx index a7d0318eeb70c..0f09406806fca 100644 --- a/packages/next/src/server/app-render/use-flight-response.tsx +++ b/packages/next/src/server/app-render/use-flight-response.tsx @@ -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, { 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 62648d4bfe801..41edb225462f8 100644 --- a/packages/next/src/server/lib/router-utils/setup-dev.ts +++ b/packages/next/src/server/lib/router-utils/setup-dev.ts @@ -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 ): Promise> { - 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() 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( 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 { 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 { + 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 { 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 { 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()