3614 lines
107 KiB
JavaScript
3614 lines
107 KiB
JavaScript
/**
|
|
* @license React
|
|
* react-server-dom-webpack-server.edge.development.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';
|
|
|
|
if (process.env.NODE_ENV !== "production") {
|
|
(function() {
|
|
'use strict';
|
|
|
|
var React = require('react');
|
|
var ReactDOM = require('react-dom');
|
|
|
|
var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
|
|
|
function error(format) {
|
|
{
|
|
{
|
|
for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
|
|
args[_key2 - 1] = arguments[_key2];
|
|
}
|
|
|
|
printWarning('error', format, args);
|
|
}
|
|
}
|
|
}
|
|
|
|
function printWarning(level, format, args) {
|
|
// When changing this logic, you might want to also
|
|
// update consoleWithStackDev.www.js as well.
|
|
{
|
|
var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
|
|
var stack = ReactDebugCurrentFrame.getStackAddendum();
|
|
|
|
if (stack !== '') {
|
|
format += '%s';
|
|
args = args.concat([stack]);
|
|
} // eslint-disable-next-line react-internal/safe-string-coercion
|
|
|
|
|
|
var argsWithFormat = args.map(function (item) {
|
|
return String(item);
|
|
}); // Careful: RN currently depends on this prefix
|
|
|
|
argsWithFormat.unshift('Warning: ' + format); // We intentionally don't use spread (or .apply) directly because it
|
|
// breaks IE9: https://github.com/facebook/react/issues/13610
|
|
// eslint-disable-next-line react-internal/no-production-logging
|
|
|
|
Function.prototype.apply.call(console[level], console, argsWithFormat);
|
|
}
|
|
}
|
|
|
|
function scheduleWork(callback) {
|
|
setTimeout(callback, 0);
|
|
}
|
|
var VIEW_SIZE = 512;
|
|
var currentView = null;
|
|
var 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) {
|
|
{
|
|
if (precomputedChunkSet.has(chunk)) {
|
|
error('A large precomputed chunk was passed to writeChunk without being copied.' + ' Large chunks get enqueued directly and are not copied. This is incompatible with precomputed chunks because you cannot enqueue the same precomputed chunk twice.' + ' Use "cloneChunk" to make a copy of this large precomputed chunk before writing it. This is a bug in React.');
|
|
}
|
|
} // this chunk may overflow a single view which implies it was not
|
|
// 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;
|
|
}
|
|
|
|
var bytesToWrite = chunk;
|
|
var 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();
|
|
}
|
|
var textEncoder = new TextEncoder();
|
|
function stringToChunk(content) {
|
|
return textEncoder.encode(content);
|
|
}
|
|
var precomputedChunkSet = new Set() ;
|
|
function typedArrayToBinaryChunk(content) {
|
|
// Convert any non-Uint8Array array to Uint8Array. We could avoid this for Uint8Arrays.
|
|
// If we passed through this straight to enqueue we wouldn't have to convert it but since
|
|
// we need to copy the buffer in that case, we need to convert it to copy it.
|
|
// When we copy it into another array using set() it needs to be a Uint8Array.
|
|
var buffer = new Uint8Array(content.buffer, content.byteOffset, content.byteLength); // We clone large chunks so that we can transfer them when we write them.
|
|
// Others get copied into the target buffer.
|
|
|
|
return content.byteLength > VIEW_SIZE ? buffer.slice() : buffer;
|
|
}
|
|
function byteLengthOfChunk(chunk) {
|
|
return chunk.byteLength;
|
|
}
|
|
function byteLengthOfBinaryChunk(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
|
|
var CLIENT_REFERENCE_TAG = Symbol.for('react.client.reference');
|
|
var 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]
|
|
|
|
|
|
var FunctionBind = Function.prototype.bind; // $FlowFixMe[method-unbinding]
|
|
|
|
var ArraySlice = Array.prototype.slice;
|
|
|
|
function bind() {
|
|
// $FlowFixMe[unsupported-syntax]
|
|
var newFn = FunctionBind.apply(this, arguments);
|
|
|
|
if (this.$$typeof === SERVER_REFERENCE_TAG) {
|
|
var args = ArraySlice.call(arguments, 1);
|
|
newFn.$$typeof = SERVER_REFERENCE_TAG;
|
|
newFn.$$id = this.$$id;
|
|
newFn.$$bound = this.$$bound ? this.$$bound.concat(args) : args;
|
|
}
|
|
|
|
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
|
|
}
|
|
});
|
|
}
|
|
var PROMISE_PROTOTYPE = Promise.prototype;
|
|
var 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
|
|
|
|
|
|
var 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.
|
|
var 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.
|
|
var clientReference = registerClientReferenceImpl({}, target.$$id, true);
|
|
var proxy = new Proxy(clientReference, proxyHandlers); // Treat this as a resolved Promise for React's use()
|
|
|
|
target.status = 'fulfilled';
|
|
target.value = proxy;
|
|
var 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;
|
|
}
|
|
|
|
}
|
|
|
|
var cachedReference = target[name];
|
|
|
|
if (!cachedReference) {
|
|
var 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;
|
|
}
|
|
|
|
var proxyHandlers = {
|
|
get: function (target, name, receiver) {
|
|
return getReference(target, name);
|
|
},
|
|
getOwnPropertyDescriptor: function (target, name) {
|
|
var 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: function (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) {
|
|
var 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) {
|
|
var modulePath = clientReference.$$id;
|
|
var name = '';
|
|
var 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.
|
|
var 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;
|
|
}
|
|
|
|
var ReactDOMSharedInternals = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
|
|
|
var ReactDOMFlightServerDispatcher = {
|
|
prefetchDNS: prefetchDNS,
|
|
preconnect: preconnect,
|
|
preload: preload,
|
|
preloadModule: preloadModule$1,
|
|
preinitStyle: preinitStyle,
|
|
preinitScript: preinitScript,
|
|
preinitModuleScript: preinitModuleScript
|
|
};
|
|
|
|
function prefetchDNS(href) {
|
|
{
|
|
if (typeof href === 'string' && href) {
|
|
var request = resolveRequest();
|
|
|
|
if (request) {
|
|
var hints = getHints(request);
|
|
var 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') {
|
|
var request = resolveRequest();
|
|
|
|
if (request) {
|
|
var hints = getHints(request);
|
|
var 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') {
|
|
var request = resolveRequest();
|
|
|
|
if (request) {
|
|
var hints = getHints(request);
|
|
var 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);
|
|
var 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') {
|
|
var request = resolveRequest();
|
|
|
|
if (request) {
|
|
var hints = getHints(request);
|
|
var key = 'm|' + href;
|
|
|
|
if (hints.has(key)) {
|
|
// duplicate hint
|
|
return;
|
|
}
|
|
|
|
hints.add(key);
|
|
var 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') {
|
|
var request = resolveRequest();
|
|
|
|
if (request) {
|
|
var hints = getHints(request);
|
|
var key = 'S|' + href;
|
|
|
|
if (hints.has(key)) {
|
|
// duplicate hint
|
|
return;
|
|
}
|
|
|
|
hints.add(key);
|
|
var 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') {
|
|
var request = resolveRequest();
|
|
|
|
if (request) {
|
|
var hints = getHints(request);
|
|
var key = 'X|' + href;
|
|
|
|
if (hints.has(key)) {
|
|
// duplicate hint
|
|
return;
|
|
}
|
|
|
|
hints.add(key);
|
|
var 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') {
|
|
var request = resolveRequest();
|
|
|
|
if (request) {
|
|
var hints = getHints(request);
|
|
var key = 'M|' + href;
|
|
|
|
if (hints.has(key)) {
|
|
// duplicate hint
|
|
return;
|
|
}
|
|
|
|
hints.add(key);
|
|
var 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;
|
|
var hasProperties = false;
|
|
var trimmed = {};
|
|
|
|
for (var key in options) {
|
|
if (options[key] != null) {
|
|
hasProperties = true;
|
|
trimmed[key] = options[key];
|
|
}
|
|
}
|
|
|
|
return hasProperties ? trimmed : null;
|
|
}
|
|
|
|
function getImagePreloadKey(href, imageSrcSet, imageSizes) {
|
|
var uniquePart = '';
|
|
|
|
if (typeof imageSrcSet === 'string' && imageSrcSet !== '') {
|
|
uniquePart += '[' + imageSrcSet + ']';
|
|
|
|
if (typeof imageSizes === 'string') {
|
|
uniquePart += '[' + imageSizes + ']';
|
|
}
|
|
} else {
|
|
uniquePart += '[][]' + href;
|
|
}
|
|
|
|
return "[image]" + uniquePart;
|
|
}
|
|
|
|
var 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();
|
|
}
|
|
|
|
var supportsRequestStorage = typeof AsyncLocalStorage === 'function';
|
|
var requestStorage = supportsRequestStorage ? new AsyncLocalStorage() : 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.
|
|
var REACT_ELEMENT_TYPE = Symbol.for('react.element');
|
|
var REACT_FRAGMENT_TYPE = Symbol.for('react.fragment');
|
|
var REACT_PROVIDER_TYPE = Symbol.for('react.provider');
|
|
var REACT_SERVER_CONTEXT_TYPE = Symbol.for('react.server_context');
|
|
var REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref');
|
|
var REACT_SUSPENSE_TYPE = Symbol.for('react.suspense');
|
|
var REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list');
|
|
var REACT_MEMO_TYPE = Symbol.for('react.memo');
|
|
var REACT_LAZY_TYPE = Symbol.for('react.lazy');
|
|
var REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED = Symbol.for('react.default_value');
|
|
var REACT_MEMO_CACHE_SENTINEL = Symbol.for('react.memo_cache_sentinel');
|
|
var REACT_POSTPONE_TYPE = Symbol.for('react.postpone');
|
|
var MAYBE_ITERATOR_SYMBOL = Symbol.iterator;
|
|
var FAUX_ITERATOR_SYMBOL = '@@iterator';
|
|
function getIteratorFn(maybeIterable) {
|
|
if (maybeIterable === null || typeof maybeIterable !== 'object') {
|
|
return null;
|
|
}
|
|
|
|
var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL];
|
|
|
|
if (typeof maybeIterator === 'function') {
|
|
return maybeIterator;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
var rendererSigil;
|
|
|
|
{
|
|
// Use this to detect multiple renderers using the same context
|
|
rendererSigil = {};
|
|
} // Used to store the parent path of all context overrides in a shared linked list.
|
|
// Forming a reverse tree.
|
|
// The structure of a context snapshot is an implementation of this file.
|
|
// Currently, it's implemented as tracking the current active node.
|
|
|
|
|
|
var rootContextSnapshot = null; // We assume that this runtime owns the "current" field on all ReactContext instances.
|
|
// This global (actually thread local) state represents what state all those "current",
|
|
// fields are currently in.
|
|
|
|
var currentActiveSnapshot = null;
|
|
|
|
function popNode(prev) {
|
|
{
|
|
prev.context._currentValue = prev.parentValue;
|
|
}
|
|
}
|
|
|
|
function pushNode(next) {
|
|
{
|
|
next.context._currentValue = next.value;
|
|
}
|
|
}
|
|
|
|
function popToNearestCommonAncestor(prev, next) {
|
|
if (prev === next) ; else {
|
|
popNode(prev);
|
|
var parentPrev = prev.parent;
|
|
var parentNext = next.parent;
|
|
|
|
if (parentPrev === null) {
|
|
if (parentNext !== null) {
|
|
throw new Error('The stacks must reach the root at the same time. This is a bug in React.');
|
|
}
|
|
} else {
|
|
if (parentNext === null) {
|
|
throw new Error('The stacks must reach the root at the same time. This is a bug in React.');
|
|
}
|
|
|
|
popToNearestCommonAncestor(parentPrev, parentNext); // On the way back, we push the new ones that weren't common.
|
|
|
|
pushNode(next);
|
|
}
|
|
}
|
|
}
|
|
|
|
function popAllPrevious(prev) {
|
|
popNode(prev);
|
|
var parentPrev = prev.parent;
|
|
|
|
if (parentPrev !== null) {
|
|
popAllPrevious(parentPrev);
|
|
}
|
|
}
|
|
|
|
function pushAllNext(next) {
|
|
var parentNext = next.parent;
|
|
|
|
if (parentNext !== null) {
|
|
pushAllNext(parentNext);
|
|
}
|
|
|
|
pushNode(next);
|
|
}
|
|
|
|
function popPreviousToCommonLevel(prev, next) {
|
|
popNode(prev);
|
|
var parentPrev = prev.parent;
|
|
|
|
if (parentPrev === null) {
|
|
throw new Error('The depth must equal at least at zero before reaching the root. This is a bug in React.');
|
|
}
|
|
|
|
if (parentPrev.depth === next.depth) {
|
|
// We found the same level. Now we just need to find a shared ancestor.
|
|
popToNearestCommonAncestor(parentPrev, next);
|
|
} else {
|
|
// We must still be deeper.
|
|
popPreviousToCommonLevel(parentPrev, next);
|
|
}
|
|
}
|
|
|
|
function popNextToCommonLevel(prev, next) {
|
|
var parentNext = next.parent;
|
|
|
|
if (parentNext === null) {
|
|
throw new Error('The depth must equal at least at zero before reaching the root. This is a bug in React.');
|
|
}
|
|
|
|
if (prev.depth === parentNext.depth) {
|
|
// We found the same level. Now we just need to find a shared ancestor.
|
|
popToNearestCommonAncestor(prev, parentNext);
|
|
} else {
|
|
// We must still be deeper.
|
|
popNextToCommonLevel(prev, parentNext);
|
|
}
|
|
|
|
pushNode(next);
|
|
} // Perform context switching to the new snapshot.
|
|
// To make it cheap to read many contexts, while not suspending, we make the switch eagerly by
|
|
// updating all the context's current values. That way reads, always just read the current value.
|
|
// At the cost of updating contexts even if they're never read by this subtree.
|
|
|
|
|
|
function switchContext(newSnapshot) {
|
|
// The basic algorithm we need to do is to pop back any contexts that are no longer on the stack.
|
|
// We also need to update any new contexts that are now on the stack with the deepest value.
|
|
// The easiest way to update new contexts is to just reapply them in reverse order from the
|
|
// perspective of the backpointers. To avoid allocating a lot when switching, we use the stack
|
|
// for that. Therefore this algorithm is recursive.
|
|
// 1) First we pop which ever snapshot tree was deepest. Popping old contexts as we go.
|
|
// 2) Then we find the nearest common ancestor from there. Popping old contexts as we go.
|
|
// 3) Then we reapply new contexts on the way back up the stack.
|
|
var prev = currentActiveSnapshot;
|
|
var next = newSnapshot;
|
|
|
|
if (prev !== next) {
|
|
if (prev === null) {
|
|
// $FlowFixMe[incompatible-call]: This has to be non-null since it's not equal to prev.
|
|
pushAllNext(next);
|
|
} else if (next === null) {
|
|
popAllPrevious(prev);
|
|
} else if (prev.depth === next.depth) {
|
|
popToNearestCommonAncestor(prev, next);
|
|
} else if (prev.depth > next.depth) {
|
|
popPreviousToCommonLevel(prev, next);
|
|
} else {
|
|
popNextToCommonLevel(prev, next);
|
|
}
|
|
|
|
currentActiveSnapshot = next;
|
|
}
|
|
}
|
|
function pushProvider(context, nextValue) {
|
|
var prevValue;
|
|
|
|
{
|
|
prevValue = context._currentValue;
|
|
context._currentValue = nextValue;
|
|
|
|
{
|
|
if (context._currentRenderer !== undefined && context._currentRenderer !== null && context._currentRenderer !== rendererSigil) {
|
|
error('Detected multiple renderers concurrently rendering the ' + 'same context provider. This is currently unsupported.');
|
|
}
|
|
|
|
context._currentRenderer = rendererSigil;
|
|
}
|
|
}
|
|
|
|
var prevNode = currentActiveSnapshot;
|
|
var newNode = {
|
|
parent: prevNode,
|
|
depth: prevNode === null ? 0 : prevNode.depth + 1,
|
|
context: context,
|
|
parentValue: prevValue,
|
|
value: nextValue
|
|
};
|
|
currentActiveSnapshot = newNode;
|
|
return newNode;
|
|
}
|
|
function popProvider() {
|
|
var prevSnapshot = currentActiveSnapshot;
|
|
|
|
if (prevSnapshot === null) {
|
|
throw new Error('Tried to pop a Context at the root of the app. This is a bug in React.');
|
|
}
|
|
|
|
{
|
|
var value = prevSnapshot.parentValue;
|
|
|
|
if (value === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) {
|
|
prevSnapshot.context._currentValue = prevSnapshot.context._defaultValue;
|
|
} else {
|
|
prevSnapshot.context._currentValue = value;
|
|
}
|
|
}
|
|
|
|
return currentActiveSnapshot = prevSnapshot.parent;
|
|
}
|
|
function getActiveContext() {
|
|
return currentActiveSnapshot;
|
|
}
|
|
function readContext$1(context) {
|
|
var value = context._currentValue ;
|
|
return value;
|
|
}
|
|
|
|
// 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.
|
|
var 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) {
|
|
var 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':
|
|
{
|
|
var fulfilledValue = thenable.value;
|
|
return fulfilledValue;
|
|
}
|
|
|
|
case 'rejected':
|
|
{
|
|
var rejectedError = thenable.reason;
|
|
throw rejectedError;
|
|
}
|
|
|
|
default:
|
|
{
|
|
if (typeof thenable.status === 'string') ; else {
|
|
var pendingThenable = thenable;
|
|
pendingThenable.status = 'pending';
|
|
pendingThenable.then(function (fulfilledValue) {
|
|
if (thenable.status === 'pending') {
|
|
var fulfilledThenable = thenable;
|
|
fulfilledThenable.status = 'fulfilled';
|
|
fulfilledThenable.value = fulfilledValue;
|
|
}
|
|
}, function (error) {
|
|
if (thenable.status === 'pending') {
|
|
var rejectedThenable = thenable;
|
|
rejectedThenable.status = 'rejected';
|
|
rejectedThenable.reason = error;
|
|
}
|
|
}); // Check one more time in case the thenable resolved synchronously
|
|
|
|
switch (thenable.status) {
|
|
case 'fulfilled':
|
|
{
|
|
var fulfilledThenable = thenable;
|
|
return fulfilledThenable.value;
|
|
}
|
|
|
|
case 'rejected':
|
|
{
|
|
var 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.
|
|
|
|
var 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.');
|
|
}
|
|
|
|
var thenable = suspendedThenable;
|
|
suspendedThenable = null;
|
|
return thenable;
|
|
}
|
|
|
|
var currentRequest$1 = null;
|
|
var thenableIndexCounter = 0;
|
|
var thenableState = null;
|
|
function prepareToUseHooksForRequest(request) {
|
|
currentRequest$1 = request;
|
|
}
|
|
function resetHooksForRequest() {
|
|
currentRequest$1 = null;
|
|
}
|
|
function prepareToUseHooksForComponent(prevThenableState) {
|
|
thenableIndexCounter = 0;
|
|
thenableState = prevThenableState;
|
|
}
|
|
function getThenableStateAfterSuspending() {
|
|
var state = thenableState;
|
|
thenableState = null;
|
|
return state;
|
|
}
|
|
|
|
function readContext(context) {
|
|
{
|
|
if (context.$$typeof !== REACT_SERVER_CONTEXT_TYPE) {
|
|
if (isClientReference(context)) {
|
|
error('Cannot read a Client Context from a Server Component.');
|
|
} else {
|
|
error('Only createServerContext is supported in Server Components.');
|
|
}
|
|
}
|
|
|
|
if (currentRequest$1 === null) {
|
|
error('Context can only be read while React is rendering. ' + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + 'In function components, you can read it directly in the function body, but not ' + 'inside Hooks like useReducer() or useMemo().');
|
|
}
|
|
}
|
|
|
|
return readContext$1(context);
|
|
}
|
|
|
|
var HooksDispatcher = {
|
|
useMemo: function (nextCreate) {
|
|
return nextCreate();
|
|
},
|
|
useCallback: function (callback) {
|
|
return callback;
|
|
},
|
|
useDebugValue: function () {},
|
|
useDeferredValue: unsupportedHook,
|
|
useTransition: unsupportedHook,
|
|
readContext: readContext,
|
|
useContext: readContext,
|
|
useReducer: unsupportedHook,
|
|
useRef: unsupportedHook,
|
|
useState: unsupportedHook,
|
|
useInsertionEffect: unsupportedHook,
|
|
useLayoutEffect: unsupportedHook,
|
|
useImperativeHandle: unsupportedHook,
|
|
useEffect: unsupportedHook,
|
|
useId: useId,
|
|
useSyncExternalStore: unsupportedHook,
|
|
useCacheRefresh: function () {
|
|
return unsupportedRefresh;
|
|
},
|
|
useMemoCache: function (size) {
|
|
var data = new Array(size);
|
|
|
|
for (var i = 0; i < size; i++) {
|
|
data[i] = REACT_MEMO_CACHE_SENTINEL;
|
|
}
|
|
|
|
return data;
|
|
},
|
|
use: 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 useId() {
|
|
if (currentRequest$1 === null) {
|
|
throw new Error('useId can only be used while React is rendering');
|
|
}
|
|
|
|
var 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.
|
|
var thenable = usable; // Track the position of the thenable within this fiber.
|
|
|
|
var index = thenableIndexCounter;
|
|
thenableIndexCounter += 1;
|
|
|
|
if (thenableState === null) {
|
|
thenableState = createThenableState();
|
|
}
|
|
|
|
return trackUsedThenable(thenableState, thenable, index);
|
|
} else if (usable.$$typeof === REACT_SERVER_CONTEXT_TYPE) {
|
|
var context = usable;
|
|
return readContext(context);
|
|
}
|
|
}
|
|
|
|
{
|
|
if (isClientReference(usable)) {
|
|
error('Cannot use() an already resolved Client Reference.');
|
|
}
|
|
} // eslint-disable-next-line react-internal/safe-string-coercion
|
|
|
|
|
|
throw new Error('An unsupported type was passed to use(): ' + String(usable));
|
|
}
|
|
|
|
function createSignal() {
|
|
return new AbortController().signal;
|
|
}
|
|
|
|
function resolveCache() {
|
|
var request = resolveRequest();
|
|
|
|
if (request) {
|
|
return getCache(request);
|
|
}
|
|
|
|
return new Map();
|
|
}
|
|
|
|
var DefaultCacheDispatcher = {
|
|
getCacheSignal: function () {
|
|
var cache = resolveCache();
|
|
var entry = cache.get(createSignal);
|
|
|
|
if (entry === undefined) {
|
|
entry = createSignal();
|
|
cache.set(createSignal, entry);
|
|
}
|
|
|
|
return entry;
|
|
},
|
|
getCacheForType: function (resourceType) {
|
|
var cache = resolveCache();
|
|
var entry = cache.get(resourceType);
|
|
|
|
if (entry === undefined) {
|
|
entry = resourceType(); // TODO: Warn if undefined?
|
|
|
|
cache.set(resourceType, entry);
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
};
|
|
|
|
var isArrayImpl = Array.isArray; // eslint-disable-next-line no-redeclare
|
|
|
|
function isArray(a) {
|
|
return isArrayImpl(a);
|
|
}
|
|
|
|
// in case they error.
|
|
|
|
var jsxPropsParents = new WeakMap();
|
|
var jsxChildrenParents = new WeakMap();
|
|
|
|
function isObjectPrototype(object) {
|
|
if (!object) {
|
|
return false;
|
|
}
|
|
|
|
var ObjectPrototype = Object.prototype;
|
|
|
|
if (object === ObjectPrototype) {
|
|
return true;
|
|
} // It might be an object from a different Realm which is
|
|
// still just a plain simple object.
|
|
|
|
|
|
if (Object.getPrototypeOf(object)) {
|
|
return false;
|
|
}
|
|
|
|
var names = Object.getOwnPropertyNames(object);
|
|
|
|
for (var i = 0; i < names.length; i++) {
|
|
if (!(names[i] in ObjectPrototype)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function isSimpleObject(object) {
|
|
if (!isObjectPrototype(Object.getPrototypeOf(object))) {
|
|
return false;
|
|
}
|
|
|
|
var names = Object.getOwnPropertyNames(object);
|
|
|
|
for (var i = 0; i < names.length; i++) {
|
|
var descriptor = Object.getOwnPropertyDescriptor(object, names[i]);
|
|
|
|
if (!descriptor) {
|
|
return false;
|
|
}
|
|
|
|
if (!descriptor.enumerable) {
|
|
if ((names[i] === 'key' || names[i] === 'ref') && typeof descriptor.get === 'function') {
|
|
// React adds key and ref getters to props objects to issue warnings.
|
|
// Those getters will not be transferred to the client, but that's ok,
|
|
// so we'll special case them.
|
|
continue;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
function objectName(object) {
|
|
// $FlowFixMe[method-unbinding]
|
|
var name = Object.prototype.toString.call(object);
|
|
return name.replace(/^\[object (.*)\]$/, function (m, p0) {
|
|
return p0;
|
|
});
|
|
}
|
|
|
|
function describeKeyForErrorMessage(key) {
|
|
var 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 '[...]';
|
|
}
|
|
|
|
var 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:
|
|
{
|
|
var lazyComponent = type;
|
|
var payload = lazyComponent._payload;
|
|
var 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) {
|
|
var objKind = objectName(objectOrArray);
|
|
|
|
if (objKind !== 'Object' && objKind !== 'Array') {
|
|
return objKind;
|
|
}
|
|
|
|
var str = '';
|
|
var start = -1;
|
|
var length = 0;
|
|
|
|
if (isArray(objectOrArray)) {
|
|
if (jsxChildrenParents.has(objectOrArray)) {
|
|
// Print JSX Children
|
|
var type = jsxChildrenParents.get(objectOrArray);
|
|
str = '<' + describeElementType(type) + '>';
|
|
var array = objectOrArray;
|
|
|
|
for (var i = 0; i < array.length; i++) {
|
|
var value = array[i];
|
|
var substr = void 0;
|
|
|
|
if (typeof value === 'string') {
|
|
substr = value;
|
|
} else 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 < 15 && str.length + substr.length < 40) {
|
|
str += substr;
|
|
} else {
|
|
str += '{...}';
|
|
}
|
|
}
|
|
|
|
str += '</' + describeElementType(type) + '>';
|
|
} else {
|
|
// Print Array
|
|
str = '[';
|
|
var _array = objectOrArray;
|
|
|
|
for (var _i = 0; _i < _array.length; _i++) {
|
|
if (_i > 0) {
|
|
str += ', ';
|
|
}
|
|
|
|
var _value = _array[_i];
|
|
|
|
var _substr = void 0;
|
|
|
|
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 if (jsxPropsParents.has(objectOrArray)) {
|
|
// Print JSX
|
|
var _type = jsxPropsParents.get(objectOrArray);
|
|
|
|
str = '<' + (describeElementType(_type) || '...');
|
|
var object = objectOrArray;
|
|
var names = Object.keys(object);
|
|
|
|
for (var _i2 = 0; _i2 < names.length; _i2++) {
|
|
str += ' ';
|
|
var name = names[_i2];
|
|
str += describeKeyForErrorMessage(name) + '=';
|
|
var _value2 = object[name];
|
|
|
|
var _substr2 = void 0;
|
|
|
|
if (name === expandedName && typeof _value2 === 'object' && _value2 !== null) {
|
|
_substr2 = describeObjectForErrorMessage(_value2);
|
|
} else {
|
|
_substr2 = describeValueForErrorMessage(_value2);
|
|
}
|
|
|
|
if (typeof _value2 !== 'string') {
|
|
_substr2 = '{' + _substr2 + '}';
|
|
}
|
|
|
|
if (name === expandedName) {
|
|
start = str.length;
|
|
length = _substr2.length;
|
|
str += _substr2;
|
|
} else if (_substr2.length < 10 && str.length + _substr2.length < 40) {
|
|
str += _substr2;
|
|
} else {
|
|
str += '...';
|
|
}
|
|
}
|
|
|
|
str += '>';
|
|
} else {
|
|
// Print Object
|
|
str = '{';
|
|
var _object = objectOrArray;
|
|
|
|
var _names = Object.keys(_object);
|
|
|
|
for (var _i3 = 0; _i3 < _names.length; _i3++) {
|
|
if (_i3 > 0) {
|
|
str += ', ';
|
|
}
|
|
|
|
var _name = _names[_i3];
|
|
str += describeKeyForErrorMessage(_name) + ': ';
|
|
var _value3 = _object[_name];
|
|
|
|
var _substr3 = void 0;
|
|
|
|
if (typeof _value3 === 'object' && _value3 !== null) {
|
|
_substr3 = describeObjectForErrorMessage(_value3);
|
|
} else {
|
|
_substr3 = describeValueForErrorMessage(_value3);
|
|
}
|
|
|
|
if (_name === expandedName) {
|
|
start = str.length;
|
|
length = _substr3.length;
|
|
str += _substr3;
|
|
} else if (_substr3.length < 10 && str.length + _substr3.length < 40) {
|
|
str += _substr3;
|
|
} else {
|
|
str += '...';
|
|
}
|
|
}
|
|
|
|
str += '}';
|
|
}
|
|
}
|
|
|
|
if (expandedName === undefined) {
|
|
return str;
|
|
}
|
|
|
|
if (start > -1 && length > 0) {
|
|
var highlight = ' '.repeat(start) + '^'.repeat(length);
|
|
return '\n ' + str + '\n ' + highlight;
|
|
}
|
|
|
|
return '\n ' + str;
|
|
}
|
|
|
|
var ContextRegistry = ReactSharedInternals.ContextRegistry;
|
|
function getOrCreateServerContext(globalName) {
|
|
if (!ContextRegistry[globalName]) {
|
|
var context = {
|
|
$$typeof: REACT_SERVER_CONTEXT_TYPE,
|
|
// As a workaround to support multiple concurrent renderers, we categorize
|
|
// some renderers as primary and others as secondary. We only expect
|
|
// there to be two concurrent renderers at most: React Native (primary) and
|
|
// Fabric (secondary); React DOM (primary) and React ART (secondary).
|
|
// Secondary renderers store their context values on separate fields.
|
|
_currentValue: REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED,
|
|
_currentValue2: REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED,
|
|
_defaultValue: REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED,
|
|
// Used to track how many concurrent renderers this context currently
|
|
// supports within in a single renderer. Such as parallel server rendering.
|
|
_threadCount: 0,
|
|
// These are circular
|
|
Provider: null,
|
|
Consumer: null,
|
|
_globalName: globalName
|
|
};
|
|
context.Provider = {
|
|
$$typeof: REACT_PROVIDER_TYPE,
|
|
_context: context
|
|
};
|
|
|
|
{
|
|
var hasWarnedAboutUsingConsumer;
|
|
context._currentRenderer = null;
|
|
context._currentRenderer2 = null;
|
|
Object.defineProperties(context, {
|
|
Consumer: {
|
|
get: function () {
|
|
if (!hasWarnedAboutUsingConsumer) {
|
|
error('Consumer pattern is not supported by ReactServerContext');
|
|
|
|
hasWarnedAboutUsingConsumer = true;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
ContextRegistry[globalName] = context;
|
|
}
|
|
|
|
return ContextRegistry[globalName];
|
|
}
|
|
|
|
var stringify = JSON.stringify; // Serializable values
|
|
// Thenable<ReactClientValue>
|
|
|
|
var PENDING$1 = 0;
|
|
var COMPLETED = 1;
|
|
var ABORTED = 3;
|
|
var ERRORED$1 = 4;
|
|
var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
|
|
var ReactCurrentCache = ReactSharedInternals.ReactCurrentCache;
|
|
|
|
function defaultErrorHandler(error) {
|
|
console['error'](error); // Don't transform to our wrapper
|
|
}
|
|
|
|
function defaultPostponeHandler(reason) {// Noop
|
|
}
|
|
|
|
var OPEN = 0;
|
|
var CLOSING = 1;
|
|
var CLOSED = 2;
|
|
function createRequest(model, bundlerConfig, onError, context, identifierPrefix, onPostpone) {
|
|
if (ReactCurrentCache.current !== null && ReactCurrentCache.current !== DefaultCacheDispatcher) {
|
|
throw new Error('Currently React only supports one RSC renderer at a time.');
|
|
}
|
|
|
|
prepareHostDispatcher();
|
|
ReactCurrentCache.current = DefaultCacheDispatcher;
|
|
var abortSet = new Set();
|
|
var pingedTasks = [];
|
|
var hints = createHints();
|
|
var request = {
|
|
status: OPEN,
|
|
flushScheduled: false,
|
|
fatalError: null,
|
|
destination: null,
|
|
bundlerConfig: bundlerConfig,
|
|
cache: new Map(),
|
|
nextChunkId: 0,
|
|
pendingChunks: 0,
|
|
hints: hints,
|
|
abortableTasks: abortSet,
|
|
pingedTasks: pingedTasks,
|
|
completedImportChunks: [],
|
|
completedHintChunks: [],
|
|
completedRegularChunks: [],
|
|
completedErrorChunks: [],
|
|
writtenSymbols: new Map(),
|
|
writtenClientReferences: new Map(),
|
|
writtenServerReferences: new Map(),
|
|
writtenProviders: new Map(),
|
|
identifierPrefix: identifierPrefix || '',
|
|
identifierCount: 1,
|
|
onError: onError === undefined ? defaultErrorHandler : onError,
|
|
onPostpone: onPostpone === undefined ? defaultPostponeHandler : onPostpone,
|
|
// $FlowFixMe[missing-this-annot]
|
|
toJSON: function (key, value) {
|
|
return resolveModelToJSON(request, this, key, value);
|
|
}
|
|
};
|
|
request.pendingChunks++;
|
|
var rootContext = createRootContext(context);
|
|
var rootTask = createTask(request, model, rootContext, abortSet);
|
|
pingedTasks.push(rootTask);
|
|
return request;
|
|
}
|
|
var currentRequest = null;
|
|
function resolveRequest() {
|
|
if (currentRequest) return currentRequest;
|
|
|
|
if (supportsRequestStorage) {
|
|
var store = requestStorage.getStore();
|
|
if (store) return store;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function createRootContext(reqContext) {
|
|
return importServerContexts(reqContext);
|
|
}
|
|
|
|
var POP = {};
|
|
|
|
function serializeThenable(request, thenable) {
|
|
request.pendingChunks++;
|
|
var newTask = createTask(request, null, getActiveContext(), 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':
|
|
{
|
|
var x = thenable.reason;
|
|
|
|
if (typeof x === 'object' && x !== null && x.$$typeof === REACT_POSTPONE_TYPE) {
|
|
var postponeInstance = x;
|
|
logPostpone(request, postponeInstance.message);
|
|
emitPostponeChunk(request, newTask.id, postponeInstance);
|
|
} else {
|
|
var digest = logRecoverableError(request, x);
|
|
emitErrorChunk(request, newTask.id, digest, x);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
var pendingThenable = thenable;
|
|
pendingThenable.status = 'pending';
|
|
pendingThenable.then(function (fulfilledValue) {
|
|
if (thenable.status === 'pending') {
|
|
var fulfilledThenable = thenable;
|
|
fulfilledThenable.status = 'fulfilled';
|
|
fulfilledThenable.value = fulfilledValue;
|
|
}
|
|
}, function (error) {
|
|
if (thenable.status === 'pending') {
|
|
var rejectedThenable = thenable;
|
|
rejectedThenable.status = 'rejected';
|
|
rejectedThenable.reason = error;
|
|
}
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
|
|
thenable.then(function (value) {
|
|
newTask.model = value;
|
|
pingTask(request, newTask);
|
|
}, function (reason) {
|
|
newTask.status = ERRORED$1;
|
|
request.abortableTasks.delete(newTask); // TODO: We should ideally do this inside performWork so it's scheduled
|
|
|
|
var digest = logRecoverableError(request, reason);
|
|
emitErrorChunk(request, newTask.id, digest, reason);
|
|
|
|
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.
|
|
var 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;
|
|
}
|
|
|
|
var pendingThenable = thenable;
|
|
pendingThenable.status = 'pending';
|
|
pendingThenable.then(function (fulfilledValue) {
|
|
if (thenable.status === 'pending') {
|
|
var fulfilledThenable = thenable;
|
|
fulfilledThenable.status = 'fulfilled';
|
|
fulfilledThenable.value = fulfilledValue;
|
|
}
|
|
}, function (error) {
|
|
if (thenable.status === 'pending') {
|
|
var rejectedThenable = thenable;
|
|
rejectedThenable.status = 'rejected';
|
|
rejectedThenable.reason = error;
|
|
}
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
|
|
var lazyType = {
|
|
$$typeof: REACT_LAZY_TYPE,
|
|
_payload: thenable,
|
|
_init: readThenable
|
|
};
|
|
return lazyType;
|
|
}
|
|
|
|
function attemptResolveElement(request, type, key, ref, props, prevThenableState) {
|
|
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.');
|
|
}
|
|
|
|
{
|
|
jsxPropsParents.set(props, type);
|
|
|
|
if (typeof props.children === 'object' && props.children !== null) {
|
|
jsxChildrenParents.set(props.children, type);
|
|
}
|
|
}
|
|
|
|
if (typeof type === 'function') {
|
|
if (isClientReference(type)) {
|
|
// This is a reference to a Client Component.
|
|
return [REACT_ELEMENT_TYPE, type, key, props];
|
|
} // This is a server-side component.
|
|
|
|
|
|
prepareToUseHooksForComponent(prevThenableState);
|
|
var result = type(props);
|
|
|
|
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.
|
|
var 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.
|
|
|
|
|
|
return createLazyWrapperAroundWakeable(result);
|
|
}
|
|
|
|
return result;
|
|
} else if (typeof type === 'string') {
|
|
// This is a host element. E.g. HTML.
|
|
return [REACT_ELEMENT_TYPE, type, key, props];
|
|
} else if (typeof type === 'symbol') {
|
|
if (type === REACT_FRAGMENT_TYPE) {
|
|
// For key-less fragments, we add a small optimization to avoid serializing
|
|
// it as a wrapper.
|
|
// TODO: If a key is specified, we should propagate its key to any children.
|
|
// Same as if a Server Component has a key.
|
|
return props.children;
|
|
} // 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 [REACT_ELEMENT_TYPE, type, key, props];
|
|
} else if (type != null && typeof type === 'object') {
|
|
if (isClientReference(type)) {
|
|
// This is a reference to a Client Component.
|
|
return [REACT_ELEMENT_TYPE, type, key, props];
|
|
}
|
|
|
|
switch (type.$$typeof) {
|
|
case REACT_LAZY_TYPE:
|
|
{
|
|
var payload = type._payload;
|
|
var init = type._init;
|
|
var wrappedType = init(payload);
|
|
return attemptResolveElement(request, wrappedType, key, ref, props, prevThenableState);
|
|
}
|
|
|
|
case REACT_FORWARD_REF_TYPE:
|
|
{
|
|
var render = type.render;
|
|
prepareToUseHooksForComponent(prevThenableState);
|
|
return render(props, undefined);
|
|
}
|
|
|
|
case REACT_MEMO_TYPE:
|
|
{
|
|
return attemptResolveElement(request, type.type, key, ref, props, prevThenableState);
|
|
}
|
|
|
|
case REACT_PROVIDER_TYPE:
|
|
{
|
|
pushProvider(type._context, props.value);
|
|
|
|
{
|
|
var extraKeys = Object.keys(props).filter(function (value) {
|
|
if (value === 'children' || value === 'value') {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
if (extraKeys.length !== 0) {
|
|
error('ServerContext can only have a value prop and children. Found: %s', JSON.stringify(extraKeys));
|
|
}
|
|
}
|
|
|
|
return [REACT_ELEMENT_TYPE, type, key, // Rely on __popProvider being serialized last to pop the provider.
|
|
{
|
|
value: props.value,
|
|
children: props.children,
|
|
__pop: POP
|
|
}];
|
|
}
|
|
}
|
|
}
|
|
|
|
throw new Error("Unsupported Server Component type: " + describeValueForErrorMessage(type));
|
|
}
|
|
|
|
function pingTask(request, task) {
|
|
var pingedTasks = request.pingedTasks;
|
|
pingedTasks.push(task);
|
|
|
|
if (pingedTasks.length === 1) {
|
|
request.flushScheduled = request.destination !== null;
|
|
scheduleWork(function () {
|
|
return performWork(request);
|
|
});
|
|
}
|
|
}
|
|
|
|
function createTask(request, model, context, abortSet) {
|
|
var id = request.nextChunkId++;
|
|
var task = {
|
|
id: id,
|
|
status: PENDING$1,
|
|
model: model,
|
|
context: context,
|
|
ping: function () {
|
|
return pingTask(request, task);
|
|
},
|
|
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 serializeProviderReference(name) {
|
|
return '$P' + 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) {
|
|
var json = stringify(reference);
|
|
var row = id.toString(16) + ':' + json + '\n';
|
|
return stringToChunk(row);
|
|
}
|
|
|
|
function serializeClientReference(request, parent, key, clientReference) {
|
|
var clientReferenceKey = getClientReferenceKey(clientReference);
|
|
var writtenClientReferences = request.writtenClientReferences;
|
|
var existingId = writtenClientReferences.get(clientReferenceKey);
|
|
|
|
if (existingId !== undefined) {
|
|
if (parent[0] === REACT_ELEMENT_TYPE && key === '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 {
|
|
var clientReferenceMetadata = resolveClientReferenceMetadata(request.bundlerConfig, clientReference);
|
|
request.pendingChunks++;
|
|
var importId = request.nextChunkId++;
|
|
emitImportChunk(request, importId, clientReferenceMetadata);
|
|
writtenClientReferences.set(clientReferenceKey, importId);
|
|
|
|
if (parent[0] === REACT_ELEMENT_TYPE && key === '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++;
|
|
var errorId = request.nextChunkId++;
|
|
var digest = logRecoverableError(request, x);
|
|
emitErrorChunk(request, errorId, digest, x);
|
|
return serializeByValueID(errorId);
|
|
}
|
|
}
|
|
|
|
function outlineModel(request, value) {
|
|
request.pendingChunks++;
|
|
var outlinedId = request.nextChunkId++; // We assume that this object doesn't suspend, but a child might.
|
|
|
|
emitModelChunk(request, outlinedId, value);
|
|
return outlinedId;
|
|
}
|
|
|
|
function serializeServerReference(request, parent, key, serverReference) {
|
|
var writtenServerReferences = request.writtenServerReferences;
|
|
var existingId = writtenServerReferences.get(serverReference);
|
|
|
|
if (existingId !== undefined) {
|
|
return serializeServerReferenceID(existingId);
|
|
}
|
|
|
|
var bound = getServerReferenceBoundArguments(request.bundlerConfig, serverReference);
|
|
var serverReferenceMetadata = {
|
|
id: getServerReferenceId(request.bundlerConfig, serverReference),
|
|
bound: bound ? Promise.resolve(bound) : null
|
|
};
|
|
var metadataId = outlineModel(request, serverReferenceMetadata);
|
|
writtenServerReferences.set(serverReference, metadataId);
|
|
return serializeServerReferenceID(metadataId);
|
|
}
|
|
|
|
function serializeLargeTextString(request, text) {
|
|
request.pendingChunks += 2;
|
|
var textId = request.nextChunkId++;
|
|
var textChunk = stringToChunk(text);
|
|
var binaryLength = byteLengthOfChunk(textChunk);
|
|
var row = textId.toString(16) + ':T' + binaryLength.toString(16) + ',';
|
|
var headerChunk = stringToChunk(row);
|
|
request.completedRegularChunks.push(headerChunk, textChunk);
|
|
return serializeByValueID(textId);
|
|
}
|
|
|
|
function serializeMap(request, map) {
|
|
var id = outlineModel(request, Array.from(map));
|
|
return '$Q' + id.toString(16);
|
|
}
|
|
|
|
function serializeSet(request, set) {
|
|
var id = outlineModel(request, Array.from(set));
|
|
return '$W' + id.toString(16);
|
|
}
|
|
|
|
function serializeTypedArray(request, tag, typedArray) {
|
|
request.pendingChunks += 2;
|
|
var bufferId = request.nextChunkId++; // TODO: Convert to little endian if that's not the server default.
|
|
|
|
var binaryChunk = typedArrayToBinaryChunk(typedArray);
|
|
var binaryLength = byteLengthOfBinaryChunk(binaryChunk);
|
|
var row = bufferId.toString(16) + ':' + tag + binaryLength.toString(16) + ',';
|
|
var headerChunk = stringToChunk(row);
|
|
request.completedRegularChunks.push(headerChunk, binaryChunk);
|
|
return serializeByValueID(bufferId);
|
|
}
|
|
|
|
function escapeStringValue(value) {
|
|
if (value[0] === '$') {
|
|
// We need to escape $ prefixed strings since we use those to encode
|
|
// references to IDs and as special symbol values.
|
|
return '$' + value;
|
|
} else {
|
|
return value;
|
|
}
|
|
}
|
|
|
|
var insideContextProps = null;
|
|
var isInsideContextValue = false;
|
|
|
|
function resolveModelToJSON(request, parent, key, value) {
|
|
// Make sure that `parent[key]` wasn't JSONified before `value` was passed to us
|
|
{
|
|
// $FlowFixMe[incompatible-use]
|
|
var originalValue = parent[key];
|
|
|
|
if (typeof originalValue === 'object' && originalValue !== value && !(originalValue instanceof Date)) {
|
|
if (objectName(originalValue) !== 'Object') {
|
|
var jsxParentType = jsxChildrenParents.get(parent);
|
|
|
|
if (typeof jsxParentType === 'string') {
|
|
error('%s objects cannot be rendered as text children. Try formatting it using toString().%s', objectName(originalValue), describeObjectForErrorMessage(parent, key));
|
|
} else {
|
|
error('Only plain objects can be passed to Client Components from Server Components. ' + '%s objects are not supported.%s', objectName(originalValue), describeObjectForErrorMessage(parent, key));
|
|
}
|
|
} else {
|
|
error('Only plain objects can be passed to Client Components from Server Components. ' + 'Objects with toJSON methods are not supported. Convert it manually ' + 'to a simple value before passing it to props.%s', describeObjectForErrorMessage(parent, key));
|
|
}
|
|
}
|
|
} // Special Symbols
|
|
|
|
|
|
switch (value) {
|
|
case REACT_ELEMENT_TYPE:
|
|
return '$';
|
|
}
|
|
|
|
{
|
|
if (parent[0] === REACT_ELEMENT_TYPE && parent[1] && parent[1].$$typeof === REACT_PROVIDER_TYPE && key === '3') {
|
|
insideContextProps = value;
|
|
} else if (insideContextProps === parent && key === 'value') {
|
|
isInsideContextValue = true;
|
|
} else if (insideContextProps === parent && key === 'children') {
|
|
isInsideContextValue = false;
|
|
}
|
|
} // Resolve Server Components.
|
|
|
|
|
|
while (typeof value === 'object' && value !== null && (value.$$typeof === REACT_ELEMENT_TYPE || value.$$typeof === REACT_LAZY_TYPE)) {
|
|
{
|
|
if (isInsideContextValue) {
|
|
error('React elements are not allowed in ServerContext');
|
|
}
|
|
}
|
|
|
|
try {
|
|
switch (value.$$typeof) {
|
|
case REACT_ELEMENT_TYPE:
|
|
{
|
|
// TODO: Concatenate keys of parents onto children.
|
|
var element = value; // Attempt to render the Server Component.
|
|
|
|
value = attemptResolveElement(request, element.type, element.key, element.ref, element.props, null);
|
|
break;
|
|
}
|
|
|
|
case REACT_LAZY_TYPE:
|
|
{
|
|
var payload = value._payload;
|
|
var init = value._init;
|
|
value = init(payload);
|
|
break;
|
|
}
|
|
}
|
|
} catch (thrownValue) {
|
|
var x = thrownValue === SuspenseException ? // This is a special type of exception used for Suspense. For historical
|
|
// reasons, the rest of the Suspense implementation expects the thrown
|
|
// value to be a thenable, because before `use` existed that was the
|
|
// (unstable) API for suspending. This implementation detail can change
|
|
// later, once we deprecate the old API in favor of `use`.
|
|
getSuspendedThenable() : thrownValue;
|
|
|
|
if (typeof x === 'object' && x !== null) {
|
|
// $FlowFixMe[method-unbinding]
|
|
if (typeof x.then === 'function') {
|
|
// Something suspended, we'll need to create a new task and resolve it later.
|
|
request.pendingChunks++;
|
|
var newTask = createTask(request, value, getActiveContext(), request.abortableTasks);
|
|
var ping = newTask.ping;
|
|
x.then(ping, ping);
|
|
newTask.thenableState = getThenableStateAfterSuspending();
|
|
return serializeLazyID(newTask.id);
|
|
} else if (x.$$typeof === REACT_POSTPONE_TYPE) {
|
|
// Something postponed. We'll still send everything we have up until this point.
|
|
// We'll replace this element with a lazy reference that postpones on the client.
|
|
var postponeInstance = x;
|
|
request.pendingChunks++;
|
|
var postponeId = request.nextChunkId++;
|
|
logPostpone(request, postponeInstance.message);
|
|
emitPostponeChunk(request, postponeId, postponeInstance);
|
|
return serializeLazyID(postponeId);
|
|
}
|
|
} // Something errored. We'll still send everything we have up until this point.
|
|
// We'll replace this element with a lazy reference that throws on the client
|
|
// once it gets rendered.
|
|
|
|
|
|
request.pendingChunks++;
|
|
var errorId = request.nextChunkId++;
|
|
var digest = logRecoverableError(request, x);
|
|
emitErrorChunk(request, errorId, digest, x);
|
|
return serializeLazyID(errorId);
|
|
}
|
|
}
|
|
|
|
if (value === null) {
|
|
return null;
|
|
}
|
|
|
|
if (typeof value === 'object') {
|
|
if (isClientReference(value)) {
|
|
return serializeClientReference(request, parent, key, value); // $FlowFixMe[method-unbinding]
|
|
} else if (typeof value.then === 'function') {
|
|
// We assume that any object with a .then property is a "Thenable" type,
|
|
// or a Promise type. Either of which can be represented by a Promise.
|
|
var promiseId = serializeThenable(request, value);
|
|
return serializePromiseID(promiseId);
|
|
} else if (value.$$typeof === REACT_PROVIDER_TYPE) {
|
|
var providerKey = value._context._globalName;
|
|
var writtenProviders = request.writtenProviders;
|
|
var providerId = writtenProviders.get(key);
|
|
|
|
if (providerId === undefined) {
|
|
request.pendingChunks++;
|
|
providerId = request.nextChunkId++;
|
|
writtenProviders.set(providerKey, providerId);
|
|
emitProviderChunk(request, providerId, providerKey);
|
|
}
|
|
|
|
return serializeByValueID(providerId);
|
|
} else if (value === POP) {
|
|
popProvider();
|
|
|
|
{
|
|
insideContextProps = null;
|
|
isInsideContextValue = false;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
if (value instanceof Map) {
|
|
return serializeMap(request, value);
|
|
}
|
|
|
|
if (value instanceof Set) {
|
|
return serializeSet(request, value);
|
|
}
|
|
|
|
{
|
|
if (value instanceof ArrayBuffer) {
|
|
return serializeTypedArray(request, 'A', new Uint8Array(value));
|
|
}
|
|
|
|
if (value instanceof Int8Array) {
|
|
// char
|
|
return serializeTypedArray(request, 'C', value);
|
|
}
|
|
|
|
if (value instanceof Uint8Array) {
|
|
// unsigned char
|
|
return serializeTypedArray(request, 'c', value);
|
|
}
|
|
|
|
if (value instanceof Uint8ClampedArray) {
|
|
// unsigned clamped char
|
|
return serializeTypedArray(request, 'U', value);
|
|
}
|
|
|
|
if (value instanceof Int16Array) {
|
|
// sort
|
|
return serializeTypedArray(request, 'S', value);
|
|
}
|
|
|
|
if (value instanceof Uint16Array) {
|
|
// unsigned short
|
|
return serializeTypedArray(request, 's', value);
|
|
}
|
|
|
|
if (value instanceof Int32Array) {
|
|
// long
|
|
return serializeTypedArray(request, 'L', value);
|
|
}
|
|
|
|
if (value instanceof Uint32Array) {
|
|
// unsigned long
|
|
return serializeTypedArray(request, 'l', value);
|
|
}
|
|
|
|
if (value instanceof Float32Array) {
|
|
// float
|
|
return serializeTypedArray(request, 'F', value);
|
|
}
|
|
|
|
if (value instanceof Float64Array) {
|
|
// double
|
|
return serializeTypedArray(request, 'D', value);
|
|
}
|
|
|
|
if (value instanceof BigInt64Array) {
|
|
// number
|
|
return serializeTypedArray(request, 'N', value);
|
|
}
|
|
|
|
if (value instanceof BigUint64Array) {
|
|
// unsigned number
|
|
// We use "m" instead of "n" since JSON can start with "null"
|
|
return serializeTypedArray(request, 'm', value);
|
|
}
|
|
|
|
if (value instanceof DataView) {
|
|
return serializeTypedArray(request, 'V', value);
|
|
}
|
|
}
|
|
|
|
if (!isArray(value)) {
|
|
var iteratorFn = getIteratorFn(value);
|
|
|
|
if (iteratorFn) {
|
|
return Array.from(value);
|
|
}
|
|
}
|
|
|
|
{
|
|
if (value !== null && !isArray(value)) {
|
|
// Verify that this is a simple plain object.
|
|
if (objectName(value) !== 'Object') {
|
|
error('Only plain objects can be passed to Client Components from Server Components. ' + '%s objects are not supported.%s', objectName(value), describeObjectForErrorMessage(parent, key));
|
|
} else if (!isSimpleObject(value)) {
|
|
error('Only plain objects can be passed to Client Components from Server Components. ' + 'Classes or other objects with methods are not supported.%s', describeObjectForErrorMessage(parent, key));
|
|
} else if (Object.getOwnPropertySymbols) {
|
|
var symbols = Object.getOwnPropertySymbols(value);
|
|
|
|
if (symbols.length > 0) {
|
|
error('Only plain objects can be passed to Client Components from Server Components. ' + 'Objects with symbol properties like %s are not supported.%s', symbols[0].description, describeObjectForErrorMessage(parent, key));
|
|
}
|
|
}
|
|
}
|
|
} // $FlowFixMe[incompatible-return]
|
|
|
|
|
|
return value;
|
|
}
|
|
|
|
if (typeof value === 'string') {
|
|
// TODO: Maybe too clever. If we support URL there's no similar trick.
|
|
if (value[value.length - 1] === 'Z') {
|
|
// Possibly a Date, whose toJSON automatically calls toISOString
|
|
// $FlowFixMe[incompatible-use]
|
|
var _originalValue = parent[key];
|
|
|
|
if (_originalValue instanceof Date) {
|
|
return serializeDateFromDateJSON(value);
|
|
}
|
|
}
|
|
|
|
if (value.length >= 1024) {
|
|
// For large strings, we encode them outside the JSON payload so that we
|
|
// don't have to double encode and double parse the strings. This can also
|
|
// be more compact in case the string has a lot of escaped characters.
|
|
return serializeLargeTextString(request, value);
|
|
}
|
|
|
|
return escapeStringValue(value);
|
|
}
|
|
|
|
if (typeof value === 'boolean') {
|
|
return value;
|
|
}
|
|
|
|
if (typeof value === 'number') {
|
|
return serializeNumber(value);
|
|
}
|
|
|
|
if (typeof value === 'undefined') {
|
|
return serializeUndefined();
|
|
}
|
|
|
|
if (typeof value === 'function') {
|
|
if (isClientReference(value)) {
|
|
return serializeClientReference(request, parent, key, value);
|
|
}
|
|
|
|
if (isServerReference(value)) {
|
|
return serializeServerReference(request, parent, key, value);
|
|
}
|
|
|
|
if (/^on[A-Z]/.test(key)) {
|
|
throw new Error('Event handlers cannot be passed to Client Component props.' + describeObjectForErrorMessage(parent, key) + '\nIf you need interactivity, consider converting part of this to a Client Component.');
|
|
} else {
|
|
throw new Error('Functions cannot be passed directly to Client Components ' + 'unless you explicitly expose it by marking it with "use server".' + describeObjectForErrorMessage(parent, key));
|
|
}
|
|
}
|
|
|
|
if (typeof value === 'symbol') {
|
|
var writtenSymbols = request.writtenSymbols;
|
|
var existingId = writtenSymbols.get(value);
|
|
|
|
if (existingId !== undefined) {
|
|
return serializeByValueID(existingId);
|
|
} // $FlowFixMe[incompatible-type] `description` might be undefined
|
|
|
|
|
|
var name = value.description;
|
|
|
|
if (Symbol.for(name) !== value) {
|
|
throw new Error('Only global symbols received from Symbol.for(...) can be passed to Client Components. ' + ("The symbol Symbol.for(" + // $FlowFixMe[incompatible-type] `description` might be undefined
|
|
value.description + ") cannot be found among global symbols.") + describeObjectForErrorMessage(parent, key));
|
|
}
|
|
|
|
request.pendingChunks++;
|
|
var symbolId = request.nextChunkId++;
|
|
emitSymbolChunk(request, symbolId, name);
|
|
writtenSymbols.set(value, symbolId);
|
|
return serializeByValueID(symbolId);
|
|
}
|
|
|
|
if (typeof value === 'bigint') {
|
|
return serializeBigInt(value);
|
|
}
|
|
|
|
throw new Error("Type " + typeof value + " is not supported in Client Component props." + describeObjectForErrorMessage(parent, key));
|
|
}
|
|
|
|
function logPostpone(request, reason) {
|
|
var onPostpone = request.onPostpone;
|
|
onPostpone(reason);
|
|
}
|
|
|
|
function logRecoverableError(request, error) {
|
|
var onError = request.onError;
|
|
var errorDigest = onError(error);
|
|
|
|
if (errorDigest != null && typeof errorDigest !== 'string') {
|
|
// eslint-disable-next-line react-internal/prod-error-codes
|
|
throw new Error("onError returned something with a type other than \"string\". onError should return a string and may return null or undefined but must not return anything else. It received something of type \"" + typeof errorDigest + "\" instead");
|
|
}
|
|
|
|
return errorDigest || '';
|
|
}
|
|
|
|
function fatalError(request, error) {
|
|
// This is called outside error handling code such as if an error happens in React internals.
|
|
if (request.destination !== null) {
|
|
request.status = CLOSED;
|
|
closeWithError(request.destination, error);
|
|
} else {
|
|
request.status = CLOSING;
|
|
request.fatalError = error;
|
|
}
|
|
}
|
|
|
|
function emitPostponeChunk(request, id, postponeInstance) {
|
|
var row;
|
|
|
|
{
|
|
var reason = '';
|
|
var stack = '';
|
|
|
|
try {
|
|
// eslint-disable-next-line react-internal/safe-string-coercion
|
|
reason = String(postponeInstance.message); // eslint-disable-next-line react-internal/safe-string-coercion
|
|
|
|
stack = String(postponeInstance.stack);
|
|
} catch (x) {}
|
|
|
|
row = serializeRowHeader('P', id) + stringify({
|
|
reason: reason,
|
|
stack: stack
|
|
}) + '\n';
|
|
}
|
|
|
|
var processedChunk = stringToChunk(row);
|
|
request.completedErrorChunks.push(processedChunk);
|
|
}
|
|
|
|
function emitErrorChunk(request, id, digest, error) {
|
|
var errorInfo;
|
|
|
|
{
|
|
var message;
|
|
var stack = '';
|
|
|
|
try {
|
|
if (error instanceof Error) {
|
|
// eslint-disable-next-line react-internal/safe-string-coercion
|
|
message = String(error.message); // eslint-disable-next-line react-internal/safe-string-coercion
|
|
|
|
stack = String(error.stack);
|
|
} else {
|
|
message = 'Error: ' + error;
|
|
}
|
|
} catch (x) {
|
|
message = 'An error occurred but serializing the error message failed.';
|
|
}
|
|
|
|
errorInfo = {
|
|
digest: digest,
|
|
message: message,
|
|
stack: stack
|
|
};
|
|
}
|
|
|
|
var row = serializeRowHeader('E', id) + stringify(errorInfo) + '\n';
|
|
var processedChunk = stringToChunk(row);
|
|
request.completedErrorChunks.push(processedChunk);
|
|
}
|
|
|
|
function emitImportChunk(request, id, clientReferenceMetadata) {
|
|
// $FlowFixMe[incompatible-type] stringify can return null
|
|
var json = stringify(clientReferenceMetadata);
|
|
var row = serializeRowHeader('I', id) + json + '\n';
|
|
var processedChunk = stringToChunk(row);
|
|
request.completedImportChunks.push(processedChunk);
|
|
}
|
|
|
|
function emitHintChunk(request, code, model) {
|
|
var json = stringify(model);
|
|
var id = request.nextChunkId++;
|
|
var row = serializeRowHeader('H' + code, id) + json + '\n';
|
|
var processedChunk = stringToChunk(row);
|
|
request.completedHintChunks.push(processedChunk);
|
|
}
|
|
|
|
function emitSymbolChunk(request, id, name) {
|
|
var symbolReference = serializeSymbolReference(name);
|
|
var processedChunk = encodeReferenceChunk(request, id, symbolReference);
|
|
request.completedImportChunks.push(processedChunk);
|
|
}
|
|
|
|
function emitProviderChunk(request, id, contextName) {
|
|
var contextReference = serializeProviderReference(contextName);
|
|
var processedChunk = encodeReferenceChunk(request, id, contextReference);
|
|
request.completedRegularChunks.push(processedChunk);
|
|
}
|
|
|
|
function emitModelChunk(request, id, model) {
|
|
// $FlowFixMe[incompatible-type] stringify can return null
|
|
var json = stringify(model, request.toJSON);
|
|
var row = id.toString(16) + ':' + json + '\n';
|
|
var processedChunk = stringToChunk(row);
|
|
request.completedRegularChunks.push(processedChunk);
|
|
}
|
|
|
|
function retryTask(request, task) {
|
|
if (task.status !== PENDING$1) {
|
|
// We completed this by other means before we had a chance to retry it.
|
|
return;
|
|
}
|
|
|
|
switchContext(task.context);
|
|
|
|
try {
|
|
var value = task.model;
|
|
|
|
if (typeof value === 'object' && value !== null && value.$$typeof === REACT_ELEMENT_TYPE) {
|
|
// TODO: Concatenate keys of parents onto children.
|
|
var element = value; // When retrying a component, reuse the thenableState from the
|
|
// previous attempt.
|
|
|
|
var prevThenableState = task.thenableState; // Attempt to render the Server Component.
|
|
// Doing this here lets us reuse this same task if the next component
|
|
// also suspends.
|
|
|
|
task.model = value;
|
|
value = attemptResolveElement(request, element.type, element.key, element.ref, element.props, prevThenableState); // Successfully finished this component. We're going to keep rendering
|
|
// using the same task, but we reset its thenable state before continuing.
|
|
|
|
task.thenableState = null; // Keep rendering and reuse the same task. This inner loop is separate
|
|
// from the render above because we don't need to reset the thenable state
|
|
// until the next time something suspends and retries.
|
|
|
|
while (typeof value === 'object' && value !== null && value.$$typeof === REACT_ELEMENT_TYPE) {
|
|
// TODO: Concatenate keys of parents onto children.
|
|
var nextElement = value;
|
|
task.model = value;
|
|
value = attemptResolveElement(request, nextElement.type, nextElement.key, nextElement.ref, nextElement.props, null);
|
|
}
|
|
}
|
|
|
|
emitModelChunk(request, task.id, value);
|
|
request.abortableTasks.delete(task);
|
|
task.status = COMPLETED;
|
|
} catch (thrownValue) {
|
|
var x = thrownValue === SuspenseException ? // This is a special type of exception used for Suspense. For historical
|
|
// reasons, the rest of the Suspense implementation expects the thrown
|
|
// value to be a thenable, because before `use` existed that was the
|
|
// (unstable) API for suspending. This implementation detail can change
|
|
// later, once we deprecate the old API in favor of `use`.
|
|
getSuspendedThenable() : thrownValue;
|
|
|
|
if (typeof x === 'object' && x !== null) {
|
|
// $FlowFixMe[method-unbinding]
|
|
if (typeof x.then === 'function') {
|
|
// Something suspended again, let's pick it back up later.
|
|
var ping = task.ping;
|
|
x.then(ping, ping);
|
|
task.thenableState = getThenableStateAfterSuspending();
|
|
return;
|
|
} else if (x.$$typeof === REACT_POSTPONE_TYPE) {
|
|
request.abortableTasks.delete(task);
|
|
task.status = ERRORED$1;
|
|
var postponeInstance = x;
|
|
logPostpone(request, postponeInstance.message);
|
|
emitPostponeChunk(request, task.id, postponeInstance);
|
|
return;
|
|
}
|
|
}
|
|
|
|
request.abortableTasks.delete(task);
|
|
task.status = ERRORED$1;
|
|
var digest = logRecoverableError(request, x);
|
|
emitErrorChunk(request, task.id, digest, x);
|
|
}
|
|
}
|
|
|
|
function performWork(request) {
|
|
var prevDispatcher = ReactCurrentDispatcher.current;
|
|
ReactCurrentDispatcher.current = HooksDispatcher;
|
|
var prevRequest = currentRequest;
|
|
currentRequest = request;
|
|
prepareToUseHooksForRequest(request);
|
|
|
|
try {
|
|
var pingedTasks = request.pingedTasks;
|
|
request.pingedTasks = [];
|
|
|
|
for (var i = 0; i < pingedTasks.length; i++) {
|
|
var task = pingedTasks[i];
|
|
retryTask(request, task);
|
|
}
|
|
|
|
if (request.destination !== null) {
|
|
flushCompletedChunks(request, request.destination);
|
|
}
|
|
} catch (error) {
|
|
logRecoverableError(request, error);
|
|
fatalError(request, error);
|
|
} finally {
|
|
ReactCurrentDispatcher.current = prevDispatcher;
|
|
resetHooksForRequest();
|
|
currentRequest = prevRequest;
|
|
}
|
|
}
|
|
|
|
function abortTask(task, request, errorId) {
|
|
task.status = ABORTED; // Instead of emitting an error per task.id, we emit a model that only
|
|
// has a single value referencing the error.
|
|
|
|
var ref = serializeByValueID(errorId);
|
|
var processedChunk = encodeReferenceChunk(request, task.id, ref);
|
|
request.completedErrorChunks.push(processedChunk);
|
|
}
|
|
|
|
function flushCompletedChunks(request, destination) {
|
|
beginWriting();
|
|
|
|
try {
|
|
// We emit module chunks first in the stream so that
|
|
// they can be preloaded as early as possible.
|
|
var importsChunks = request.completedImportChunks;
|
|
var i = 0;
|
|
|
|
for (; i < importsChunks.length; i++) {
|
|
request.pendingChunks--;
|
|
var chunk = importsChunks[i];
|
|
var keepWriting = writeChunkAndReturn(destination, chunk);
|
|
|
|
if (!keepWriting) {
|
|
request.destination = null;
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
importsChunks.splice(0, i); // Next comes hints.
|
|
|
|
var hintChunks = request.completedHintChunks;
|
|
i = 0;
|
|
|
|
for (; i < hintChunks.length; i++) {
|
|
var _chunk = hintChunks[i];
|
|
|
|
var _keepWriting = writeChunkAndReturn(destination, _chunk);
|
|
|
|
if (!_keepWriting) {
|
|
request.destination = null;
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
hintChunks.splice(0, i); // Next comes model data.
|
|
|
|
var regularChunks = request.completedRegularChunks;
|
|
i = 0;
|
|
|
|
for (; i < regularChunks.length; i++) {
|
|
request.pendingChunks--;
|
|
var _chunk2 = regularChunks[i];
|
|
|
|
var _keepWriting2 = writeChunkAndReturn(destination, _chunk2);
|
|
|
|
if (!_keepWriting2) {
|
|
request.destination = null;
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
regularChunks.splice(0, i); // Finally, errors are sent. The idea is that it's ok to delay
|
|
// any error messages and prioritize display of other parts of
|
|
// the page.
|
|
|
|
var errorChunks = request.completedErrorChunks;
|
|
i = 0;
|
|
|
|
for (; i < errorChunks.length; i++) {
|
|
request.pendingChunks--;
|
|
var _chunk3 = errorChunks[i];
|
|
|
|
var _keepWriting3 = writeChunkAndReturn(destination, _chunk3);
|
|
|
|
if (!_keepWriting3) {
|
|
request.destination = null;
|
|
i++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
errorChunks.splice(0, i);
|
|
} finally {
|
|
request.flushScheduled = false;
|
|
completeWriting(destination);
|
|
}
|
|
|
|
if (request.pendingChunks === 0) {
|
|
// We're done.
|
|
close$1(destination);
|
|
}
|
|
}
|
|
|
|
function startWork(request) {
|
|
request.flushScheduled = request.destination !== null;
|
|
|
|
if (supportsRequestStorage) {
|
|
scheduleWork(function () {
|
|
return requestStorage.run(request, performWork, request);
|
|
});
|
|
} else {
|
|
scheduleWork(function () {
|
|
return performWork(request);
|
|
});
|
|
}
|
|
}
|
|
|
|
function enqueueFlush(request) {
|
|
if (request.flushScheduled === false && // If there are pinged tasks we are going to flush anyway after work completes
|
|
request.pingedTasks.length === 0 && // If there is no destination there is nothing we can flush to. A flush will
|
|
// happen when we start flowing again
|
|
request.destination !== null) {
|
|
var destination = request.destination;
|
|
request.flushScheduled = true;
|
|
scheduleWork(function () {
|
|
return flushCompletedChunks(request, destination);
|
|
});
|
|
}
|
|
}
|
|
|
|
function startFlowing(request, destination) {
|
|
if (request.status === CLOSING) {
|
|
request.status = CLOSED;
|
|
closeWithError(destination, request.fatalError);
|
|
return;
|
|
}
|
|
|
|
if (request.status === CLOSED) {
|
|
return;
|
|
}
|
|
|
|
if (request.destination !== null) {
|
|
// We're already flowing.
|
|
return;
|
|
}
|
|
|
|
request.destination = destination;
|
|
|
|
try {
|
|
flushCompletedChunks(request, destination);
|
|
} catch (error) {
|
|
logRecoverableError(request, error);
|
|
fatalError(request, error);
|
|
}
|
|
}
|
|
function stopFlowing(request) {
|
|
request.destination = null;
|
|
} // This is called to early terminate a request. It creates an error at all pending tasks.
|
|
|
|
function abort(request, reason) {
|
|
try {
|
|
var abortableTasks = request.abortableTasks;
|
|
|
|
if (abortableTasks.size > 0) {
|
|
// We have tasks to abort. We'll emit one error row and then emit a reference
|
|
// to that row from every row that's still remaining.
|
|
var error = reason === undefined ? new Error('The render was aborted by the server without a reason.') : reason;
|
|
var digest = logRecoverableError(request, error);
|
|
request.pendingChunks++;
|
|
var errorId = request.nextChunkId++;
|
|
emitErrorChunk(request, errorId, digest, error);
|
|
abortableTasks.forEach(function (task) {
|
|
return abortTask(task, request, errorId);
|
|
});
|
|
abortableTasks.clear();
|
|
}
|
|
|
|
if (request.destination !== null) {
|
|
flushCompletedChunks(request, request.destination);
|
|
}
|
|
} catch (error) {
|
|
logRecoverableError(request, error);
|
|
fatalError(request, error);
|
|
}
|
|
}
|
|
|
|
function importServerContexts(contexts) {
|
|
if (contexts) {
|
|
var prevContext = getActiveContext();
|
|
switchContext(rootContextSnapshot);
|
|
|
|
for (var i = 0; i < contexts.length; i++) {
|
|
var _contexts$i = contexts[i],
|
|
name = _contexts$i[0],
|
|
value = _contexts$i[1];
|
|
var context = getOrCreateServerContext(name);
|
|
pushProvider(context, value);
|
|
}
|
|
|
|
var importedContext = getActiveContext();
|
|
switchContext(prevContext);
|
|
return importedContext;
|
|
}
|
|
|
|
return rootContextSnapshot;
|
|
}
|
|
|
|
// This is the parsed shape of the wire format which is why it is
|
|
// condensed to only the essentialy information
|
|
var ID = 0;
|
|
var CHUNKS = 1;
|
|
var NAME = 2; // export const ASYNC = 3;
|
|
// This logic is correct because currently only include the 4th tuple member
|
|
// when the module is async. If that changes we will need to actually assert
|
|
// the value is true. We don't index into the 4th slot because flow does not
|
|
// like the potential out of bounds access
|
|
|
|
function isAsyncImport(metadata) {
|
|
return metadata.length === 4;
|
|
}
|
|
|
|
function resolveServerReference(bundlerConfig, id) {
|
|
var name = '';
|
|
var resolvedModuleData = bundlerConfig[id];
|
|
|
|
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.
|
|
var idx = id.lastIndexOf('#');
|
|
|
|
if (idx !== -1) {
|
|
name = id.slice(idx + 1);
|
|
resolvedModuleData = bundlerConfig[id.slice(0, idx)];
|
|
}
|
|
|
|
if (!resolvedModuleData) {
|
|
throw new Error('Could not find the module "' + id + '" in the React Server Manifest. ' + 'This is probably a bug in the React Server Components bundler.');
|
|
}
|
|
} // TODO: This needs to return async: true if it's an async module.
|
|
|
|
|
|
return [resolvedModuleData.id, resolvedModuleData.chunks, name];
|
|
} // The chunk cache contains all the chunks we've preloaded so far.
|
|
// If they're still pending they're a thenable. This map also exists
|
|
// in Webpack but unfortunately it's not exposed so we have to
|
|
// replicate it in user space. null means that it has already loaded.
|
|
|
|
var chunkCache = new Map();
|
|
|
|
function requireAsyncModule(id) {
|
|
// We've already loaded all the chunks. We can require the module.
|
|
var promise = globalThis.__next_require__(id);
|
|
|
|
if (typeof promise.then !== 'function') {
|
|
// This wasn't a promise after all.
|
|
return null;
|
|
} else if (promise.status === 'fulfilled') {
|
|
// This module was already resolved earlier.
|
|
return null;
|
|
} else {
|
|
// Instrument the Promise to stash the result.
|
|
promise.then(function (value) {
|
|
var fulfilledThenable = promise;
|
|
fulfilledThenable.status = 'fulfilled';
|
|
fulfilledThenable.value = value;
|
|
}, function (reason) {
|
|
var rejectedThenable = promise;
|
|
rejectedThenable.status = 'rejected';
|
|
rejectedThenable.reason = reason;
|
|
});
|
|
return promise;
|
|
}
|
|
}
|
|
|
|
function ignoreReject() {// We rely on rejected promises to be handled by another listener.
|
|
} // Start preloading the modules since we might need them soon.
|
|
// This function doesn't suspend.
|
|
|
|
|
|
function preloadModule(metadata) {
|
|
var chunks = metadata[CHUNKS];
|
|
var promises = [];
|
|
var i = 0;
|
|
|
|
while (i < chunks.length) {
|
|
var chunkId = chunks[i++];
|
|
chunks[i++];
|
|
var entry = chunkCache.get(chunkId);
|
|
|
|
if (entry === undefined) {
|
|
var thenable = loadChunk(chunkId);
|
|
promises.push(thenable); // $FlowFixMe[method-unbinding]
|
|
|
|
var resolve = chunkCache.set.bind(chunkCache, chunkId, null);
|
|
thenable.then(resolve, ignoreReject);
|
|
chunkCache.set(chunkId, thenable);
|
|
} else if (entry !== null) {
|
|
promises.push(entry);
|
|
}
|
|
}
|
|
|
|
if (isAsyncImport(metadata)) {
|
|
if (promises.length === 0) {
|
|
return requireAsyncModule(metadata[ID]);
|
|
} else {
|
|
return Promise.all(promises).then(function () {
|
|
return requireAsyncModule(metadata[ID]);
|
|
});
|
|
}
|
|
} else if (promises.length > 0) {
|
|
return Promise.all(promises);
|
|
} else {
|
|
return null;
|
|
}
|
|
} // Actually require the module or suspend if it's not yet ready.
|
|
// Increase priority if necessary.
|
|
|
|
function requireModule(metadata) {
|
|
var moduleExports = globalThis.__next_require__(metadata[ID]);
|
|
|
|
if (isAsyncImport(metadata)) {
|
|
if (typeof moduleExports.then !== 'function') ; else if (moduleExports.status === 'fulfilled') {
|
|
// This Promise should've been instrumented by preloadModule.
|
|
moduleExports = moduleExports.value;
|
|
} else {
|
|
throw moduleExports.reason;
|
|
}
|
|
}
|
|
|
|
if (metadata[NAME] === '*') {
|
|
// This is a placeholder value that represents that the caller imported this
|
|
// as a CommonJS module as is.
|
|
return moduleExports;
|
|
}
|
|
|
|
if (metadata[NAME] === '') {
|
|
// This is a placeholder value that represents that the caller accessed the
|
|
// default property of this if it was an ESM interop module.
|
|
return moduleExports.__esModule ? moduleExports.default : moduleExports;
|
|
}
|
|
|
|
return moduleExports[metadata[NAME]];
|
|
}
|
|
|
|
function loadChunk(chunkId, filename) {
|
|
return __webpack_chunk_load__(chunkId);
|
|
}
|
|
|
|
// The server acts as a Client of itself when resolving Server References.
|
|
var PENDING = 'pending';
|
|
var BLOCKED = 'blocked';
|
|
var RESOLVED_MODEL = 'resolved_model';
|
|
var INITIALIZED = 'fulfilled';
|
|
var ERRORED = 'rejected'; // $FlowFixMe[missing-this-annot]
|
|
|
|
function Chunk(status, value, reason, response) {
|
|
this.status = status;
|
|
this.value = value;
|
|
this.reason = reason;
|
|
this._response = response;
|
|
} // We subclass Promise.prototype so that we get other methods like .catch
|
|
|
|
|
|
Chunk.prototype = Object.create(Promise.prototype); // TODO: This doesn't return a new Promise chain unlike the real .then
|
|
|
|
Chunk.prototype.then = function (resolve, reject) {
|
|
var chunk = this; // If we have resolved content, we try to initialize it first which
|
|
// might put us back into one of the other states.
|
|
|
|
switch (chunk.status) {
|
|
case RESOLVED_MODEL:
|
|
initializeModelChunk(chunk);
|
|
break;
|
|
} // The status might have changed after initialization.
|
|
|
|
|
|
switch (chunk.status) {
|
|
case INITIALIZED:
|
|
resolve(chunk.value);
|
|
break;
|
|
|
|
case PENDING:
|
|
case BLOCKED:
|
|
if (resolve) {
|
|
if (chunk.value === null) {
|
|
chunk.value = [];
|
|
}
|
|
|
|
chunk.value.push(resolve);
|
|
}
|
|
|
|
if (reject) {
|
|
if (chunk.reason === null) {
|
|
chunk.reason = [];
|
|
}
|
|
|
|
chunk.reason.push(reject);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
reject(chunk.reason);
|
|
break;
|
|
}
|
|
};
|
|
|
|
function getRoot(response) {
|
|
var chunk = getChunk(response, 0);
|
|
return chunk;
|
|
}
|
|
|
|
function createPendingChunk(response) {
|
|
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
|
|
return new Chunk(PENDING, null, null, response);
|
|
}
|
|
|
|
function wakeChunk(listeners, value) {
|
|
for (var i = 0; i < listeners.length; i++) {
|
|
var listener = listeners[i];
|
|
listener(value);
|
|
}
|
|
}
|
|
|
|
function triggerErrorOnChunk(chunk, error) {
|
|
if (chunk.status !== PENDING && chunk.status !== BLOCKED) {
|
|
// We already resolved. We didn't expect to see this.
|
|
return;
|
|
}
|
|
|
|
var listeners = chunk.reason;
|
|
var erroredChunk = chunk;
|
|
erroredChunk.status = ERRORED;
|
|
erroredChunk.reason = error;
|
|
|
|
if (listeners !== null) {
|
|
wakeChunk(listeners, error);
|
|
}
|
|
}
|
|
|
|
function createResolvedModelChunk(response, value) {
|
|
// $FlowFixMe[invalid-constructor] Flow doesn't support functions as constructors
|
|
return new Chunk(RESOLVED_MODEL, value, null, response);
|
|
}
|
|
|
|
function bindArgs$1(fn, args) {
|
|
return fn.bind.apply(fn, [null].concat(args));
|
|
}
|
|
|
|
function loadServerReference$1(response, id, bound, parentChunk, parentObject, key) {
|
|
var serverReference = resolveServerReference(response._bundlerConfig, id); // We expect most servers to not really need this because you'd just have all
|
|
// the relevant modules already loaded but it allows for lazy loading of code
|
|
// if needed.
|
|
|
|
var preloadPromise = preloadModule(serverReference);
|
|
var promise;
|
|
|
|
if (bound) {
|
|
promise = Promise.all([bound, preloadPromise]).then(function (_ref) {
|
|
var args = _ref[0];
|
|
return bindArgs$1(requireModule(serverReference), args);
|
|
});
|
|
} else {
|
|
if (preloadPromise) {
|
|
promise = Promise.resolve(preloadPromise).then(function () {
|
|
return requireModule(serverReference);
|
|
});
|
|
} else {
|
|
// Synchronously available
|
|
return requireModule(serverReference);
|
|
}
|
|
}
|
|
|
|
promise.then(createModelResolver(parentChunk, parentObject, key), createModelReject(parentChunk)); // We need a placeholder value that will be replaced later.
|
|
|
|
return null;
|
|
}
|
|
|
|
var initializingChunk = null;
|
|
var initializingChunkBlockedModel = null;
|
|
|
|
function initializeModelChunk(chunk) {
|
|
var prevChunk = initializingChunk;
|
|
var prevBlocked = initializingChunkBlockedModel;
|
|
initializingChunk = chunk;
|
|
initializingChunkBlockedModel = null;
|
|
|
|
try {
|
|
var value = JSON.parse(chunk.value, chunk._response._fromJSON);
|
|
|
|
if (initializingChunkBlockedModel !== null && initializingChunkBlockedModel.deps > 0) {
|
|
initializingChunkBlockedModel.value = value; // We discovered new dependencies on modules that are not yet resolved.
|
|
// We have to go the BLOCKED state until they're resolved.
|
|
|
|
var blockedChunk = chunk;
|
|
blockedChunk.status = BLOCKED;
|
|
blockedChunk.value = null;
|
|
blockedChunk.reason = null;
|
|
} else {
|
|
var initializedChunk = chunk;
|
|
initializedChunk.status = INITIALIZED;
|
|
initializedChunk.value = value;
|
|
}
|
|
} catch (error) {
|
|
var erroredChunk = chunk;
|
|
erroredChunk.status = ERRORED;
|
|
erroredChunk.reason = error;
|
|
} finally {
|
|
initializingChunk = prevChunk;
|
|
initializingChunkBlockedModel = prevBlocked;
|
|
}
|
|
} // Report that any missing chunks in the model is now going to throw this
|
|
// error upon read. Also notify any pending promises.
|
|
|
|
|
|
function reportGlobalError(response, error) {
|
|
response._chunks.forEach(function (chunk) {
|
|
// If this chunk was already resolved or errored, it won't
|
|
// trigger an error but if it wasn't then we need to
|
|
// because we won't be getting any new data to resolve it.
|
|
if (chunk.status === PENDING) {
|
|
triggerErrorOnChunk(chunk, error);
|
|
}
|
|
});
|
|
}
|
|
|
|
function getChunk(response, id) {
|
|
var chunks = response._chunks;
|
|
var chunk = chunks.get(id);
|
|
|
|
if (!chunk) {
|
|
var prefix = response._prefix;
|
|
var key = prefix + id; // Check if we have this field in the backing store already.
|
|
|
|
var backingEntry = response._formData.get(key);
|
|
|
|
if (backingEntry != null) {
|
|
// We assume that this is a string entry for now.
|
|
chunk = createResolvedModelChunk(response, backingEntry);
|
|
} else {
|
|
// We're still waiting on this entry to stream in.
|
|
chunk = createPendingChunk(response);
|
|
}
|
|
|
|
chunks.set(id, chunk);
|
|
}
|
|
|
|
return chunk;
|
|
}
|
|
|
|
function createModelResolver(chunk, parentObject, key) {
|
|
var blocked;
|
|
|
|
if (initializingChunkBlockedModel) {
|
|
blocked = initializingChunkBlockedModel;
|
|
blocked.deps++;
|
|
} else {
|
|
blocked = initializingChunkBlockedModel = {
|
|
deps: 1,
|
|
value: null
|
|
};
|
|
}
|
|
|
|
return function (value) {
|
|
parentObject[key] = value;
|
|
blocked.deps--;
|
|
|
|
if (blocked.deps === 0) {
|
|
if (chunk.status !== BLOCKED) {
|
|
return;
|
|
}
|
|
|
|
var resolveListeners = chunk.value;
|
|
var initializedChunk = chunk;
|
|
initializedChunk.status = INITIALIZED;
|
|
initializedChunk.value = blocked.value;
|
|
|
|
if (resolveListeners !== null) {
|
|
wakeChunk(resolveListeners, blocked.value);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function createModelReject(chunk) {
|
|
return function (error) {
|
|
return triggerErrorOnChunk(chunk, error);
|
|
};
|
|
}
|
|
|
|
function getOutlinedModel(response, id) {
|
|
var chunk = getChunk(response, id);
|
|
|
|
if (chunk.status === RESOLVED_MODEL) {
|
|
initializeModelChunk(chunk);
|
|
}
|
|
|
|
if (chunk.status !== INITIALIZED) {
|
|
// We know that this is emitted earlier so otherwise it's an error.
|
|
throw chunk.reason;
|
|
}
|
|
|
|
return chunk.value;
|
|
}
|
|
|
|
function parseModelString(response, parentObject, key, value) {
|
|
if (value[0] === '$') {
|
|
switch (value[1]) {
|
|
case '$':
|
|
{
|
|
// This was an escaped string value.
|
|
return value.slice(1);
|
|
}
|
|
|
|
case '@':
|
|
{
|
|
// Promise
|
|
var id = parseInt(value.slice(2), 16);
|
|
var chunk = getChunk(response, id);
|
|
return chunk;
|
|
}
|
|
|
|
case 'S':
|
|
{
|
|
// Symbol
|
|
return Symbol.for(value.slice(2));
|
|
}
|
|
|
|
case 'F':
|
|
{
|
|
// Server Reference
|
|
var _id = parseInt(value.slice(2), 16); // TODO: Just encode this in the reference inline instead of as a model.
|
|
|
|
|
|
var metaData = getOutlinedModel(response, _id);
|
|
return loadServerReference$1(response, metaData.id, metaData.bound, initializingChunk, parentObject, key);
|
|
}
|
|
|
|
case 'Q':
|
|
{
|
|
// Map
|
|
var _id2 = parseInt(value.slice(2), 16);
|
|
|
|
var data = getOutlinedModel(response, _id2);
|
|
return new Map(data);
|
|
}
|
|
|
|
case 'W':
|
|
{
|
|
// Set
|
|
var _id3 = parseInt(value.slice(2), 16);
|
|
|
|
var _data = getOutlinedModel(response, _id3);
|
|
|
|
return new Set(_data);
|
|
}
|
|
|
|
case 'K':
|
|
{
|
|
// FormData
|
|
var stringId = value.slice(2);
|
|
var formPrefix = response._prefix + stringId + '_';
|
|
|
|
var _data2 = new FormData();
|
|
|
|
var backingFormData = response._formData; // We assume that the reference to FormData always comes after each
|
|
// entry that it references so we can assume they all exist in the
|
|
// backing store already.
|
|
// $FlowFixMe[prop-missing] FormData has forEach on it.
|
|
|
|
backingFormData.forEach(function (entry, entryKey) {
|
|
if (entryKey.startsWith(formPrefix)) {
|
|
_data2.append(entryKey.slice(formPrefix.length), entry);
|
|
}
|
|
});
|
|
return _data2;
|
|
}
|
|
|
|
case 'I':
|
|
{
|
|
// $Infinity
|
|
return Infinity;
|
|
}
|
|
|
|
case '-':
|
|
{
|
|
// $-0 or $-Infinity
|
|
if (value === '$-0') {
|
|
return -0;
|
|
} else {
|
|
return -Infinity;
|
|
}
|
|
}
|
|
|
|
case 'N':
|
|
{
|
|
// $NaN
|
|
return NaN;
|
|
}
|
|
|
|
case 'u':
|
|
{
|
|
// matches "$undefined"
|
|
// Special encoding for `undefined` which can't be serialized as JSON otherwise.
|
|
return undefined;
|
|
}
|
|
|
|
case 'D':
|
|
{
|
|
// Date
|
|
return new Date(Date.parse(value.slice(2)));
|
|
}
|
|
|
|
case 'n':
|
|
{
|
|
// BigInt
|
|
return BigInt(value.slice(2));
|
|
}
|
|
|
|
default:
|
|
{
|
|
// We assume that anything else is a reference ID.
|
|
var _id4 = parseInt(value.slice(1), 16);
|
|
|
|
var _chunk = getChunk(response, _id4);
|
|
|
|
switch (_chunk.status) {
|
|
case RESOLVED_MODEL:
|
|
initializeModelChunk(_chunk);
|
|
break;
|
|
} // The status might have changed after initialization.
|
|
|
|
|
|
switch (_chunk.status) {
|
|
case INITIALIZED:
|
|
return _chunk.value;
|
|
|
|
case PENDING:
|
|
case BLOCKED:
|
|
var parentChunk = initializingChunk;
|
|
|
|
_chunk.then(createModelResolver(parentChunk, parentObject, key), createModelReject(parentChunk));
|
|
|
|
return null;
|
|
|
|
default:
|
|
throw _chunk.reason;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
function createResponse(bundlerConfig, formFieldPrefix) {
|
|
var backingFormData = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new FormData();
|
|
var chunks = new Map();
|
|
var response = {
|
|
_bundlerConfig: bundlerConfig,
|
|
_prefix: formFieldPrefix,
|
|
_formData: backingFormData,
|
|
_chunks: chunks,
|
|
_fromJSON: function (key, value) {
|
|
if (typeof value === 'string') {
|
|
// We can't use .bind here because we need the "this" value.
|
|
return parseModelString(response, this, key, value);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
};
|
|
return response;
|
|
}
|
|
function close(response) {
|
|
// In case there are any remaining unresolved chunks, they won't
|
|
// be resolved now. So we need to issue an error to those.
|
|
// Ideally we should be able to early bail out if we kept a
|
|
// ref count of pending chunks.
|
|
reportGlobalError(response, new Error('Connection closed.'));
|
|
}
|
|
|
|
function bindArgs(fn, args) {
|
|
return fn.bind.apply(fn, [null].concat(args));
|
|
}
|
|
|
|
function loadServerReference(bundlerConfig, id, bound) {
|
|
var serverReference = resolveServerReference(bundlerConfig, id); // We expect most servers to not really need this because you'd just have all
|
|
// the relevant modules already loaded but it allows for lazy loading of code
|
|
// if needed.
|
|
|
|
var preloadPromise = preloadModule(serverReference);
|
|
|
|
if (bound) {
|
|
return Promise.all([bound, preloadPromise]).then(function (_ref) {
|
|
var args = _ref[0];
|
|
return bindArgs(requireModule(serverReference), args);
|
|
});
|
|
} else if (preloadPromise) {
|
|
return Promise.resolve(preloadPromise).then(function () {
|
|
return requireModule(serverReference);
|
|
});
|
|
} else {
|
|
// Synchronously available
|
|
return Promise.resolve(requireModule(serverReference));
|
|
}
|
|
}
|
|
|
|
function decodeBoundActionMetaData(body, serverManifest, formFieldPrefix) {
|
|
// The data for this reference is encoded in multiple fields under this prefix.
|
|
var actionResponse = createResponse(serverManifest, formFieldPrefix, body);
|
|
close(actionResponse);
|
|
var refPromise = getRoot(actionResponse); // Force it to initialize
|
|
// $FlowFixMe
|
|
|
|
refPromise.then(function () {});
|
|
|
|
if (refPromise.status !== 'fulfilled') {
|
|
// $FlowFixMe
|
|
throw refPromise.reason;
|
|
}
|
|
|
|
return refPromise.value;
|
|
}
|
|
|
|
function decodeAction(body, serverManifest) {
|
|
// We're going to create a new formData object that holds all the fields except
|
|
// the implementation details of the action data.
|
|
var formData = new FormData();
|
|
var action = null; // $FlowFixMe[prop-missing]
|
|
|
|
body.forEach(function (value, key) {
|
|
if (!key.startsWith('$ACTION_')) {
|
|
formData.append(key, value);
|
|
return;
|
|
} // Later actions may override earlier actions if a button is used to override the default
|
|
// form action.
|
|
|
|
|
|
if (key.startsWith('$ACTION_REF_')) {
|
|
var formFieldPrefix = '$ACTION_' + key.slice(12) + ':';
|
|
var metaData = decodeBoundActionMetaData(body, serverManifest, formFieldPrefix);
|
|
action = loadServerReference(serverManifest, metaData.id, metaData.bound);
|
|
return;
|
|
}
|
|
|
|
if (key.startsWith('$ACTION_ID_')) {
|
|
var id = key.slice(11);
|
|
action = loadServerReference(serverManifest, id, null);
|
|
return;
|
|
}
|
|
});
|
|
|
|
if (action === null) {
|
|
return null;
|
|
} // Return the action with the remaining FormData bound to the first argument.
|
|
|
|
|
|
return action.then(function (fn) {
|
|
return fn.bind(null, formData);
|
|
});
|
|
}
|
|
function decodeFormState(actionResult, body, serverManifest) {
|
|
var keyPath = body.get('$ACTION_KEY');
|
|
|
|
if (typeof keyPath !== 'string') {
|
|
// This form submission did not include any form state.
|
|
return Promise.resolve(null);
|
|
} // Search through the form data object to get the reference id and the number
|
|
// of bound arguments. This repeats some of the work done in decodeAction.
|
|
|
|
|
|
var metaData = null; // $FlowFixMe[prop-missing]
|
|
|
|
body.forEach(function (value, key) {
|
|
if (key.startsWith('$ACTION_REF_')) {
|
|
var formFieldPrefix = '$ACTION_' + key.slice(12) + ':';
|
|
metaData = decodeBoundActionMetaData(body, serverManifest, formFieldPrefix);
|
|
} // We don't check for the simple $ACTION_ID_ case because form state actions
|
|
// are always bound to the state argument.
|
|
|
|
});
|
|
|
|
if (metaData === null) {
|
|
// Should be unreachable.
|
|
return Promise.resolve(null);
|
|
}
|
|
|
|
var referenceId = metaData.id;
|
|
return Promise.resolve(metaData.bound).then(function (bound) {
|
|
if (bound === null) {
|
|
// Should be unreachable because form state actions are always bound to the
|
|
// state argument.
|
|
return null;
|
|
} // The form action dispatch method is always bound to the initial state.
|
|
// But when comparing signatures, we compare to the original unbound action.
|
|
// Subtract one from the arity to account for this.
|
|
|
|
|
|
var boundArity = bound.length - 1;
|
|
return [actionResult, keyPath, referenceId, boundArity];
|
|
});
|
|
}
|
|
|
|
function renderToReadableStream(model, webpackMap, options) {
|
|
var request = createRequest(model, webpackMap, options ? options.onError : undefined, options ? options.context : undefined, options ? options.identifierPrefix : undefined, options ? options.onPostpone : undefined);
|
|
|
|
if (options && options.signal) {
|
|
var signal = options.signal;
|
|
|
|
if (signal.aborted) {
|
|
abort(request, signal.reason);
|
|
} else {
|
|
var listener = function () {
|
|
abort(request, signal.reason);
|
|
signal.removeEventListener('abort', listener);
|
|
};
|
|
|
|
signal.addEventListener('abort', listener);
|
|
}
|
|
}
|
|
|
|
var stream = new ReadableStream({
|
|
type: 'bytes',
|
|
start: function (controller) {
|
|
startWork(request);
|
|
},
|
|
pull: function (controller) {
|
|
startFlowing(request, controller);
|
|
},
|
|
cancel: function (reason) {
|
|
stopFlowing(request);
|
|
abort(request, reason);
|
|
}
|
|
}, // $FlowFixMe[prop-missing] size() methods are not allowed on byte streams.
|
|
{
|
|
highWaterMark: 0
|
|
});
|
|
return stream;
|
|
}
|
|
|
|
function decodeReply(body, webpackMap) {
|
|
if (typeof body === 'string') {
|
|
var form = new FormData();
|
|
form.append('0', body);
|
|
body = form;
|
|
}
|
|
|
|
var response = createResponse(webpackMap, '', body);
|
|
close(response);
|
|
return getRoot(response);
|
|
}
|
|
|
|
exports.createClientModuleProxy = createClientModuleProxy;
|
|
exports.decodeAction = decodeAction;
|
|
exports.decodeFormState = decodeFormState;
|
|
exports.decodeReply = decodeReply;
|
|
exports.registerClientReference = registerClientReference;
|
|
exports.registerServerReference = registerServerReference;
|
|
exports.renderToReadableStream = renderToReadableStream;
|
|
})();
|
|
}
|