Skip to content

Commit

Permalink
Use the new moduleMap option in the flight client during SSR (#37406)
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
shuding committed Jun 2, 2022
1 parent b0783e9 commit a3e067e
Show file tree
Hide file tree
Showing 9 changed files with 459 additions and 399 deletions.
18 changes: 12 additions & 6 deletions packages/next/build/webpack/plugins/flight-manifest-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,15 @@ export class FlightManifestPlugin {
}

const moduleExports: any = manifest[resource] || {}
const moduleIdMapping: any = manifest.__ssr_module_id__ || {}
const moduleIdMapping: any = manifest.__ssr_module_mapping__ || {}
moduleIdMapping[id] = moduleIdMapping[id] || {}

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

const exportsInfo = compilation.moduleGraph.getExportsInfo(mod)
const cjsExports = [
Expand Down Expand Up @@ -143,10 +143,16 @@ export class FlightManifestPlugin {
: [],
}
}
if (!moduleIdMapping[id][name]) {
moduleIdMapping[id][name] = {
...moduleExports[name],
id: ssrNamedModuleId,
}
}
})

manifest[resource] = moduleExports
manifest.__ssr_module_id__ = moduleIdMapping
manifest.__ssr_module_mapping__ = moduleIdMapping
}

chunkGroup.chunks.forEach((chunk: any) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ var startInlineScript = stringToPrecomputedChunk('<script>');
var endInlineScript = stringToPrecomputedChunk('</script>');
var startScriptSrc = stringToPrecomputedChunk('<script src="');
var startModuleSrc = stringToPrecomputedChunk('<script type="module" src="');
var endAsyncScript = stringToPrecomputedChunk('" async=""></script>'); // Allows us to keep track of what we've already written so we can refer back to it.
var endAsyncScript = stringToPrecomputedChunk('" async=""></script>');

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

Expand Down Expand Up @@ -537,6 +537,10 @@ var startPendingSuspenseBoundary1 = stringToPrecomputedChunk('<!--$?--><template
var startPendingSuspenseBoundary2 = stringToPrecomputedChunk('"></template>');
var startClientRenderedSuspenseBoundary = stringToPrecomputedChunk('<!--$!-->');
var endSuspenseBoundary = stringToPrecomputedChunk('<!--/$-->');
var clientRenderedSuspenseBoundaryError1 = stringToPrecomputedChunk('<template data-hash="');
var clientRenderedSuspenseBoundaryError1A = stringToPrecomputedChunk('" data-msg="');
var clientRenderedSuspenseBoundaryError1B = stringToPrecomputedChunk('" data-stack="');
var clientRenderedSuspenseBoundaryError2 = stringToPrecomputedChunk('"></template>');
var startSegmentHTML = stringToPrecomputedChunk('<div hidden id="');
var startSegmentHTML2 = stringToPrecomputedChunk('">');
var endSegmentHTML = stringToPrecomputedChunk('</div>');
Expand Down Expand Up @@ -566,7 +570,7 @@ var endSegmentColGroup = stringToPrecomputedChunk('</colgroup></table>');
// const SUSPENSE_PENDING_START_DATA = '$?';
// const SUSPENSE_FALLBACK_START_DATA = '$!';
//
// function clientRenderBoundary(suspenseBoundaryID) {
// function clientRenderBoundary(suspenseBoundaryID, errorHash, errorMsg, errorComponentStack) {
// // Find the fallback's first element.
// const suspenseIdNode = document.getElementById(suspenseBoundaryID);
// if (!suspenseIdNode) {
Expand All @@ -578,6 +582,11 @@ var endSegmentColGroup = stringToPrecomputedChunk('</colgroup></table>');
// const suspenseNode = suspenseIdNode.previousSibling;
// // Tag it to be client rendered.
// suspenseNode.data = SUSPENSE_FALLBACK_START_DATA;
// // assign error metadata to first sibling
// let dataset = suspenseIdNode.dataset;
// if (errorHash) dataset.hash = errorHash;
// if (errorMsg) dataset.msg = errorMsg;
// if (errorComponentStack) dataset.stack = errorComponentStack;
// // Tell React to retry it if the parent already hydrated.
// if (suspenseNode._reactRetry) {
// suspenseNode._reactRetry();
Expand Down Expand Up @@ -661,7 +670,7 @@ var endSegmentColGroup = stringToPrecomputedChunk('</colgroup></table>');

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)}';
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()}}';
var clientRenderFunction = 'function $RX(a){if(a=document.getElementById(a))a=a.previousSibling,a.data="$!",a._reactRetry&&a._reactRetry()}';
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())}';
var completeSegmentScript1Full = stringToPrecomputedChunk(completeSegmentFunction + ';$RS("');
var completeSegmentScript1Partial = stringToPrecomputedChunk('$RS("');
var completeSegmentScript2 = stringToPrecomputedChunk('","');
Expand All @@ -672,7 +681,9 @@ var completeBoundaryScript2 = stringToPrecomputedChunk('","');
var completeBoundaryScript3 = stringToPrecomputedChunk('")</script>');
var clientRenderScript1Full = stringToPrecomputedChunk(clientRenderFunction + ';$RX("');
var clientRenderScript1Partial = stringToPrecomputedChunk('$RX("');
var clientRenderScript2 = stringToPrecomputedChunk('")</script>');
var clientRenderScript1A = stringToPrecomputedChunk('"');
var clientRenderScript2 = stringToPrecomputedChunk(')</script>');
var clientRenderErrorScriptArgInterstitial = stringToPrecomputedChunk(',');

var rendererSigil;

Expand Down Expand Up @@ -864,6 +875,14 @@ function readContext(context) {
return value;
}

var currentRequest = null;
function prepareToUseHooksForRequest(request) {
currentRequest = request;
}
function resetHooksForRequest() {
currentRequest = null;
}

function readContext$1(context) {
{
if (context.$$typeof !== REACT_SERVER_CONTEXT_TYPE) {
Expand Down Expand Up @@ -912,7 +931,7 @@ var Dispatcher = {
useLayoutEffect: unsupportedHook,
useImperativeHandle: unsupportedHook,
useEffect: unsupportedHook,
useId: unsupportedHook,
useId: useId,
useMutableSource: unsupportedHook,
useSyncExternalStore: unsupportedHook,
useCacheRefresh: function () {
Expand All @@ -939,6 +958,16 @@ function getCurrentCache() {
return currentCache;
}

function useId() {
if (currentRequest === null) {
throw new Error('useId can only be used while React is rendering');
}

var id = currentRequest.identifierCount++; // use 'S' for Flight components to distinguish from 'R' and 'r' in Fizz/Client

return ':' + currentRequest.identifierPrefix + 'S' + id.toString(32) + ':';
}

var ContextRegistry = ReactSharedInternals.ContextRegistry;
function getOrCreateServerContext(globalName) {
if (!ContextRegistry[globalName]) {
Expand All @@ -957,7 +986,7 @@ function defaultErrorHandler(error) {
var OPEN = 0;
var CLOSING = 1;
var CLOSED = 2;
function createRequest(model, bundlerConfig, onError, context) {
function createRequest(model, bundlerConfig, onError, context, identifierPrefix) {
var pingedSegments = [];
var request = {
status: OPEN,
Expand All @@ -974,6 +1003,8 @@ function createRequest(model, bundlerConfig, onError, context) {
writtenSymbols: new Map(),
writtenModules: new Map(),
writtenProviders: new Map(),
identifierPrefix: identifierPrefix || '',
identifierCount: 1,
onError: onError === undefined ? defaultErrorHandler : onError,
toJSON: function (key, value) {
return resolveModelToJSON(request, this, key, value);
Expand Down Expand Up @@ -1581,6 +1612,7 @@ function performWork(request) {
var prevCache = getCurrentCache();
ReactCurrentDispatcher.current = Dispatcher;
setCurrentCache(request.cache);
prepareToUseHooksForRequest(request);

try {
var pingedSegments = request.pingedSegments;
Expand All @@ -1600,6 +1632,7 @@ function performWork(request) {
} finally {
ReactCurrentDispatcher.current = prevDispatcher;
setCurrentCache(prevCache);
resetHooksForRequest();
}
}

Expand Down Expand Up @@ -1725,8 +1758,8 @@ function importServerContexts(contexts) {
return rootContextSnapshot;
}

function renderToReadableStream(model, webpackMap, options, context) {
var request = createRequest(model, webpackMap, options ? options.onError : undefined, context);
function renderToReadableStream(model, webpackMap, options) {
var request = createRequest(model, webpackMap, options ? options.onError : undefined, options ? options.context : undefined, options ? options.identifierPrefix : undefined);
var stream = new ReadableStream({
type: 'bytes',
start: function (controller) {
Expand All @@ -1736,6 +1769,9 @@ function renderToReadableStream(model, webpackMap, options, context) {
startFlowing(request, controller);
},
cancel: function (reason) {}
}, // $FlowFixMe size() methods are not allowed on byte streams.
{
highWaterMark: 0
});
return stream;
}
Expand Down
Loading

0 comments on commit a3e067e

Please sign in to comment.