-
Notifications
You must be signed in to change notification settings - Fork 46.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Flight] Instrument the Promise for Async Module instead of using a Module Cache #26985
Conversation
This is a bit tricky to test because it really requires both the server and the client to be bundled so that the server knows it would've been an async import so it can tag it as such.
We typically do this for Promises in general. Usually the ones we provide. This ensures that if Webpack replaces the module (HMR) then we'll get the new Promise when we require it again.
@@ -111,7 +111,33 @@ export function resolveServerReference<T>( | |||
// in Webpack but unfortunately it's not exposed so we have to | |||
// replicate it in user space. null means that it has already loaded. | |||
const chunkCache: Map<string, null | Promise<any>> = new Map(); | |||
const asyncModuleCache: Map<string, Thenable<any>> = new Map(); | |||
|
|||
function requireAsyncModule(id: string): null | Thenable<any> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't love the name. My intuition is this has some parity with the requireModule
function below but it's really about preloading only. maybe loadAsyncModule
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But then it sounds it should be using the load
API. It doesn't load the module itself, it requires it which might load something that isn't even a module or nothing at all.
The confusing part here is that there's two naming conventions requireAsyncModule
is kind of a webpack/cjs thing, where as requireModule
is more of a Flight concept so it names it. We should probably rename requireModule
to something else because "require" is kind of CJS specific name for that concept.
This PR updates the vendored react dependencies using `pnpm sync-react` ### React upstream changes - facebook/react#27028 - facebook/react#27027 - facebook/react#27019 - facebook/react#26954 - facebook/react#26987 - facebook/react#26985 - facebook/react#26933 - facebook/react#26625 - facebook/react#27011 - facebook/react#27008 - facebook/react#26997 - facebook/react#26989 - facebook/react#26955 - facebook/react#26963 - facebook/react#26983 - facebook/react#26914 - facebook/react#26951 - facebook/react#26977 - facebook/react#26958 - facebook/react#26940 - facebook/react#26939 - facebook/react#26887 - facebook/react#26947 - facebook/react#26945 - facebook/react#26942 - facebook/react#26938 - facebook/react#26844 - facebook/react#25510 - facebook/react#26932 - facebook/react#26896 - facebook/react#26913 - facebook/react#26888 - facebook/react#26827 - facebook/react#26889 - facebook/react#26877 - facebook/react#26873 - facebook/react#26880 - facebook/react#26842 - facebook/react#26858 - facebook/react#26754 - facebook/react#26753 - facebook/react#26881 ### Related Closes #49409 (by facebook/react#26977) fix NEXT-1189 Co-authored-by: Shu Ding <g@shud.in>
Fixes #49409 ### React upstream changes - facebook/react#27045 - facebook/react#27051 - facebook/react#27032 - facebook/react#27031 - facebook/react#27029 - facebook/react#27028 - facebook/react#27027 - facebook/react#27019 - facebook/react#26954 - facebook/react#26987 - facebook/react#26985 - facebook/react#26933 - facebook/react#26625 - facebook/react#27011 - facebook/react#27008 - facebook/react#26997 - facebook/react#26989 - facebook/react#26955 - facebook/react#26963 - facebook/react#26983 - facebook/react#26914 - facebook/react#26951 - facebook/react#26977 - facebook/react#26958 - facebook/react#26940 - facebook/react#26939 - facebook/react#26887 - facebook/react#26947 - facebook/react#26945 - facebook/react#26942 - facebook/react#26938 - facebook/react#26844 - facebook/react#25510 - facebook/react#26932 - facebook/react#26896 - facebook/react#26913 - facebook/react#26888 - facebook/react#26827 - facebook/react#26889 - facebook/react#26877 - facebook/react#26873 - facebook/react#26880 - facebook/react#26842 - facebook/react#26858 - facebook/react#26754 - facebook/react#26753 - facebook/react#26881 --------- Co-authored-by: Jiachi Liu <inbox@huozhi.im>
This PR updates the vendored react dependencies using `pnpm sync-react` ### React upstream changes - facebook/react#27028 - facebook/react#27027 - facebook/react#27019 - facebook/react#26954 - facebook/react#26987 - facebook/react#26985 - facebook/react#26933 - facebook/react#26625 - facebook/react#27011 - facebook/react#27008 - facebook/react#26997 - facebook/react#26989 - facebook/react#26955 - facebook/react#26963 - facebook/react#26983 - facebook/react#26914 - facebook/react#26951 - facebook/react#26977 - facebook/react#26958 - facebook/react#26940 - facebook/react#26939 - facebook/react#26887 - facebook/react#26947 - facebook/react#26945 - facebook/react#26942 - facebook/react#26938 - facebook/react#26844 - facebook/react#25510 - facebook/react#26932 - facebook/react#26896 - facebook/react#26913 - facebook/react#26888 - facebook/react#26827 - facebook/react#26889 - facebook/react#26877 - facebook/react#26873 - facebook/react#26880 - facebook/react#26842 - facebook/react#26858 - facebook/react#26754 - facebook/react#26753 - facebook/react#26881 ### Related Closes #49409 (by facebook/react#26977) fix NEXT-1189 Co-authored-by: Shu Ding <g@shud.in>
…odule Cache (facebook#26985) Currently, since we use a module cache for async modules, it doesn't automatically get updated when the module registry gets updated (HMR). This technique ensures that if Webpack replaces the module (HMR) then we'll get the new Promise when we require it again. This technique doesn't work for ESM and probably not Vite since ESM will provide a new Promise each time you call `import()` but in the Webpack/CJS approach this Promise is an entry in the module cache and not a promise for the entry. I tried to replicate the original issue in the fixture but it's tricky to replicate because 1) we can't really use async modules the same way without compiling both server and client 2) even then I'm not quite sure how to repro the HMR issue.
…odule Cache (#26985) Currently, since we use a module cache for async modules, it doesn't automatically get updated when the module registry gets updated (HMR). This technique ensures that if Webpack replaces the module (HMR) then we'll get the new Promise when we require it again. This technique doesn't work for ESM and probably not Vite since ESM will provide a new Promise each time you call `import()` but in the Webpack/CJS approach this Promise is an entry in the module cache and not a promise for the entry. I tried to replicate the original issue in the fixture but it's tricky to replicate because 1) we can't really use async modules the same way without compiling both server and client 2) even then I'm not quite sure how to repro the HMR issue. DiffTrain build for commit 5945e06.
…8535) The first iteration of `deleteAppClientCache()` was introduced in #41510. Its purpose was to remove stale client components (for SSR) from React's cache. Since React didn't (and still doesn't) offer an API for that, this was accomplished by deleting `react-server-dom-webpack` from the require cache. With the bundling that was introduced in #55362, `deleteAppClientCache()` was changed to delete the whole server runtimes (e.g. `app-page.runtime.dev.js`) from the require cache. This has the unfortunate side effect that also React's other module scope cache (back then `ReactCurrentCache`, now `ReactSharedInternals.A`) is reset between compilations. As a result, when fetching data from a route handler in a page, the fetch cache didn't work properly. This issue was masked though by response buffering for the static generation cache. In #68447 the response buffering will be removed which [uncovered the issue](https://github.com/vercel/next.js/actions/runs/10217197012/job/28270497496). React had two module-scoped caches for client components: - `chunkCache` – caches the loaded JS chunks, as specified in the SSR manifest - `asyncModuleCache` – caches the required modules, if marked as `async` in the SSR manifest The `asyncModuleCache` was subsequently dropped in facebook/react#26985. In addition, with Webpack, we don't create any client component chunks for SSR (removed from the manifest in #50959). So there's no need anymore to delete server runtime bundles from the require cache, and we can remove `deleteAppClientCache()` from the `NextJsRequireCacheHotReloader`. For Turbopack, the exported `deleteAppClientCache` function is still used because Turbopack does create SSR client component chunks. Ideally, React would offer an API to clear the chunk cache. But for now we can keep this logic, since Turbopack also handles this a bit more sophisticated than the Webpack plugin, so that the aforementioned fetch issue does not occur there. This PR in conjunction with #68193 should then also fix the issue that was reported in #52126.
Currently, since we use a module cache for async modules, it doesn't automatically get updated when the module registry gets updated (HMR).
This technique ensures that if Webpack replaces the module (HMR) then we'll get the new Promise when we require it again.
This technique doesn't work for ESM and probably not Vite since ESM will provide a new Promise each time you call
import()
but in the Webpack/CJS approach this Promise is an entry in the module cache and not a promise for the entry.I tried to replicate the original issue in the fixture but it's tricky to replicate because 1) we can't really use async modules the same way without compiling both server and client 2) even then I'm not quite sure how to repro the HMR issue.