Skip to content

Commit

Permalink
Update RSC plugin to support useId (#1577)
Browse files Browse the repository at this point in the history
* Update RSC plugin to support useId

* Update RSC types

* Update SSR types
  • Loading branch information
frandiox committed Jun 15, 2022
1 parent f70a3cb commit fb04eb9
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 130 deletions.
4 changes: 2 additions & 2 deletions packages/hydrogen/src/entry-server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -408,10 +408,10 @@ async function runSSR({
bootstrapScripts,
bootstrapModules,
onError(error) {
ssrDidError = error;
ssrDidError = error as Error;

if (dev && !writable.closed && !!responseOptions.status) {
writable.write(getErrorMarkup(error));
writable.write(getErrorMarkup(error as Error));
}

log.error(error);
Expand Down
45 changes: 18 additions & 27 deletions packages/hydrogen/src/streaming.server.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
import {
// @ts-ignore
renderToPipeableStream as _ssrRenderToPipeableStream, // Only available in Node context
// @ts-ignore
renderToReadableStream as _ssrRenderToReadableStream, // Only available in Browser/Worker context
export {
renderToPipeableStream as ssrRenderToPipeableStream, // Only available in Node context
renderToReadableStream as ssrRenderToReadableStream, // Only available in Browser/Worker context
} from 'react-dom/server';

// @ts-ignore
import {renderToReadableStream as _rscRenderToReadableStream} from '@shopify/hydrogen/vendor/react-server-dom-vite/writer.browser.server';
// @ts-ignore
import {createFromReadableStream as _createFromReadableStream} from '@shopify/hydrogen/vendor/react-server-dom-vite';
import type {Writable} from 'stream';

// From Flight flow types
type ServerContextJSONValue =
| string
| boolean
| number
| null
| Readonly<ServerContextJSONValueCircular>
| {[key: string]: ServerContextJSONValueCircular};

interface ServerContextJSONValueCircular
extends Array<ServerContextJSONValue> {}

export const rscRenderToReadableStream = _rscRenderToReadableStream as (
App: JSX.Element,
options?: {
onError?: (error: Error) => void;
context?: Array<[string, ServerContextJSONValue]>;
identifierPrefix?: string;
}
) => ReadableStream<Uint8Array>;

Expand All @@ -23,27 +35,6 @@ export const createFromReadableStream = _createFromReadableStream as (
readRoot: () => JSX.Element;
};

type StreamOptions = {
nonce?: string;
bootstrapScripts?: string[];
bootstrapModules?: string[];
onError?: (error: Error) => void;
};

export const ssrRenderToPipeableStream = _ssrRenderToPipeableStream as (
App: JSX.Element,
options: StreamOptions & {
onAllReady?: () => void;
onShellReady?: () => void;
onShellError?: (error: Error) => void;
}
) => {pipe: Writable['pipe']};

export const ssrRenderToReadableStream = _ssrRenderToReadableStream as (
App: JSX.Element,
options: StreamOptions
) => Promise<ReadableStream<Uint8Array> & {allReady: Promise<void>}>;

export async function bufferReadableStream(
reader: ReadableStreamDefaultReader,
cb?: (chunk: string) => void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -545,10 +545,12 @@ 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 clientRenderedSuspenseBoundaryError1 = stringToPrecomputedChunk('<template');
var clientRenderedSuspenseBoundaryErrorAttrInterstitial = stringToPrecomputedChunk('"');
var clientRenderedSuspenseBoundaryError1A = stringToPrecomputedChunk(' data-dgst="');
var clientRenderedSuspenseBoundaryError1B = stringToPrecomputedChunk(' data-msg="');
var clientRenderedSuspenseBoundaryError1C = stringToPrecomputedChunk(' data-stck="');
var clientRenderedSuspenseBoundaryError2 = stringToPrecomputedChunk('></template>');
var startSegmentHTML = stringToPrecomputedChunk('<div hidden id="');
var startSegmentHTML2 = stringToPrecomputedChunk('">');
var endSegmentHTML = stringToPrecomputedChunk('</div>');
Expand Down Expand Up @@ -578,7 +580,7 @@ var endSegmentColGroup = stringToPrecomputedChunk('</colgroup></table>');
// const SUSPENSE_PENDING_START_DATA = '$?';
// const SUSPENSE_FALLBACK_START_DATA = '$!';
//
// function clientRenderBoundary(suspenseBoundaryID, errorHash, errorMsg, errorComponentStack) {
// function clientRenderBoundary(suspenseBoundaryID, errorDigest, errorMsg, errorComponentStack) {
// // Find the fallback's first element.
// const suspenseIdNode = document.getElementById(suspenseBoundaryID);
// if (!suspenseIdNode) {
Expand All @@ -592,9 +594,9 @@ var endSegmentColGroup = stringToPrecomputedChunk('</colgroup></table>');
// suspenseNode.data = SUSPENSE_FALLBACK_START_DATA;
// // assign error metadata to first sibling
// let dataset = suspenseIdNode.dataset;
// if (errorHash) dataset.hash = errorHash;
// if (errorDigest) dataset.dgst = errorDigest;
// if (errorMsg) dataset.msg = errorMsg;
// if (errorComponentStack) dataset.stack = errorComponentStack;
// if (errorComponentStack) dataset.stck = errorComponentStack;
// // Tell React to retry it if the parent already hydrated.
// if (suspenseNode._reactRetry) {
// suspenseNode._reactRetry();
Expand Down Expand Up @@ -678,7 +680,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(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 clientRenderFunction = 'function $RX(b,c,d,e){var a=document.getElementById(b);a&&(b=a.previousSibling,b.data="$!",a=a.dataset,c&&(a.dgst=c),d&&(a.msg=d),e&&(a.stck=e),b._reactRetry&&b._reactRetry())}';
var completeSegmentScript1Full = stringToPrecomputedChunk(completeSegmentFunction + ';$RS("');
var completeSegmentScript1Partial = stringToPrecomputedChunk('$RS("');
var completeSegmentScript2 = stringToPrecomputedChunk('","');
Expand Down Expand Up @@ -883,6 +885,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 @@ -931,7 +941,7 @@ var Dispatcher = {
useLayoutEffect: unsupportedHook,
useImperativeHandle: unsupportedHook,
useEffect: unsupportedHook,
useId: unsupportedHook,
useId: useId,
useMutableSource: unsupportedHook,
useSyncExternalStore: unsupportedHook,
useCacheRefresh: function () {
Expand All @@ -958,6 +968,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 @@ -976,7 +996,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 @@ -993,6 +1013,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 @@ -1605,6 +1627,7 @@ function performWork(request) {
var prevCache = getCurrentCache();
ReactCurrentDispatcher.current = Dispatcher;
setCurrentCache(request.cache);
prepareToUseHooksForRequest(request);

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

Expand Down Expand Up @@ -1749,7 +1773,7 @@ function importServerContexts(contexts) {
return rootContextSnapshot;
}

function renderToReadableStream(model, options, context) {
function renderToReadableStream(model, options) {
var request = createRequest( // Wrap root in a dummy element that simply adds a flag
// to the current dispatcher to check later in the proxies.
assign({}, model, {
Expand All @@ -1762,7 +1786,7 @@ function renderToReadableStream(model, options, context) {
return model;
}
}), {}, // Manifest, not used
options ? options.onError : undefined, context);
options ? options.onError : undefined, options ? options.context : undefined, options ? options.identifierPrefix : undefined);
var stream = new ReadableStream({
type: 'bytes',
start: function (controller) {
Expand Down

0 comments on commit fb04eb9

Please sign in to comment.