diff --git a/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.development.server.js b/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.development.server.js index 6f4c459c3a7ed5e..948d711b6407868 100644 --- a/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.development.server.js +++ b/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.development.server.js @@ -1,4 +1,5 @@ -/** @license React vundefined +/** + * @license React * react-server-dom-webpack-writer.browser.development.server.js * * Copyright (c) Facebook, Inc. and its affiliates. @@ -57,9 +58,68 @@ function printWarning(level, format, args) { function scheduleWork(callback) { callback(); } +var VIEW_SIZE = 512; +var currentView = null; +var writtenBytes = 0; +function beginWriting(destination) { + currentView = new Uint8Array(VIEW_SIZE); + writtenBytes = 0; +} function writeChunk(destination, chunk) { - destination.enqueue(chunk); - return destination.desiredSize > 0; + if (chunk.length === 0) { + return; + } + + if (chunk.length > VIEW_SIZE) { + // 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.length) { + // 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.length; +} +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(destination) { destination.close(); @@ -68,6 +128,9 @@ var textEncoder = new TextEncoder(); function stringToChunk(content) { return textEncoder.encode(content); } +function stringToPrecomputedChunk(content) { + return textEncoder.encode(content); +} function closeWithError(destination, error) { if (typeof destination.error === 'function') { // $FlowFixMe: This is an Error object or the destination accepts other types. @@ -108,6 +171,10 @@ function processModuleChunk(request, id, moduleMetaData) { var row = serializeRowHeader('M', id) + json + '\n'; return stringToChunk(row); } +function processProviderChunk(request, id, contextName) { + var row = serializeRowHeader('P', id) + contextName + '\n'; + return stringToChunk(row); +} function processSymbolChunk(request, id, name) { var json = stringify(name); var row = serializeRowHeader('S', id) + json + '\n'; @@ -129,53 +196,758 @@ function resolveModuleMetaData(config, moduleReference) { // 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. If there is no native Symbol -// nor polyfill, then a plain number is used for performance. -var REACT_ELEMENT_TYPE = 0xeac7; -var REACT_PORTAL_TYPE = 0xeaca; -var REACT_FRAGMENT_TYPE = 0xeacb; -var REACT_STRICT_MODE_TYPE = 0xeacc; -var REACT_PROFILER_TYPE = 0xead2; -var REACT_PROVIDER_TYPE = 0xeacd; -var REACT_CONTEXT_TYPE = 0xeace; -var REACT_FORWARD_REF_TYPE = 0xead0; -var REACT_SUSPENSE_TYPE = 0xead1; -var REACT_SUSPENSE_LIST_TYPE = 0xead8; -var REACT_MEMO_TYPE = 0xead3; -var REACT_LAZY_TYPE = 0xead4; -var REACT_SCOPE_TYPE = 0xead7; -var REACT_DEBUG_TRACING_MODE_TYPE = 0xeae1; -var REACT_OFFSCREEN_TYPE = 0xeae2; -var REACT_LEGACY_HIDDEN_TYPE = 0xeae3; -var REACT_CACHE_TYPE = 0xeae4; - -if (typeof Symbol === 'function' && Symbol.for) { - var symbolFor = Symbol.for; - REACT_ELEMENT_TYPE = symbolFor('react.element'); - REACT_PORTAL_TYPE = symbolFor('react.portal'); - REACT_FRAGMENT_TYPE = symbolFor('react.fragment'); - REACT_STRICT_MODE_TYPE = symbolFor('react.strict_mode'); - REACT_PROFILER_TYPE = symbolFor('react.profiler'); - REACT_PROVIDER_TYPE = symbolFor('react.provider'); - REACT_CONTEXT_TYPE = symbolFor('react.context'); - REACT_FORWARD_REF_TYPE = symbolFor('react.forward_ref'); - REACT_SUSPENSE_TYPE = symbolFor('react.suspense'); - REACT_SUSPENSE_LIST_TYPE = symbolFor('react.suspense_list'); - REACT_MEMO_TYPE = symbolFor('react.memo'); - REACT_LAZY_TYPE = symbolFor('react.lazy'); - REACT_SCOPE_TYPE = symbolFor('react.scope'); - REACT_DEBUG_TRACING_MODE_TYPE = symbolFor('react.debug_trace_mode'); - REACT_OFFSCREEN_TYPE = symbolFor('react.offscreen'); - REACT_LEGACY_HIDDEN_TYPE = symbolFor('react.legacy_hidden'); - REACT_CACHE_TYPE = symbolFor('react.cache'); +// 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_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'); + +// A reserved attribute. +// It is handled by React separately and shouldn't be written to the DOM. +var RESERVED = 0; // A simple string attribute. +// Attributes that aren't in the filter are presumed to have this type. + +var STRING = 1; // A string attribute that accepts booleans in React. In HTML, these are called +// "enumerated" attributes with "true" and "false" as possible values. +// When true, it should be set to a "true" string. +// When false, it should be set to a "false" string. + +var BOOLEANISH_STRING = 2; // A real boolean attribute. +// When true, it should be present (set either to an empty string or its name). +// When false, it should be omitted. + +var BOOLEAN = 3; // An attribute that can be used as a flag as well as with a value. +// When true, it should be present (set either to an empty string or its name). +// When false, it should be omitted. +// For any other value, should be present with that value. + +var OVERLOADED_BOOLEAN = 4; // An attribute that must be numeric or parse as a numeric. +// When falsy, it should be removed. + +var NUMERIC = 5; // An attribute that must be positive numeric or parse as a positive numeric. +// When falsy, it should be removed. + +var POSITIVE_NUMERIC = 6; + +function PropertyInfoRecord(name, type, mustUseProperty, attributeName, attributeNamespace, sanitizeURL, removeEmptyString) { + this.acceptsBooleans = type === BOOLEANISH_STRING || type === BOOLEAN || type === OVERLOADED_BOOLEAN; + this.attributeName = attributeName; + this.attributeNamespace = attributeNamespace; + this.mustUseProperty = mustUseProperty; + this.propertyName = name; + this.type = type; + this.sanitizeURL = sanitizeURL; + this.removeEmptyString = removeEmptyString; +} // When adding attributes to this list, be sure to also add them to +// the `possibleStandardNames` module to ensure casing and incorrect +// name warnings. + + +var properties = {}; // These props are reserved by React. They shouldn't be written to the DOM. + +var reservedProps = ['children', 'dangerouslySetInnerHTML', // TODO: This prevents the assignment of defaultValue to regular +// elements (not just inputs). Now that ReactDOMInput assigns to the +// defaultValue property -- do we need this? +'defaultValue', 'defaultChecked', 'innerHTML', 'suppressContentEditableWarning', 'suppressHydrationWarning', 'style']; + +{ + reservedProps.push('innerText', 'textContent'); } +reservedProps.forEach(function (name) { + properties[name] = new PropertyInfoRecord(name, RESERVED, false, // mustUseProperty + name, // attributeName + null, // attributeNamespace + false, // sanitizeURL + false); +}); // A few React string attributes have a different name. +// This is a mapping from React prop names to the attribute names. + +[['acceptCharset', 'accept-charset'], ['className', 'class'], ['htmlFor', 'for'], ['httpEquiv', 'http-equiv']].forEach(function (_ref) { + var name = _ref[0], + attributeName = _ref[1]; + properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty + attributeName, // attributeName + null, // attributeNamespace + false, // sanitizeURL + false); +}); // These are "enumerated" HTML attributes that accept "true" and "false". +// In React, we let users pass `true` and `false` even though technically +// these aren't boolean attributes (they are coerced to strings). + +['contentEditable', 'draggable', 'spellCheck', 'value'].forEach(function (name) { + properties[name] = new PropertyInfoRecord(name, BOOLEANISH_STRING, false, // mustUseProperty + name.toLowerCase(), // attributeName + null, // attributeNamespace + false, // sanitizeURL + false); +}); // These are "enumerated" SVG attributes that accept "true" and "false". +// In React, we let users pass `true` and `false` even though technically +// these aren't boolean attributes (they are coerced to strings). +// Since these are SVG attributes, their attribute names are case-sensitive. + +['autoReverse', 'externalResourcesRequired', 'focusable', 'preserveAlpha'].forEach(function (name) { + properties[name] = new PropertyInfoRecord(name, BOOLEANISH_STRING, false, // mustUseProperty + name, // attributeName + null, // attributeNamespace + false, // sanitizeURL + false); +}); // These are HTML boolean attributes. + +['allowFullScreen', 'async', // Note: there is a special case that prevents it from being written to the DOM +// on the client side because the browsers are inconsistent. Instead we call focus(). +'autoFocus', 'autoPlay', 'controls', 'default', 'defer', 'disabled', 'disablePictureInPicture', 'disableRemotePlayback', 'formNoValidate', 'hidden', 'loop', 'noModule', 'noValidate', 'open', 'playsInline', 'readOnly', 'required', 'reversed', 'scoped', 'seamless', // Microdata +'itemScope'].forEach(function (name) { + properties[name] = new PropertyInfoRecord(name, BOOLEAN, false, // mustUseProperty + name.toLowerCase(), // attributeName + null, // attributeNamespace + false, // sanitizeURL + false); +}); // These are the few React props that we set as DOM properties +// rather than attributes. These are all booleans. + +['checked', // Note: `option.selected` is not updated if `select.multiple` is +// disabled with `removeAttribute`. We have special logic for handling this. +'multiple', 'muted', 'selected' // NOTE: if you add a camelCased prop to this list, +// you'll need to set attributeName to name.toLowerCase() +// instead in the assignment below. +].forEach(function (name) { + properties[name] = new PropertyInfoRecord(name, BOOLEAN, true, // mustUseProperty + name, // attributeName + null, // attributeNamespace + false, // sanitizeURL + false); +}); // These are HTML attributes that are "overloaded booleans": they behave like +// booleans, but can also accept a string value. + +['capture', 'download' // NOTE: if you add a camelCased prop to this list, +// you'll need to set attributeName to name.toLowerCase() +// instead in the assignment below. +].forEach(function (name) { + properties[name] = new PropertyInfoRecord(name, OVERLOADED_BOOLEAN, false, // mustUseProperty + name, // attributeName + null, // attributeNamespace + false, // sanitizeURL + false); +}); // These are HTML attributes that must be positive numbers. + +['cols', 'rows', 'size', 'span' // NOTE: if you add a camelCased prop to this list, +// you'll need to set attributeName to name.toLowerCase() +// instead in the assignment below. +].forEach(function (name) { + properties[name] = new PropertyInfoRecord(name, POSITIVE_NUMERIC, false, // mustUseProperty + name, // attributeName + null, // attributeNamespace + false, // sanitizeURL + false); +}); // These are HTML attributes that must be numbers. + +['rowSpan', 'start'].forEach(function (name) { + properties[name] = new PropertyInfoRecord(name, NUMERIC, false, // mustUseProperty + name.toLowerCase(), // attributeName + null, // attributeNamespace + false, // sanitizeURL + false); +}); +var CAMELIZE = /[\-\:]([a-z])/g; + +var capitalize = function (token) { + return token[1].toUpperCase(); +}; // This is a list of all SVG attributes that need special casing, namespacing, +// or boolean value assignment. Regular attributes that just accept strings +// and have the same names are omitted, just like in the HTML attribute filter. +// Some of these attributes can be hard to find. This list was created by +// scraping the MDN documentation. + + +['accent-height', 'alignment-baseline', 'arabic-form', 'baseline-shift', 'cap-height', 'clip-path', 'clip-rule', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'dominant-baseline', 'enable-background', 'fill-opacity', 'fill-rule', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-name', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'horiz-adv-x', 'horiz-origin-x', 'image-rendering', 'letter-spacing', 'lighting-color', 'marker-end', 'marker-mid', 'marker-start', 'overline-position', 'overline-thickness', 'paint-order', 'panose-1', 'pointer-events', 'rendering-intent', 'shape-rendering', 'stop-color', 'stop-opacity', 'strikethrough-position', 'strikethrough-thickness', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-decoration', 'text-rendering', 'underline-position', 'underline-thickness', 'unicode-bidi', 'unicode-range', 'units-per-em', 'v-alphabetic', 'v-hanging', 'v-ideographic', 'v-mathematical', 'vector-effect', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'word-spacing', 'writing-mode', 'xmlns:xlink', 'x-height' // NOTE: if you add a camelCased prop to this list, +// you'll need to set attributeName to name.toLowerCase() +// instead in the assignment below. +].forEach(function (attributeName) { + var name = attributeName.replace(CAMELIZE, capitalize); + properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty + attributeName, null, // attributeNamespace + false, // sanitizeURL + false); +}); // String SVG attributes with the xlink namespace. + +['xlink:actuate', 'xlink:arcrole', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type' // NOTE: if you add a camelCased prop to this list, +// you'll need to set attributeName to name.toLowerCase() +// instead in the assignment below. +].forEach(function (attributeName) { + var name = attributeName.replace(CAMELIZE, capitalize); + properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty + attributeName, 'http://www.w3.org/1999/xlink', false, // sanitizeURL + false); +}); // String SVG attributes with the xml namespace. + +['xml:base', 'xml:lang', 'xml:space' // NOTE: if you add a camelCased prop to this list, +// you'll need to set attributeName to name.toLowerCase() +// instead in the assignment below. +].forEach(function (attributeName) { + var name = attributeName.replace(CAMELIZE, capitalize); + properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty + attributeName, 'http://www.w3.org/XML/1998/namespace', false, // sanitizeURL + false); +}); // These attribute exists both in HTML and SVG. +// The attribute name is case-sensitive in SVG so we can't just use +// the React name like we do for attributes that exist only in HTML. + +['tabIndex', 'crossOrigin'].forEach(function (attributeName) { + properties[attributeName] = new PropertyInfoRecord(attributeName, STRING, false, // mustUseProperty + attributeName.toLowerCase(), // attributeName + null, // attributeNamespace + false, // sanitizeURL + false); +}); // These attributes accept URLs. These must not allow javascript: URLS. +// These will also need to accept Trusted Types object in the future. + +var xlinkHref = 'xlinkHref'; +properties[xlinkHref] = new PropertyInfoRecord('xlinkHref', STRING, false, // mustUseProperty +'xlink:href', 'http://www.w3.org/1999/xlink', true, // sanitizeURL +false); +['src', 'href', 'action', 'formAction'].forEach(function (attributeName) { + properties[attributeName] = new PropertyInfoRecord(attributeName, STRING, false, // mustUseProperty + attributeName.toLowerCase(), // attributeName + null, // attributeNamespace + true, // sanitizeURL + true); +}); + +/** + * CSS properties which accept numbers but are not in units of "px". + */ +var isUnitlessNumber = { + animationIterationCount: true, + aspectRatio: true, + borderImageOutset: true, + borderImageSlice: true, + borderImageWidth: true, + boxFlex: true, + boxFlexGroup: true, + boxOrdinalGroup: true, + columnCount: true, + columns: true, + flex: true, + flexGrow: true, + flexPositive: true, + flexShrink: true, + flexNegative: true, + flexOrder: true, + gridArea: true, + gridRow: true, + gridRowEnd: true, + gridRowSpan: true, + gridRowStart: true, + gridColumn: true, + gridColumnEnd: true, + gridColumnSpan: true, + gridColumnStart: true, + fontWeight: true, + lineClamp: true, + lineHeight: true, + opacity: true, + order: true, + orphans: true, + tabSize: true, + widows: true, + zIndex: true, + zoom: true, + // SVG-related properties + fillOpacity: true, + floodOpacity: true, + stopOpacity: true, + strokeDasharray: true, + strokeDashoffset: true, + strokeMiterlimit: true, + strokeOpacity: true, + strokeWidth: true +}; +/** + * @param {string} prefix vendor-specific prefix, eg: Webkit + * @param {string} key style name, eg: transitionDuration + * @return {string} style name prefixed with `prefix`, properly camelCased, eg: + * WebkitTransitionDuration + */ + +function prefixKey(prefix, key) { + return prefix + key.charAt(0).toUpperCase() + key.substring(1); +} +/** + * Support style names that may come passed in prefixed by adding permutations + * of vendor prefixes. + */ + + +var prefixes = ['Webkit', 'ms', 'Moz', 'O']; // Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an +// infinite loop, because it iterates over the newly added props too. + +Object.keys(isUnitlessNumber).forEach(function (prop) { + prefixes.forEach(function (prefix) { + isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop]; + }); +}); + var isArrayImpl = Array.isArray; // eslint-disable-next-line no-redeclare function isArray(a) { return isArrayImpl(a); } +var startInlineScript = stringToPrecomputedChunk(''); +var startScriptSrc = stringToPrecomputedChunk(''); // Allows us to keep track of what we've already written so we can refer back to it. + +var textSeparator = stringToPrecomputedChunk(''); + +var styleAttributeStart = stringToPrecomputedChunk(' style="'); +var styleAssign = stringToPrecomputedChunk(':'); +var styleSeparator = stringToPrecomputedChunk(';'); + +var attributeSeparator = stringToPrecomputedChunk(' '); +var attributeAssign = stringToPrecomputedChunk('="'); +var attributeEnd = stringToPrecomputedChunk('"'); +var attributeEmptyString = stringToPrecomputedChunk('=""'); + +var endOfStartTag = stringToPrecomputedChunk('>'); +var endOfStartTagSelfClosing = stringToPrecomputedChunk('/>'); + +var selectedMarkerAttribute = stringToPrecomputedChunk(' selected=""'); + +var leadingNewline = stringToPrecomputedChunk('\n'); + +var DOCTYPE = stringToPrecomputedChunk(''); +var endTag1 = stringToPrecomputedChunk(''); +// A placeholder is a node inside a hidden partial tree that can be filled in later, but before +// display. It's never visible to users. We use the template tag because it can be used in every +// type of parent. '); +var completeBoundaryScript1Full = stringToPrecomputedChunk(completeBoundaryFunction + ';$RC("'); +var completeBoundaryScript1Partial = stringToPrecomputedChunk('$RC("'); +var completeBoundaryScript2 = stringToPrecomputedChunk('","'); +var completeBoundaryScript3 = stringToPrecomputedChunk('")'); +var clientRenderScript1Full = stringToPrecomputedChunk(clientRenderFunction + ';$RX("'); +var clientRenderScript1Partial = stringToPrecomputedChunk('$RX("'); +var clientRenderScript2 = stringToPrecomputedChunk('")'); + +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. + + +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: 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(context) { + var value = context._currentValue ; + return value; +} + +function readContext$1(context) { + { + if (context.$$typeof !== REACT_SERVER_CONTEXT_TYPE) { + error('Only ServerContext is supported in Flight'); + } + + if (currentCache === 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(context); +} + +var Dispatcher = { + useMemo: function (nextCreate) { + return nextCreate(); + }, + useCallback: function (callback) { + return callback; + }, + useDebugValue: function () {}, + useDeferredValue: unsupportedHook, + useTransition: unsupportedHook, + getCacheForType: function (resourceType) { + if (!currentCache) { + throw new Error('Reading the cache is only supported while rendering.'); + } + + var entry = currentCache.get(resourceType); + + if (entry === undefined) { + entry = resourceType(); // TODO: Warn if undefined? + + currentCache.set(resourceType, entry); + } + + return entry; + }, + readContext: readContext$1, + useContext: readContext$1, + useReducer: unsupportedHook, + useRef: unsupportedHook, + useState: unsupportedHook, + useInsertionEffect: unsupportedHook, + useLayoutEffect: unsupportedHook, + useImperativeHandle: unsupportedHook, + useEffect: unsupportedHook, + useId: unsupportedHook, + useMutableSource: unsupportedHook, + useSyncExternalStore: unsupportedHook, + useCacheRefresh: function () { + return unsupportedRefresh; + } +}; + +function unsupportedHook() { + throw new Error('This Hook is not supported in Server Components.'); +} + +function unsupportedRefresh() { + if (!currentCache) { + throw new Error('Refreshing the cache is not supported in Server Components.'); + } +} + +var currentCache = null; +function setCurrentCache(cache) { + currentCache = cache; + return currentCache; +} +function getCurrentCache() { + return currentCache; +} + +var ContextRegistry = ReactSharedInternals.ContextRegistry; +function getOrCreateServerContext(globalName) { + if (!ContextRegistry[globalName]) { + ContextRegistry[globalName] = React.createServerContext(globalName, REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED); + } + + return ContextRegistry[globalName]; +} + var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; function defaultErrorHandler(error) { @@ -185,7 +957,7 @@ function defaultErrorHandler(error) { var OPEN = 0; var CLOSING = 1; var CLOSED = 2; -function createRequest(model, bundlerConfig, onError) { +function createRequest(model, bundlerConfig, onError, context) { var pingedSegments = []; var request = { status: OPEN, @@ -201,17 +973,25 @@ function createRequest(model, bundlerConfig, onError) { completedErrorChunks: [], writtenSymbols: new Map(), writtenModules: new Map(), + writtenProviders: new Map(), onError: onError === undefined ? defaultErrorHandler : onError, toJSON: function (key, value) { return resolveModelToJSON(request, this, key, value); } }; request.pendingChunks++; - var rootSegment = createSegment(request, model); + var rootContext = createRootContext(context); + var rootSegment = createSegment(request, model, rootContext); pingedSegments.push(rootSegment); return request; } +function createRootContext(reqContext) { + return importServerContexts(reqContext); +} + +var POP = {}; + function attemptResolveElement(type, key, ref, props) { if (ref !== null && ref !== undefined) { // When the ref moves to the regular props object this will implicitly @@ -245,6 +1025,14 @@ function attemptResolveElement(type, key, ref, props) { } switch (type.$$typeof) { + case REACT_LAZY_TYPE: + { + var payload = type._payload; + var init = type._init; + var wrappedType = init(payload); + return attemptResolveElement(wrappedType, key, ref, props); + } + case REACT_FORWARD_REF_TYPE: { var render = type.render; @@ -255,6 +1043,32 @@ function attemptResolveElement(type, key, ref, props) { { return attemptResolveElement(type.type, key, ref, props); } + + 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 + }]; + } } } @@ -272,11 +1086,12 @@ function pingSegment(request, segment) { } } -function createSegment(request, model) { +function createSegment(request, model, context) { var id = request.nextChunkId++; var segment = { id: id, model: model, + context: context, ping: function () { return pingSegment(request, segment); } @@ -305,8 +1120,7 @@ function escapeStringValue(value) { function isObjectPrototype(object) { if (!object) { return false; - } // $FlowFixMe - + } var ObjectPrototype = Object.prototype; @@ -405,8 +1219,7 @@ function describeValueForErrorMessage(value) { function describeObjectForErrorMessage(objectOrArray, expandedName) { if (isArray(objectOrArray)) { - var str = '['; // $FlowFixMe: Should be refined by now. - + var str = '['; var array = objectOrArray; for (var i = 0; i < array.length; i++) { @@ -431,8 +1244,7 @@ function describeObjectForErrorMessage(objectOrArray, expandedName) { str += ']'; return str; } else { - var _str = '{'; // $FlowFixMe: Should be refined by now. - + var _str = '{'; var object = objectOrArray; var names = Object.keys(object); @@ -462,6 +1274,8 @@ function describeObjectForErrorMessage(objectOrArray, expandedName) { } } +var insideContextProps = null; +var isInsideContextValue = false; function resolveModelToJSON(request, parent, key, value) { { // $FlowFixMe @@ -476,29 +1290,55 @@ function resolveModelToJSON(request, parent, key, value) { switch (value) { case REACT_ELEMENT_TYPE: return '$'; + } - case REACT_LAZY_TYPE: - throw new Error('React Lazy Components are not yet supported on the server.'); + { + 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) { - // TODO: Concatenate keys of parents onto children. - var element = value; + 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 { - // Attempt to render the server component. - value = attemptResolveElement(element.type, element.key, element.ref, element.props); + 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(element.type, element.key, element.ref, element.props); + break; + } + + case REACT_LAZY_TYPE: + { + var payload = value._payload; + var init = value._init; + value = init(payload); + break; + } + } } catch (x) { if (typeof x === 'object' && x !== null && typeof x.then === 'function') { // Something suspended, we'll need to create a new segment and resolve it later. request.pendingChunks++; - var newSegment = createSegment(request, value); + var newSegment = createSegment(request, value, getActiveContext()); var ping = newSegment.ping; x.then(ping, ping); return serializeByRefID(newSegment.id); } else { - reportError(request, x); // Something errored. We'll still send everything we have up until this point. + logRecoverableError(request, x); // 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. @@ -559,6 +1399,28 @@ function resolveModelToJSON(request, parent, key, value) { emitErrorChunk(request, _errorId, x); return serializeByValueID(_errorId); } + } 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; } { @@ -627,7 +1489,7 @@ function resolveModelToJSON(request, parent, key, value) { throw new Error("Type " + typeof value + " is not supported in client component props. " + ("Remove " + describeKeyForErrorMessage(key) + " from this object, or avoid the entire object: " + describeObjectForErrorMessage(parent))); } -function reportError(request, error) { +function logRecoverableError(request, error) { var onError = request.onError; onError(error); } @@ -677,7 +1539,14 @@ function emitSymbolChunk(request, id, name) { request.completedModuleChunks.push(processedChunk); } +function emitProviderChunk(request, id, contextName) { + var processedChunk = processProviderChunk(request, id, contextName); + request.completedJSONChunks.push(processedChunk); +} + function retrySegment(request, segment) { + switchContext(segment.context); + try { var _value3 = segment.model; @@ -700,7 +1569,7 @@ function retrySegment(request, segment) { x.then(ping, ping); return; } else { - reportError(request, x); // This errored, we need to serialize this error to the + logRecoverableError(request, x); // This errored, we need to serialize this error to the emitErrorChunk(request, segment.id, x); } @@ -709,9 +1578,9 @@ function retrySegment(request, segment) { function performWork(request) { var prevDispatcher = ReactCurrentDispatcher.current; - var prevCache = currentCache; + var prevCache = getCurrentCache(); ReactCurrentDispatcher.current = Dispatcher; - currentCache = request.cache; + setCurrentCache(request.cache); try { var pingedSegments = request.pingedSegments; @@ -726,15 +1595,16 @@ function performWork(request) { flushCompletedChunks(request, request.destination); } } catch (error) { - reportError(request, error); + logRecoverableError(request, error); fatalError(request, error); } finally { ReactCurrentDispatcher.current = prevDispatcher; - currentCache = prevCache; + setCurrentCache(prevCache); } } function flushCompletedChunks(request, destination) { + beginWriting(); try { // We emit module chunks first in the stream so that @@ -745,8 +1615,9 @@ function flushCompletedChunks(request, destination) { for (; i < moduleChunks.length; i++) { request.pendingChunks--; var chunk = moduleChunks[i]; + var keepWriting = writeChunkAndReturn(destination, chunk); - if (!writeChunk(destination, chunk)) { + if (!keepWriting) { request.destination = null; i++; break; @@ -762,7 +1633,9 @@ function flushCompletedChunks(request, destination) { request.pendingChunks--; var _chunk = jsonChunks[i]; - if (!writeChunk(destination, _chunk)) { + var _keepWriting = writeChunkAndReturn(destination, _chunk); + + if (!_keepWriting) { request.destination = null; i++; break; @@ -780,7 +1653,9 @@ function flushCompletedChunks(request, destination) { request.pendingChunks--; var _chunk2 = errorChunks[i]; - if (!writeChunk(destination, _chunk2)) { + var _keepWriting2 = writeChunkAndReturn(destination, _chunk2); + + if (!_keepWriting2) { request.destination = null; i++; break; @@ -789,6 +1664,7 @@ function flushCompletedChunks(request, destination) { errorChunks.splice(0, i); } finally { + completeWriting(destination); } if (request.pendingChunks === 0) { @@ -813,83 +1689,51 @@ function startFlowing(request, destination) { return; } + if (request.destination !== null) { + // We're already flowing. + return; + } + request.destination = destination; try { flushCompletedChunks(request, destination); } catch (error) { - reportError(request, error); + logRecoverableError(request, error); fatalError(request, error); } } -function unsupportedHook() { - throw new Error('This Hook is not supported in Server Components.'); -} - -function unsupportedRefresh() { - if (!currentCache) { - throw new Error('Refreshing the cache is not supported in Server Components.'); - } -} - -var currentCache = null; -var Dispatcher = { - useMemo: function (nextCreate) { - return nextCreate(); - }, - useCallback: function (callback) { - return callback; - }, - useDebugValue: function () {}, - useDeferredValue: unsupportedHook, - useTransition: unsupportedHook, - getCacheForType: function (resourceType) { - if (!currentCache) { - throw new Error('Reading the cache is only supported while rendering.'); - } - - var entry = currentCache.get(resourceType); - - if (entry === undefined) { - entry = resourceType(); // TODO: Warn if undefined? - - currentCache.set(resourceType, entry); +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], + _value4 = _contexts$i[1]; + var context = getOrCreateServerContext(name); + pushProvider(context, _value4); } - return entry; - }, - readContext: unsupportedHook, - useContext: unsupportedHook, - useReducer: unsupportedHook, - useRef: unsupportedHook, - useState: unsupportedHook, - useInsertionEffect: unsupportedHook, - useLayoutEffect: unsupportedHook, - useImperativeHandle: unsupportedHook, - useEffect: unsupportedHook, - useId: unsupportedHook, - useMutableSource: unsupportedHook, - useSyncExternalStore: unsupportedHook, - useCacheRefresh: function () { - return unsupportedRefresh; + var importedContext = getActiveContext(); + switchContext(prevContext); + return importedContext; } -}; -function renderToReadableStream(model, webpackMap, options) { - var request = createRequest(model, webpackMap, options ? options.onError : undefined); + return rootContextSnapshot; +} + +function renderToReadableStream(model, webpackMap, options, context) { + var request = createRequest(model, webpackMap, options ? options.onError : undefined, context); var stream = new ReadableStream({ + type: 'bytes', start: function (controller) { startWork(request); }, pull: function (controller) { - // Pull is called immediately even if the stream is not passed to anything. - // That's buffering too early. We want to start buffering once the stream - // is actually used by something so we can give it the best result possible - // at that point. - if (stream.locked) { - startFlowing(request, controller); - } + startFlowing(request, controller); }, cancel: function (reason) {} }); diff --git a/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.production.min.server.js b/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.production.min.server.js index 2977e6eea4d4f99..bb235427138f69e 100644 --- a/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.production.min.server.js +++ b/packages/next/compiled/react-server-dom-webpack/cjs/react-server-dom-webpack-writer.browser.production.min.server.js @@ -1,4 +1,5 @@ -/** @license React vundefined +/** + * @license React * react-server-dom-webpack-writer.browser.production.min.server.js * * Copyright (c) Facebook, Inc. and its affiliates. @@ -6,21 +7,39 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -'use strict';var h=require("react");function n(a,d){a.enqueue(d);return 0=a.length?a:a.substr(0,10)+"...");case "object":if(B(a))return"[...]";a=N(a);return"Object"===a?"{...}":a;case "function":return"function";default:return String(a)}} -function P(a,d){if(B(a)){for(var c="[",b=0;b