UNPKG

react-server-dom-webpack

Version:

React Server Components bindings for DOM using Webpack. This is intended to be integrated into meta-frameworks. It is not intended to be imported directly.

1,672 lines (1,389 loc) 95.5 kB
/** * @license React * react-server-dom-webpack-server.edge.production.min.js * * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ 'use strict'; var React = require('react'); var ReactDOM = require('react-dom'); // ----------------------------------------------------------------------------- const enablePostpone = false; function scheduleWork(callback) { setTimeout(callback, 0); } const VIEW_SIZE = 512; let currentView = null; let writtenBytes = 0; function beginWriting(destination) { currentView = new Uint8Array(VIEW_SIZE); writtenBytes = 0; } function writeChunk(destination, chunk) { if (chunk.byteLength === 0) { return; } if (chunk.byteLength > VIEW_SIZE) { // one that is cached by the streaming renderer. We will enqueu // it directly and expect it is not re-used if (writtenBytes > 0) { destination.enqueue(new Uint8Array(currentView.buffer, 0, writtenBytes)); currentView = new Uint8Array(VIEW_SIZE); writtenBytes = 0; } destination.enqueue(chunk); return; } let bytesToWrite = chunk; const allowableBytes = currentView.length - writtenBytes; if (allowableBytes < bytesToWrite.byteLength) { // this chunk would overflow the current view. We enqueue a full view // and start a new view with the remaining chunk if (allowableBytes === 0) { // the current view is already full, send it destination.enqueue(currentView); } else { // fill up the current view and apply the remaining chunk bytes // to a new view. currentView.set(bytesToWrite.subarray(0, allowableBytes), writtenBytes); // writtenBytes += allowableBytes; // this can be skipped because we are going to immediately reset the view destination.enqueue(currentView); bytesToWrite = bytesToWrite.subarray(allowableBytes); } currentView = new Uint8Array(VIEW_SIZE); writtenBytes = 0; } currentView.set(bytesToWrite, writtenBytes); writtenBytes += bytesToWrite.byteLength; } function writeChunkAndReturn(destination, chunk) { writeChunk(destination, chunk); // in web streams there is no backpressure so we can alwas write more return true; } function completeWriting(destination) { if (currentView && writtenBytes > 0) { destination.enqueue(new Uint8Array(currentView.buffer, 0, writtenBytes)); currentView = null; writtenBytes = 0; } } function close$1(destination) { destination.close(); } const textEncoder = new TextEncoder(); function stringToChunk(content) { return textEncoder.encode(content); } function byteLengthOfChunk(chunk) { return chunk.byteLength; } function closeWithError(destination, error) { // $FlowFixMe[method-unbinding] if (typeof destination.error === 'function') { // $FlowFixMe[incompatible-call]: This is an Error object or the destination accepts other types. destination.error(error); } else { // Earlier implementations doesn't support this method. In that environment you're // supposed to throw from a promise returned but we don't return a promise in our // approach. We could fork this implementation but this is environment is an edge // case to begin with. It's even less common to run this in an older environment. // Even then, this is not where errors are supposed to happen and they get reported // to a global callback in addition to this anyway. So it's fine just to close this. destination.close(); } } // eslint-disable-next-line no-unused-vars const CLIENT_REFERENCE_TAG = Symbol.for('react.client.reference'); const SERVER_REFERENCE_TAG = Symbol.for('react.server.reference'); function isClientReference(reference) { return reference.$$typeof === CLIENT_REFERENCE_TAG; } function isServerReference(reference) { return reference.$$typeof === SERVER_REFERENCE_TAG; } function registerClientReference(proxyImplementation, id, exportName) { return registerClientReferenceImpl(proxyImplementation, id + '#' + exportName, false); } function registerClientReferenceImpl(proxyImplementation, id, async) { return Object.defineProperties(proxyImplementation, { $$typeof: { value: CLIENT_REFERENCE_TAG }, $$id: { value: id }, $$async: { value: async } }); } // $FlowFixMe[method-unbinding] const FunctionBind = Function.prototype.bind; // $FlowFixMe[method-unbinding] const ArraySlice = Array.prototype.slice; function bind() { // $FlowFixMe[unsupported-syntax] const newFn = FunctionBind.apply(this, arguments); if (this.$$typeof === SERVER_REFERENCE_TAG) { const args = ArraySlice.call(arguments, 1); return Object.defineProperties(newFn, { $$typeof: { value: SERVER_REFERENCE_TAG }, $$id: { value: this.$$id }, $$bound: { value: this.$$bound ? this.$$bound.concat(args) : args }, bind: { value: bind } }); } return newFn; } function registerServerReference(reference, id, exportName) { return Object.defineProperties(reference, { $$typeof: { value: SERVER_REFERENCE_TAG }, $$id: { value: exportName === null ? id : id + '#' + exportName }, $$bound: { value: null }, bind: { value: bind } }); } const PROMISE_PROTOTYPE = Promise.prototype; const deepProxyHandlers = { get: function (target, name, receiver) { switch (name) { // These names are read by the Flight runtime if you end up using the exports object. case '$$typeof': // These names are a little too common. We should probably have a way to // have the Flight runtime extract the inner target instead. return target.$$typeof; case '$$id': return target.$$id; case '$$async': return target.$$async; case 'name': return target.name; case 'displayName': return undefined; // We need to special case this because createElement reads it if we pass this // reference. case 'defaultProps': return undefined; // Avoid this attempting to be serialized. case 'toJSON': return undefined; case Symbol.toPrimitive: // $FlowFixMe[prop-missing] return Object.prototype[Symbol.toPrimitive]; case 'Provider': throw new Error("Cannot render a Client Context Provider on the Server. " + "Instead, you can export a Client Component wrapper " + "that itself renders a Client Context Provider."); } // eslint-disable-next-line react-internal/safe-string-coercion const expression = String(target.name) + '.' + String(name); throw new Error("Cannot access " + expression + " on the server. " + 'You cannot dot into a client module from a server component. ' + 'You can only pass the imported name through.'); }, set: function () { throw new Error('Cannot assign to a client module from a server module.'); } }; function getReference(target, name) { switch (name) { // These names are read by the Flight runtime if you end up using the exports object. case '$$typeof': return target.$$typeof; case '$$id': return target.$$id; case '$$async': return target.$$async; case 'name': return target.name; // We need to special case this because createElement reads it if we pass this // reference. case 'defaultProps': return undefined; // Avoid this attempting to be serialized. case 'toJSON': return undefined; case Symbol.toPrimitive: // $FlowFixMe[prop-missing] return Object.prototype[Symbol.toPrimitive]; case '__esModule': // Something is conditionally checking which export to use. We'll pretend to be // an ESM compat module but then we'll check again on the client. const moduleId = target.$$id; target.default = registerClientReferenceImpl(function () { throw new Error("Attempted to call the default export of " + moduleId + " from the server " + "but it's on the client. It's not possible to invoke a client function from " + "the server, it can only be rendered as a Component or passed to props of a " + "Client Component."); }, target.$$id + '#', target.$$async); return true; case 'then': if (target.then) { // Use a cached value return target.then; } if (!target.$$async) { // If this module is expected to return a Promise (such as an AsyncModule) then // we should resolve that with a client reference that unwraps the Promise on // the client. const clientReference = registerClientReferenceImpl({}, target.$$id, true); const proxy = new Proxy(clientReference, proxyHandlers); // Treat this as a resolved Promise for React's use() target.status = 'fulfilled'; target.value = proxy; const then = target.then = registerClientReferenceImpl(function then(resolve, reject) { // Expose to React. return Promise.resolve(resolve(proxy)); }, // If this is not used as a Promise but is treated as a reference to a `.then` // export then we should treat it as a reference to that name. target.$$id + '#then', false); return then; } else { // Since typeof .then === 'function' is a feature test we'd continue recursing // indefinitely if we return a function. Instead, we return an object reference // if we check further. return undefined; } } let cachedReference = target[name]; if (!cachedReference) { const reference = registerClientReferenceImpl(function () { throw new Error( // eslint-disable-next-line react-internal/safe-string-coercion "Attempted to call " + String(name) + "() from the server but " + String(name) + " is on the client. " + "It's not possible to invoke a client function from the server, it can " + "only be rendered as a Component or passed to props of a Client Component."); }, target.$$id + '#' + name, target.$$async); Object.defineProperty(reference, 'name', { value: name }); cachedReference = target[name] = new Proxy(reference, deepProxyHandlers); } return cachedReference; } const proxyHandlers = { get: function (target, name, receiver) { return getReference(target, name); }, getOwnPropertyDescriptor: function (target, name) { let descriptor = Object.getOwnPropertyDescriptor(target, name); if (!descriptor) { descriptor = { value: getReference(target, name), writable: false, configurable: false, enumerable: false }; Object.defineProperty(target, name, descriptor); } return descriptor; }, getPrototypeOf(target) { // Pretend to be a Promise in case anyone asks. return PROMISE_PROTOTYPE; }, set: function () { throw new Error('Cannot assign to a client module from a server module.'); } }; function createClientModuleProxy(moduleId) { const clientReference = registerClientReferenceImpl({}, // Represents the whole Module object instead of a particular import. moduleId, false); return new Proxy(clientReference, proxyHandlers); } function getClientReferenceKey(reference) { return reference.$$async ? reference.$$id + '#async' : reference.$$id; } function resolveClientReferenceMetadata(config, clientReference) { const modulePath = clientReference.$$id; let name = ''; let resolvedModuleData = config[modulePath]; if (resolvedModuleData) { // The potentially aliased name. name = resolvedModuleData.name; } else { // We didn't find this specific export name but we might have the * export // which contains this name as well. // TODO: It's unfortunate that we now have to parse this string. We should // probably go back to encoding path and name separately on the client reference. const idx = modulePath.lastIndexOf('#'); if (idx !== -1) { name = modulePath.slice(idx + 1); resolvedModuleData = config[modulePath.slice(0, idx)]; } if (!resolvedModuleData) { throw new Error('Could not find the module "' + modulePath + '" in the React Client Manifest. ' + 'This is probably a bug in the React Server Components bundler.'); } } if (clientReference.$$async === true) { return [resolvedModuleData.id, resolvedModuleData.chunks, name, 1]; } else { return [resolvedModuleData.id, resolvedModuleData.chunks, name]; } } function getServerReferenceId(config, serverReference) { return serverReference.$$id; } function getServerReferenceBoundArguments(config, serverReference) { return serverReference.$$bound; } const ReactDOMSharedInternals = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; const ReactDOMFlightServerDispatcher = { prefetchDNS, preconnect, preload, preloadModule: preloadModule$1, preinitStyle, preinitScript, preinitModuleScript }; function prefetchDNS(href) { { if (typeof href === 'string' && href) { const request = resolveRequest(); if (request) { const hints = getHints(request); const key = 'D|' + href; if (hints.has(key)) { // duplicate hint return; } hints.add(key); emitHint(request, 'D', href); } } } } function preconnect(href, crossOrigin) { { if (typeof href === 'string') { const request = resolveRequest(); if (request) { const hints = getHints(request); const key = "C|" + (crossOrigin == null ? 'null' : crossOrigin) + "|" + href; if (hints.has(key)) { // duplicate hint return; } hints.add(key); if (typeof crossOrigin === 'string') { emitHint(request, 'C', [href, crossOrigin]); } else { emitHint(request, 'C', href); } } } } } function preload(href, as, options) { { if (typeof href === 'string') { const request = resolveRequest(); if (request) { const hints = getHints(request); let key = 'L'; if (as === 'image' && options) { key += getImagePreloadKey(href, options.imageSrcSet, options.imageSizes); } else { key += "[" + as + "]" + href; } if (hints.has(key)) { // duplicate hint return; } hints.add(key); const trimmed = trimOptions(options); if (trimmed) { emitHint(request, 'L', [href, as, trimmed]); } else { emitHint(request, 'L', [href, as]); } } } } } function preloadModule$1(href, options) { { if (typeof href === 'string') { const request = resolveRequest(); if (request) { const hints = getHints(request); const key = 'm|' + href; if (hints.has(key)) { // duplicate hint return; } hints.add(key); const trimmed = trimOptions(options); if (trimmed) { return emitHint(request, 'm', [href, trimmed]); } else { return emitHint(request, 'm', href); } } } } } function preinitStyle(href, precedence, options) { { if (typeof href === 'string') { const request = resolveRequest(); if (request) { const hints = getHints(request); const key = 'S|' + href; if (hints.has(key)) { // duplicate hint return; } hints.add(key); const trimmed = trimOptions(options); if (trimmed) { return emitHint(request, 'S', [href, typeof precedence === 'string' ? precedence : 0, trimmed]); } else if (typeof precedence === 'string') { return emitHint(request, 'S', [href, precedence]); } else { return emitHint(request, 'S', href); } } } } } function preinitScript(href, options) { { if (typeof href === 'string') { const request = resolveRequest(); if (request) { const hints = getHints(request); const key = 'X|' + href; if (hints.has(key)) { // duplicate hint return; } hints.add(key); const trimmed = trimOptions(options); if (trimmed) { return emitHint(request, 'X', [href, trimmed]); } else { return emitHint(request, 'X', href); } } } } } function preinitModuleScript(href, options) { { if (typeof href === 'string') { const request = resolveRequest(); if (request) { const hints = getHints(request); const key = 'M|' + href; if (hints.has(key)) { // duplicate hint return; } hints.add(key); const trimmed = trimOptions(options); if (trimmed) { return emitHint(request, 'M', [href, trimmed]); } else { return emitHint(request, 'M', href); } } } } } // Flight normally encodes undefined as a special character however for directive option // arguments we don't want to send unnecessary keys and bloat the payload so we create a // trimmed object which omits any keys with null or undefined values. // This is only typesafe because these option objects have entirely optional fields where // null and undefined represent the same thing as no property. function trimOptions(options) { if (options == null) return null; let hasProperties = false; const trimmed = {}; for (const key in options) { if (options[key] != null) { hasProperties = true; trimmed[key] = options[key]; } } return hasProperties ? trimmed : null; } function getImagePreloadKey(href, imageSrcSet, imageSizes) { let uniquePart = ''; if (typeof imageSrcSet === 'string' && imageSrcSet !== '') { uniquePart += '[' + imageSrcSet + ']'; if (typeof imageSizes === 'string') { uniquePart += '[' + imageSizes + ']'; } } else { uniquePart += '[][]' + href; } return "[image]" + uniquePart; } const ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher; function prepareHostDispatcher() { ReactDOMCurrentDispatcher.current = ReactDOMFlightServerDispatcher; } // Used to distinguish these contexts from ones used in other renderers. // small, smaller than how we encode undefined, and is unambiguous. We could use // a different tuple structure to encode this instead but this makes the runtime // cost cheaper by eliminating a type checks in more positions. // prettier-ignore function createHints() { return new Set(); } const supportsRequestStorage = typeof AsyncLocalStorage === 'function'; const requestStorage = supportsRequestStorage ? new AsyncLocalStorage() : null; // We use the Node version but get access to async_hooks from a global. typeof async_hooks === 'object' ? async_hooks.createHook : function () { return { enable() {}, disable() {} }; }; typeof async_hooks === 'object' ? async_hooks.executionAsyncId : null; // ATTENTION // When adding new symbols to this file, // Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols' // The Symbol used to tag the ReactElement-like types. const REACT_ELEMENT_TYPE = Symbol.for('react.element'); const REACT_FRAGMENT_TYPE = Symbol.for('react.fragment'); const REACT_CONTEXT_TYPE = Symbol.for('react.context'); const REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref'); const REACT_SUSPENSE_TYPE = Symbol.for('react.suspense'); const REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list'); const REACT_MEMO_TYPE = Symbol.for('react.memo'); const REACT_LAZY_TYPE = Symbol.for('react.lazy'); const REACT_MEMO_CACHE_SENTINEL = Symbol.for('react.memo_cache_sentinel'); const REACT_POSTPONE_TYPE = Symbol.for('react.postpone'); const MAYBE_ITERATOR_SYMBOL = Symbol.iterator; const FAUX_ITERATOR_SYMBOL = '@@iterator'; function getIteratorFn(maybeIterable) { if (maybeIterable === null || typeof maybeIterable !== 'object') { return null; } const maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]; if (typeof maybeIterator === 'function') { return maybeIterator; } return null; } // Corresponds to ReactFiberWakeable and ReactFizzWakeable modules. Generally, // changes to one module should be reflected in the others. // TODO: Rename this module and the corresponding Fiber one to "Thenable" // instead of "Wakeable". Or some other more appropriate name. // An error that is thrown (e.g. by `use`) to trigger Suspense. If we // detect this is caught by userspace, we'll log a warning in development. const SuspenseException = new Error("Suspense Exception: This is not a real error! It's an implementation " + 'detail of `use` to interrupt the current render. You must either ' + 'rethrow it immediately, or move the `use` call outside of the ' + '`try/catch` block. Capturing without rethrowing will lead to ' + 'unexpected behavior.\n\n' + 'To handle async errors, wrap your component in an error boundary, or ' + "call the promise's `.catch` method and pass the result to `use`"); function createThenableState() { // The ThenableState is created the first time a component suspends. If it // suspends again, we'll reuse the same state. return []; } function noop() {} function trackUsedThenable(thenableState, thenable, index) { const previous = thenableState[index]; if (previous === undefined) { thenableState.push(thenable); } else { if (previous !== thenable) { // Reuse the previous thenable, and drop the new one. We can assume // they represent the same value, because components are idempotent. // Avoid an unhandled rejection errors for the Promises that we'll // intentionally ignore. thenable.then(noop, noop); thenable = previous; } } // We use an expando to track the status and result of a thenable so that we // can synchronously unwrap the value. Think of this as an extension of the // Promise API, or a custom interface that is a superset of Thenable. // // If the thenable doesn't have a status, set it to "pending" and attach // a listener that will update its status and result when it resolves. switch (thenable.status) { case 'fulfilled': { const fulfilledValue = thenable.value; return fulfilledValue; } case 'rejected': { const rejectedError = thenable.reason; throw rejectedError; } default: { if (typeof thenable.status === 'string') ; else { const pendingThenable = thenable; pendingThenable.status = 'pending'; pendingThenable.then(fulfilledValue => { if (thenable.status === 'pending') { const fulfilledThenable = thenable; fulfilledThenable.status = 'fulfilled'; fulfilledThenable.value = fulfilledValue; } }, error => { if (thenable.status === 'pending') { const rejectedThenable = thenable; rejectedThenable.status = 'rejected'; rejectedThenable.reason = error; } }); // Check one more time in case the thenable resolved synchronously switch (thenable.status) { case 'fulfilled': { const fulfilledThenable = thenable; return fulfilledThenable.value; } case 'rejected': { const rejectedThenable = thenable; throw rejectedThenable.reason; } } } // Suspend. // // Throwing here is an implementation detail that allows us to unwind the // call stack. But we shouldn't allow it to leak into userspace. Throw an // opaque placeholder value instead of the actual thenable. If it doesn't // get captured by the work loop, log a warning, because that means // something in userspace must have caught it. suspendedThenable = thenable; throw SuspenseException; } } } // This is used to track the actual thenable that suspended so it can be // passed to the rest of the Suspense implementation — which, for historical // reasons, expects to receive a thenable. let suspendedThenable = null; function getSuspendedThenable() { // This is called right after `use` suspends by throwing an exception. `use` // throws an opaque value instead of the thenable itself so that it can't be // caught in userspace. Then the work loop accesses the actual thenable using // this function. if (suspendedThenable === null) { throw new Error('Expected a suspended thenable. This is a bug in React. Please file ' + 'an issue.'); } const thenable = suspendedThenable; suspendedThenable = null; return thenable; } let currentRequest$1 = null; let thenableIndexCounter = 0; let thenableState = null; function prepareToUseHooksForRequest(request) { currentRequest$1 = request; } function resetHooksForRequest() { currentRequest$1 = null; } function prepareToUseHooksForComponent(prevThenableState) { thenableIndexCounter = 0; thenableState = prevThenableState; } function getThenableStateAfterSuspending() { // If you use() to Suspend this should always exist but if you throw a Promise instead, // which is not really supported anymore, it will be empty. We use the empty set as a // marker to know if this was a replay of the same component or first attempt. const state = thenableState || createThenableState(); thenableState = null; return state; } const HooksDispatcher = { useMemo(nextCreate) { return nextCreate(); }, useCallback(callback) { return callback; }, useDebugValue() {}, useDeferredValue: unsupportedHook, useTransition: unsupportedHook, readContext: unsupportedContext, useContext: unsupportedContext, useReducer: unsupportedHook, useRef: unsupportedHook, useState: unsupportedHook, useInsertionEffect: unsupportedHook, useLayoutEffect: unsupportedHook, useImperativeHandle: unsupportedHook, useEffect: unsupportedHook, useId, useSyncExternalStore: unsupportedHook, useCacheRefresh() { return unsupportedRefresh; }, useMemoCache(size) { const data = new Array(size); for (let i = 0; i < size; i++) { data[i] = REACT_MEMO_CACHE_SENTINEL; } return data; }, use }; function unsupportedHook() { throw new Error('This Hook is not supported in Server Components.'); } function unsupportedRefresh() { throw new Error('Refreshing the cache is not supported in Server Components.'); } function unsupportedContext() { throw new Error('Cannot read a Client Context from a Server Component.'); } function useId() { if (currentRequest$1 === null) { throw new Error('useId can only be used while React is rendering'); } const id = currentRequest$1.identifierCount++; // use 'S' for Flight components to distinguish from 'R' and 'r' in Fizz/Client return ':' + currentRequest$1.identifierPrefix + 'S' + id.toString(32) + ':'; } function use(usable) { if (usable !== null && typeof usable === 'object' || typeof usable === 'function') { // $FlowFixMe[method-unbinding] if (typeof usable.then === 'function') { // This is a thenable. const thenable = usable; // Track the position of the thenable within this fiber. const index = thenableIndexCounter; thenableIndexCounter += 1; if (thenableState === null) { thenableState = createThenableState(); } return trackUsedThenable(thenableState, thenable, index); } else if (usable.$$typeof === REACT_CONTEXT_TYPE) { unsupportedContext(); } } if (isClientReference(usable)) { if (usable.value != null && usable.value.$$typeof === REACT_CONTEXT_TYPE) { // Show a more specific message since it's a common mistake. throw new Error('Cannot read a Client Context from a Server Component.'); } else { throw new Error('Cannot use() an already resolved Client Reference.'); } } else { throw new Error( // eslint-disable-next-line react-internal/safe-string-coercion 'An unsupported type was passed to use(): ' + String(usable)); } } function createSignal() { return new AbortController().signal; } function resolveCache() { const request = resolveRequest(); if (request) { return getCache(request); } return new Map(); } const DefaultCacheDispatcher = { getCacheSignal() { const cache = resolveCache(); let entry = cache.get(createSignal); if (entry === undefined) { entry = createSignal(); cache.set(createSignal, entry); } return entry; }, getCacheForType(resourceType) { const cache = resolveCache(); let entry = cache.get(resourceType); if (entry === undefined) { entry = resourceType(); // TODO: Warn if undefined? cache.set(resourceType, entry); } return entry; } }; const isArrayImpl = Array.isArray; // eslint-disable-next-line no-redeclare function isArray(a) { return isArrayImpl(a); } const getPrototypeOf = Object.getPrototypeOf; function objectName(object) { // $FlowFixMe[method-unbinding] const name = Object.prototype.toString.call(object); return name.replace(/^\[object (.*)\]$/, function (m, p0) { return p0; }); } function describeKeyForErrorMessage(key) { const encodedKey = JSON.stringify(key); return '"' + key + '"' === encodedKey ? key : encodedKey; } function describeValueForErrorMessage(value) { switch (typeof value) { case 'string': { return JSON.stringify(value.length <= 10 ? value : value.slice(0, 10) + '...'); } case 'object': { if (isArray(value)) { return '[...]'; } const name = objectName(value); if (name === 'Object') { return '{...}'; } return name; } case 'function': return 'function'; default: // eslint-disable-next-line react-internal/safe-string-coercion return String(value); } } function describeElementType(type) { if (typeof type === 'string') { return type; } switch (type) { case REACT_SUSPENSE_TYPE: return 'Suspense'; case REACT_SUSPENSE_LIST_TYPE: return 'SuspenseList'; } if (typeof type === 'object') { switch (type.$$typeof) { case REACT_FORWARD_REF_TYPE: return describeElementType(type.render); case REACT_MEMO_TYPE: return describeElementType(type.type); case REACT_LAZY_TYPE: { const lazyComponent = type; const payload = lazyComponent._payload; const init = lazyComponent._init; try { // Lazy may contain any component type so we recursively resolve it. return describeElementType(init(payload)); } catch (x) {} } } } return ''; } function describeObjectForErrorMessage(objectOrArray, expandedName) { const objKind = objectName(objectOrArray); if (objKind !== 'Object' && objKind !== 'Array') { return objKind; } let str = ''; let start = -1; let length = 0; if (isArray(objectOrArray)) { { // Print Array str = '['; const array = objectOrArray; for (let i = 0; i < array.length; i++) { if (i > 0) { str += ', '; } const value = array[i]; let substr; if (typeof value === 'object' && value !== null) { substr = describeObjectForErrorMessage(value); } else { substr = describeValueForErrorMessage(value); } if ('' + i === expandedName) { start = str.length; length = substr.length; str += substr; } else if (substr.length < 10 && str.length + substr.length < 40) { str += substr; } else { str += '...'; } } str += ']'; } } else { if (objectOrArray.$$typeof === REACT_ELEMENT_TYPE) { str = '<' + describeElementType(objectOrArray.type) + '/>'; } else { // Print Object str = '{'; const object = objectOrArray; const names = Object.keys(object); for (let i = 0; i < names.length; i++) { if (i > 0) { str += ', '; } const name = names[i]; str += describeKeyForErrorMessage(name) + ': '; const value = object[name]; let substr; if (typeof value === 'object' && value !== null) { substr = describeObjectForErrorMessage(value); } else { substr = describeValueForErrorMessage(value); } if (name === expandedName) { start = str.length; length = substr.length; str += substr; } else if (substr.length < 10 && str.length + substr.length < 40) { str += substr; } else { str += '...'; } } str += '}'; } } if (expandedName === undefined) { return str; } if (start > -1 && length > 0) { const highlight = ' '.repeat(start) + '^'.repeat(length); return '\n ' + str + '\n ' + highlight; } return '\n ' + str; } const ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; const ReactSharedServerInternals = // $FlowFixMe: It's defined in the one we resolve to. React.__SECRET_SERVER_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; if (!ReactSharedServerInternals) { throw new Error('The "react" package in this environment is not configured correctly. ' + 'The "react-server" condition must be enabled in any environment that ' + 'runs React Server Components.'); } const ObjectPrototype = Object.prototype; const stringify = JSON.stringify; // Serializable values // Thenable<ReactClientValue> const PENDING$1 = 0; const COMPLETED = 1; const ABORTED = 3; const ERRORED$1 = 4; const ReactCurrentCache = ReactSharedServerInternals.ReactCurrentCache; const ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; function defaultErrorHandler(error) { console['error'](error); // Don't transform to our wrapper } function defaultPostponeHandler(reason) {// Noop } const OPEN = 0; const CLOSING = 1; const CLOSED = 2; function createRequest(model, bundlerConfig, onError, identifierPrefix, onPostpone, environmentName) { if (ReactCurrentCache.current !== null && ReactCurrentCache.current !== DefaultCacheDispatcher) { throw new Error('Currently React only supports one RSC renderer at a time.'); } prepareHostDispatcher(); ReactCurrentCache.current = DefaultCacheDispatcher; const abortSet = new Set(); const pingedTasks = []; const cleanupQueue = []; const hints = createHints(); const request = { status: OPEN, flushScheduled: false, fatalError: null, destination: null, bundlerConfig, cache: new Map(), nextChunkId: 0, pendingChunks: 0, hints, abortableTasks: abortSet, pingedTasks: pingedTasks, completedImportChunks: [], completedHintChunks: [], completedRegularChunks: [], completedErrorChunks: [], writtenSymbols: new Map(), writtenClientReferences: new Map(), writtenServerReferences: new Map(), writtenObjects: new WeakMap(), identifierPrefix: identifierPrefix || '', identifierCount: 1, taintCleanupQueue: cleanupQueue, onError: onError === undefined ? defaultErrorHandler : onError, onPostpone: onPostpone === undefined ? defaultPostponeHandler : onPostpone }; const rootTask = createTask(request, model, null, false, abortSet); pingedTasks.push(rootTask); return request; } let currentRequest = null; function resolveRequest() { if (currentRequest) return currentRequest; if (supportsRequestStorage) { const store = requestStorage.getStore(); if (store) return store; } return null; } function serializeThenable(request, task, thenable) { const newTask = createTask(request, null, task.keyPath, // the server component sequence continues through Promise-as-a-child. task.implicitSlot, request.abortableTasks); switch (thenable.status) { case 'fulfilled': { // We have the resolved value, we can go ahead and schedule it for serialization. newTask.model = thenable.value; pingTask(request, newTask); return newTask.id; } case 'rejected': { const x = thenable.reason; { const digest = logRecoverableError(request, x); emitErrorChunk(request, newTask.id, digest); } return newTask.id; } default: { if (typeof thenable.status === 'string') { // Only instrument the thenable if the status if not defined. If // it's defined, but an unknown value, assume it's been instrumented by // some custom userspace implementation. We treat it as "pending". break; } const pendingThenable = thenable; pendingThenable.status = 'pending'; pendingThenable.then(fulfilledValue => { if (thenable.status === 'pending') { const fulfilledThenable = thenable; fulfilledThenable.status = 'fulfilled'; fulfilledThenable.value = fulfilledValue; } }, error => { if (thenable.status === 'pending') { const rejectedThenable = thenable; rejectedThenable.status = 'rejected'; rejectedThenable.reason = error; } }); break; } } thenable.then(value => { newTask.model = value; pingTask(request, newTask); }, reason => { { newTask.status = ERRORED$1; const digest = logRecoverableError(request, reason); emitErrorChunk(request, newTask.id, digest); } request.abortableTasks.delete(newTask); if (request.destination !== null) { flushCompletedChunks(request, request.destination); } }); return newTask.id; } function emitHint(request, code, model) { emitHintChunk(request, code, model); enqueueFlush(request); } function getHints(request) { return request.hints; } function getCache(request) { return request.cache; } function readThenable(thenable) { if (thenable.status === 'fulfilled') { return thenable.value; } else if (thenable.status === 'rejected') { throw thenable.reason; } throw thenable; } function createLazyWrapperAroundWakeable(wakeable) { // This is a temporary fork of the `use` implementation until we accept // promises everywhere. const thenable = wakeable; switch (thenable.status) { case 'fulfilled': case 'rejected': break; default: { if (typeof thenable.status === 'string') { // Only instrument the thenable if the status if not defined. If // it's defined, but an unknown value, assume it's been instrumented by // some custom userspace implementation. We treat it as "pending". break; } const pendingThenable = thenable; pendingThenable.status = 'pending'; pendingThenable.then(fulfilledValue => { if (thenable.status === 'pending') { const fulfilledThenable = thenable; fulfilledThenable.status = 'fulfilled'; fulfilledThenable.value = fulfilledValue; } }, error => { if (thenable.status === 'pending') { const rejectedThenable = thenable; rejectedThenable.status = 'rejected'; rejectedThenable.reason = error; } }); break; } } const lazyType = { $$typeof: REACT_LAZY_TYPE, _payload: thenable, _init: readThenable }; return lazyType; } function renderFunctionComponent(request, task, key, Component, props) { // Reset the task's thenable state before continuing, so that if a later // component suspends we can reuse the same task object. If the same // component suspends again, the thenable state will be restored. const prevThenableState = task.thenableState; task.thenableState = null; prepareToUseHooksForComponent(prevThenableState); // The secondArg is always undefined in Server Components since refs error early. const secondArg = undefined; let result = Component(props, secondArg); if (typeof result === 'object' && result !== null && typeof result.then === 'function') { // When the return value is in children position we can resolve it immediately, // to its value without a wrapper if it's synchronously available. const thenable = result; if (thenable.status === 'fulfilled') { return thenable.value; } // TODO: Once we accept Promises as children on the client, we can just return // the thenable here. result = createLazyWrapperAroundWakeable(result); } // Track this element's key on the Server Component on the keyPath context.. const prevKeyPath = task.keyPath; const prevImplicitSlot = task.implicitSlot; if (key !== null) { // Append the key to the path. Technically a null key should really add the child // index. We don't do that to hold the payload small and implementation simple. task.keyPath = prevKeyPath === null ? key : prevKeyPath + ',' + key; } else if (prevKeyPath === null) { // This sequence of Server Components has no keys. This means that it was rendered // in a slot that needs to assign an implicit key. Even if children below have // explicit keys, they should not be used for the outer most key since it might // collide with other slots in that set. task.implicitSlot = true; } const json = renderModelDestructive(request, task, emptyRoot, '', result); task.keyPath = prevKeyPath; task.implicitSlot = prevImplicitSlot; return json; } function renderFragment(request, task, children) { { return children; } } function renderClientElement(task, type, key, props) { { return [REACT_ELEMENT_TYPE, type, key, props]; } // We prepend the terminal client element that actually gets serialized with } // The chunk ID we're currently rendering that we can assign debug data to. let debugID = null; function renderElement(request, task, type, key, ref, props) { if (ref !== null && ref !== undefined) { // When the ref moves to the regular props object this will implicitly // throw for functions. We could probably relax it to a DEV warning for other // cases. throw new Error('Refs cannot be used in Server Components, nor passed to Client Components.'); } if (typeof type === 'function') { if (isClientReference(type)) { // This is a reference to a Client Component. return renderClientElement(task, type, key, props); } // This is a Server Component. return renderFunctionComponent(request, task, key, type, props); } else if (typeof type === 'string') { // This is a host element. E.g. HTML. return renderClientElement(task, type, key, props); } else if (typeof type === 'symbol') { if (type === REACT_FRAGMENT_TYPE && key === null) { // For key-less fragments, we add a small optimization to avoid serializing // it as a wrapper. const prevImplicitSlot = task.implicitSlot; if (task.keyPath === null) { task.implicitSlot = true; } const json = renderModelDestructive(request, task, emptyRoot, '', props.children); task.implicitSlot = prevImplicitSlot; return json; } // This might be a built-in React component. We'll let the client decide. // Any built-in works as long as its props are serializable. return renderClientElement(task, type, key, props); } else if (type != null && typeof type === 'object') { if (isClientReference(type)) { // This is a reference to a Client Component. return renderClientElement(task, type, key, props); } switch (type.$$typeof) { case REACT_LAZY_TYPE: { const payload = type._payload; const init = type._init; const wrappedType = init(payload); return renderElement(request, task, wrappedType, key, ref, props); } case REACT_FORWARD_REF_TYPE: { return renderFunctionComponent(request, task, key, type.render, props); } case REACT_MEMO_TYPE: { return renderElement(request, task, type.type, key, ref, props); } } } throw new Error("Unsupported Server Component type: " + describeValueForErrorMessage(type)); } function pingTask(request, task) { const pingedTasks = request.pingedTasks; pingedTasks.push(task); if (pingedTasks.length === 1) { request.flushScheduled = request.destination !== null; scheduleWork(() => performWork(request)); } } function createTask(request, model, keyPath, implicitSlot, abortSet) { request.pendingChunks++; const id = request.nextChunkId++; if (typeof model === 'object' && model !== null) { // If we're about to write this into a new task we can assign it an ID early so that // any other references can refer to the value we're about to write. { request.writtenObjects.set(model, id); } } const task = { id, status: PENDING$1, model, keyPath, implicitSlot, ping: () => pingTask(request, task), toJSON: function (parentPropertyName, value) { const parent = this; // Make sure that `parent[parentPropertyName]` wasn't JSONified before `value` was passed to us return renderModel(request, task, parent, parentPropertyName, value); }, thenableState: null }; abortSet.add(task); return task; } function serializeByValueID(id) { return '$' + id.toString(16); } function serializeLazyID(id) { return '$L' + id.toString(16); } function serializePromiseID(id) { return '$@' + id.toString(16); } function serializeServerReferenceID(id) { return '$F' + id.toString(16); } function serializeSymbolReference(name) { return '$S' + name; } function serializeNumber(number) { if (Number.isFinite(number)) { if (number === 0 && 1 / number === -Infinity) { return '$-0'; } else { return number; } } else { if (number === Infinity) { return '$Infinity'; } else if (number === -Infinity) { return '$-Infinity'; } else { return '$NaN'; } } } function serializeUndefined() { return '$undefined'; } function serializeDateFromDateJSON(dateJSON) { // JSON.stringify automatically calls Date.prototype.toJSON which calls toISOString. // We need only tack on a $D prefix. return '$D' + dateJSON; } function serializeBigInt(n) { return '$n' + n.toString(10); } function serializeRowHeader(tag, id) { return id.toString(16) + ':' + tag; } function encodeReferenceChunk(request, id, reference) { const json = stringify(reference); const row = id.toString(16) + ':' + json + '\n'; return stringToChunk(row); } function serializeClientReference(request, parent, parentPropertyName, clientReference) { const clientReferenceKey = getClientReferenceKey(clientReference); const writtenClientReferences = request.writtenClientReferences; const existingId = writtenClientReferences.get(clientReferenceKey); if (existingId !== undefined) { if (parent[0] === REACT_ELEMENT_TYPE && parentPropertyName === '1') { // If we're encoding the "type" of an element, we can refer // to that by a lazy reference instead of directly since React // knows how to deal with lazy values. This lets us suspend // on this component rather than its parent until the code has // loaded. return serializeLazyID(existingId); } return serializeByValueID(existingId); } try { const clientReferenceMetadata = resolveClientReferenceMetadata(request.bundlerConfig, clientReference); request.pendingChunks++; const importId = request.nextChunkId++; emitImportChunk(request, importId, clientReferenceMetadata); writtenClientReferences.set(clientReferenceKey, importId); if (parent[0] === REACT_ELEMENT_TYPE && parentPropertyName === '1') { // If we're encoding the "type" of an element, we can refer // to that by a lazy reference instead of directly since React // knows how to deal with lazy values. This lets us suspend // on this component rather than its parent until the code has // loaded. return serializeLazyID(importId); } return serializeByValueID(importId); } catch (x) { request.pendingChunks++; const errorId = request.nextChunkId++; const digest = logRecoverableError(request, x); emitErrorChunk(request, errorId, digest); return serializeByValueID(errorId); } } function outlineModel(request, value) { const newTask = createTask(request, value, null, // The way we use outlining is for reusing an object. false, // It makes no sense for that use case to be contextual. request.abortableTasks); retryTask(request, newTask); return newTask.id; } function serializeServerReference(request, serverReference) { const writtenServerReferences = request.writtenServerReferences; const existingId = writtenServerReferences.get(serverReference); if (existingId !== undefined) { return serializeServerReferenceID(existingId); } const bound = getServerReferenceBoundArguments(request.bundlerConfig, serverReference); const serverReferenceMetadata = { id: getServerReferenceId(request.bundlerConfig, serverReference), bound: bound ? Promise.resolve(bound) : null }; const metadataId = outlineModel(request, serverReferenceMetadata); writtenServerReferences.set(serverReference, metadataId); return serializeServer