Skip to content

Commit a3e067e

Browse files
authored
Use the new moduleMap option in the flight client during SSR (#37406)
Adopt the new `moduleMap` option added in facebook/react#24629, which helps us getting rid of our hacky implementation injected to `globalThis.__next_require__`. The map will be attached to the flight manifest as `__ssr_module_mapping__`. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm lint` - [ ] The examples guidelines are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing.md#adding-examples)
1 parent b0783e9 commit a3e067e

File tree

9 files changed

+459
-399
lines changed

9 files changed

+459
-399
lines changed

packages/next/build/webpack/plugins/flight-manifest-plugin.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,15 @@ export class FlightManifestPlugin {
8282
}
8383

8484
const moduleExports: any = manifest[resource] || {}
85-
const moduleIdMapping: any = manifest.__ssr_module_id__ || {}
85+
const moduleIdMapping: any = manifest.__ssr_module_mapping__ || {}
86+
moduleIdMapping[id] = moduleIdMapping[id] || {}
8687

8788
// Note that this isn't that reliable as webpack is still possible to assign
8889
// additional queries to make sure there's no conflict even using the `named`
8990
// module ID strategy.
90-
const ssrNamedModuleId = relative(context, mod.resourceResolveData.path)
91-
moduleIdMapping[id] = ssrNamedModuleId.startsWith('.')
92-
? ssrNamedModuleId
93-
: `./${ssrNamedModuleId}`
91+
let ssrNamedModuleId = relative(context, mod.resourceResolveData.path)
92+
if (!ssrNamedModuleId.startsWith('.'))
93+
ssrNamedModuleId = `./${ssrNamedModuleId}`
9494

9595
const exportsInfo = compilation.moduleGraph.getExportsInfo(mod)
9696
const cjsExports = [
@@ -143,10 +143,16 @@ export class FlightManifestPlugin {
143143
: [],
144144
}
145145
}
146+
if (!moduleIdMapping[id][name]) {
147+
moduleIdMapping[id][name] = {
148+
...moduleExports[name],
149+
id: ssrNamedModuleId,
150+
}
151+
}
146152
})
147153

148154
manifest[resource] = moduleExports
149-
manifest.__ssr_module_id__ = moduleIdMapping
155+
manifest.__ssr_module_mapping__ = moduleIdMapping
150156
}
151157

152158
chunkGroup.chunks.forEach((chunk: any) => {

packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.development.server.js

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ var startInlineScript = stringToPrecomputedChunk('<script>');
502502
var endInlineScript = stringToPrecomputedChunk('</script>');
503503
var startScriptSrc = stringToPrecomputedChunk('<script src="');
504504
var startModuleSrc = stringToPrecomputedChunk('<script type="module" src="');
505-
var endAsyncScript = stringToPrecomputedChunk('" async=""></script>'); // Allows us to keep track of what we've already written so we can refer back to it.
505+
var endAsyncScript = stringToPrecomputedChunk('" async=""></script>');
506506

507507
var textSeparator = stringToPrecomputedChunk('<!-- -->');
508508

@@ -537,6 +537,10 @@ var startPendingSuspenseBoundary1 = stringToPrecomputedChunk('<!--$?--><template
537537
var startPendingSuspenseBoundary2 = stringToPrecomputedChunk('"></template>');
538538
var startClientRenderedSuspenseBoundary = stringToPrecomputedChunk('<!--$!-->');
539539
var endSuspenseBoundary = stringToPrecomputedChunk('<!--/$-->');
540+
var clientRenderedSuspenseBoundaryError1 = stringToPrecomputedChunk('<template data-hash="');
541+
var clientRenderedSuspenseBoundaryError1A = stringToPrecomputedChunk('" data-msg="');
542+
var clientRenderedSuspenseBoundaryError1B = stringToPrecomputedChunk('" data-stack="');
543+
var clientRenderedSuspenseBoundaryError2 = stringToPrecomputedChunk('"></template>');
540544
var startSegmentHTML = stringToPrecomputedChunk('<div hidden id="');
541545
var startSegmentHTML2 = stringToPrecomputedChunk('">');
542546
var endSegmentHTML = stringToPrecomputedChunk('</div>');
@@ -566,7 +570,7 @@ var endSegmentColGroup = stringToPrecomputedChunk('</colgroup></table>');
566570
// const SUSPENSE_PENDING_START_DATA = '$?';
567571
// const SUSPENSE_FALLBACK_START_DATA = '$!';
568572
//
569-
// function clientRenderBoundary(suspenseBoundaryID) {
573+
// function clientRenderBoundary(suspenseBoundaryID, errorHash, errorMsg, errorComponentStack) {
570574
// // Find the fallback's first element.
571575
// const suspenseIdNode = document.getElementById(suspenseBoundaryID);
572576
// if (!suspenseIdNode) {
@@ -578,6 +582,11 @@ var endSegmentColGroup = stringToPrecomputedChunk('</colgroup></table>');
578582
// const suspenseNode = suspenseIdNode.previousSibling;
579583
// // Tag it to be client rendered.
580584
// suspenseNode.data = SUSPENSE_FALLBACK_START_DATA;
585+
// // assign error metadata to first sibling
586+
// let dataset = suspenseIdNode.dataset;
587+
// if (errorHash) dataset.hash = errorHash;
588+
// if (errorMsg) dataset.msg = errorMsg;
589+
// if (errorComponentStack) dataset.stack = errorComponentStack;
581590
// // Tell React to retry it if the parent already hydrated.
582591
// if (suspenseNode._reactRetry) {
583592
// suspenseNode._reactRetry();
@@ -661,7 +670,7 @@ var endSegmentColGroup = stringToPrecomputedChunk('</colgroup></table>');
661670

662671
var completeSegmentFunction = 'function $RS(a,b){a=document.getElementById(a);b=document.getElementById(b);for(a.parentNode.removeChild(a);a.firstChild;)b.parentNode.insertBefore(a.firstChild,b);b.parentNode.removeChild(b)}';
663672
var completeBoundaryFunction = 'function $RC(a,b){a=document.getElementById(a);b=document.getElementById(b);b.parentNode.removeChild(b);if(a){a=a.previousSibling;var f=a.parentNode,c=a.nextSibling,e=0;do{if(c&&8===c.nodeType){var d=c.data;if("/$"===d)if(0===e)break;else e--;else"$"!==d&&"$?"!==d&&"$!"!==d||e++}d=c.nextSibling;f.removeChild(c);c=d}while(c);for(;b.firstChild;)f.insertBefore(b.firstChild,c);a.data="$";a._reactRetry&&a._reactRetry()}}';
664-
var clientRenderFunction = 'function $RX(a){if(a=document.getElementById(a))a=a.previousSibling,a.data="$!",a._reactRetry&&a._reactRetry()}';
673+
var clientRenderFunction = 'function $RX(b,c,d,e){var a=document.getElementById(b);a&&(b=a.previousSibling,b.data="$!",a=a.dataset,c&&(a.hash=c),d&&(a.msg=d),e&&(a.stack=e),b._reactRetry&&b._reactRetry())}';
665674
var completeSegmentScript1Full = stringToPrecomputedChunk(completeSegmentFunction + ';$RS("');
666675
var completeSegmentScript1Partial = stringToPrecomputedChunk('$RS("');
667676
var completeSegmentScript2 = stringToPrecomputedChunk('","');
@@ -672,7 +681,9 @@ var completeBoundaryScript2 = stringToPrecomputedChunk('","');
672681
var completeBoundaryScript3 = stringToPrecomputedChunk('")</script>');
673682
var clientRenderScript1Full = stringToPrecomputedChunk(clientRenderFunction + ';$RX("');
674683
var clientRenderScript1Partial = stringToPrecomputedChunk('$RX("');
675-
var clientRenderScript2 = stringToPrecomputedChunk('")</script>');
684+
var clientRenderScript1A = stringToPrecomputedChunk('"');
685+
var clientRenderScript2 = stringToPrecomputedChunk(')</script>');
686+
var clientRenderErrorScriptArgInterstitial = stringToPrecomputedChunk(',');
676687

677688
var rendererSigil;
678689

@@ -864,6 +875,14 @@ function readContext(context) {
864875
return value;
865876
}
866877

878+
var currentRequest = null;
879+
function prepareToUseHooksForRequest(request) {
880+
currentRequest = request;
881+
}
882+
function resetHooksForRequest() {
883+
currentRequest = null;
884+
}
885+
867886
function readContext$1(context) {
868887
{
869888
if (context.$$typeof !== REACT_SERVER_CONTEXT_TYPE) {
@@ -912,7 +931,7 @@ var Dispatcher = {
912931
useLayoutEffect: unsupportedHook,
913932
useImperativeHandle: unsupportedHook,
914933
useEffect: unsupportedHook,
915-
useId: unsupportedHook,
934+
useId: useId,
916935
useMutableSource: unsupportedHook,
917936
useSyncExternalStore: unsupportedHook,
918937
useCacheRefresh: function () {
@@ -939,6 +958,16 @@ function getCurrentCache() {
939958
return currentCache;
940959
}
941960

961+
function useId() {
962+
if (currentRequest === null) {
963+
throw new Error('useId can only be used while React is rendering');
964+
}
965+
966+
var id = currentRequest.identifierCount++; // use 'S' for Flight components to distinguish from 'R' and 'r' in Fizz/Client
967+
968+
return ':' + currentRequest.identifierPrefix + 'S' + id.toString(32) + ':';
969+
}
970+
942971
var ContextRegistry = ReactSharedInternals.ContextRegistry;
943972
function getOrCreateServerContext(globalName) {
944973
if (!ContextRegistry[globalName]) {
@@ -957,7 +986,7 @@ function defaultErrorHandler(error) {
957986
var OPEN = 0;
958987
var CLOSING = 1;
959988
var CLOSED = 2;
960-
function createRequest(model, bundlerConfig, onError, context) {
989+
function createRequest(model, bundlerConfig, onError, context, identifierPrefix) {
961990
var pingedSegments = [];
962991
var request = {
963992
status: OPEN,
@@ -974,6 +1003,8 @@ function createRequest(model, bundlerConfig, onError, context) {
9741003
writtenSymbols: new Map(),
9751004
writtenModules: new Map(),
9761005
writtenProviders: new Map(),
1006+
identifierPrefix: identifierPrefix || '',
1007+
identifierCount: 1,
9771008
onError: onError === undefined ? defaultErrorHandler : onError,
9781009
toJSON: function (key, value) {
9791010
return resolveModelToJSON(request, this, key, value);
@@ -1581,6 +1612,7 @@ function performWork(request) {
15811612
var prevCache = getCurrentCache();
15821613
ReactCurrentDispatcher.current = Dispatcher;
15831614
setCurrentCache(request.cache);
1615+
prepareToUseHooksForRequest(request);
15841616

15851617
try {
15861618
var pingedSegments = request.pingedSegments;
@@ -1600,6 +1632,7 @@ function performWork(request) {
16001632
} finally {
16011633
ReactCurrentDispatcher.current = prevDispatcher;
16021634
setCurrentCache(prevCache);
1635+
resetHooksForRequest();
16031636
}
16041637
}
16051638

@@ -1725,8 +1758,8 @@ function importServerContexts(contexts) {
17251758
return rootContextSnapshot;
17261759
}
17271760

1728-
function renderToReadableStream(model, webpackMap, options, context) {
1729-
var request = createRequest(model, webpackMap, options ? options.onError : undefined, context);
1761+
function renderToReadableStream(model, webpackMap, options) {
1762+
var request = createRequest(model, webpackMap, options ? options.onError : undefined, options ? options.context : undefined, options ? options.identifierPrefix : undefined);
17301763
var stream = new ReadableStream({
17311764
type: 'bytes',
17321765
start: function (controller) {
@@ -1736,6 +1769,9 @@ function renderToReadableStream(model, webpackMap, options, context) {
17361769
startFlowing(request, controller);
17371770
},
17381771
cancel: function (reason) {}
1772+
}, // $FlowFixMe size() methods are not allowed on byte streams.
1773+
{
1774+
highWaterMark: 0
17391775
});
17401776
return stream;
17411777
}

0 commit comments

Comments
 (0)