diff --git a/package.json b/package.json index 0208f74d066cc..ba9d06c75dfbf 100644 --- a/package.json +++ b/package.json @@ -178,11 +178,11 @@ "random-seed": "0.3.0", "react": "18.2.0", "react-17": "npm:react@17.0.2", - "react-builtin": "npm:react@0.0.0-experimental-9cdf8a99e-20221018", + "react-builtin": "npm:react@0.0.0-experimental-1d3fc9c9c-20221023", "react-dom": "18.2.0", "react-dom-17": "npm:react-dom@17.0.2", - "react-dom-builtin": "npm:react-dom@0.0.0-experimental-9cdf8a99e-20221018", - "react-server-dom-webpack": "0.0.0-experimental-9cdf8a99e-20221018", + "react-dom-builtin": "npm:react-dom@0.0.0-experimental-1d3fc9c9c-20221023", + "react-server-dom-webpack": "0.0.0-experimental-1d3fc9c9c-20221023", "react-ssr-prepass": "1.0.8", "react-virtualized": "9.22.3", "relay-compiler": "13.0.2", diff --git a/packages/next/compiled/react-dom/cjs/react-dom-server-legacy.browser.development.js b/packages/next/compiled/react-dom/cjs/react-dom-server-legacy.browser.development.js index 39ab6e7f29336..918b1a087682a 100644 --- a/packages/next/compiled/react-dom/cjs/react-dom-server-legacy.browser.development.js +++ b/packages/next/compiled/react-dom/cjs/react-dom-server-legacy.browser.development.js @@ -17,7 +17,7 @@ if (process.env.NODE_ENV !== "production") { var React = require('react'); var ReactDOM = require('react-dom'); -var ReactVersion = '18.3.0-experimental-9cdf8a99e-20221018'; +var ReactVersion = '18.3.0-experimental-1d3fc9c9c-20221023'; var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; @@ -2087,7 +2087,10 @@ function createResources() { preloadsMap: new Map(), stylesMap: new Map(), scriptsMap: new Map(), + headsMap: new Map(), // cleared on flush + charset: null, + preconnects: new Set(), fontPreloads: new Set(), // usedImagePreloads: new Set(), precedences: new Map(), @@ -2097,6 +2100,9 @@ function createResources() { explicitStylePreloads: new Set(), // explicitImagePreloads: new Set(), explicitScriptPreloads: new Set(), + headResources: new Set(), + // cache for tracking structured meta tags + structuredMetaKeys: new Map(), // like a module global for currently rendering boundary boundaryResources: null }; @@ -2454,8 +2460,107 @@ function adoptPreloadPropsForScriptProps(resourceProps, preloadProps) { if (resourceProps.crossOrigin == null) resourceProps.crossOrigin = preloadProps.crossOrigin; if (resourceProps.referrerPolicy == null) resourceProps.referrerPolicy = preloadProps.referrerPolicy; if (resourceProps.integrity == null) resourceProps.integrity = preloadProps.integrity; -} // Construct a resource from link props. +} + +function titlePropsFromRawProps(child, rawProps) { + var props = assign({}, rawProps); + + props.children = child; + return props; +} +function resourcesFromElement(type, props) { + if (!currentResources) { + throw new Error('"currentResources" was expected to exist. This is a bug in React.'); + } + + var resources = currentResources; + + switch (type) { + case 'title': + { + var child = props.children; + + if (Array.isArray(child) && child.length === 1) { + child = child[0]; + } + + if (typeof child === 'string' || typeof child === 'number') { + var key = 'title::' + child; + var resource = resources.headsMap.get(key); + + if (!resource) { + resource = { + type: 'title', + props: titlePropsFromRawProps(child, props), + flushed: false + }; + resources.headsMap.set(key, resource); + resources.headResources.add(resource); + } + } + + return true; + } + + case 'meta': + { + var _key, propertyPath; + + if (typeof props.charSet === 'string') { + _key = 'charSet'; + } else if (typeof props.content === 'string') { + var contentKey = '::' + props.content; + + if (typeof props.httpEquiv === 'string') { + _key = 'httpEquiv::' + props.httpEquiv + contentKey; + } else if (typeof props.name === 'string') { + _key = 'name::' + props.name + contentKey; + } else if (typeof props.itemProp === 'string') { + _key = 'itemProp::' + props.itemProp + contentKey; + } else if (typeof props.property === 'string') { + var property = props.property; + _key = 'property::' + property + contentKey; + propertyPath = property; + var parentPath = property.split(':').slice(0, -1).join(':'); + var parentResource = resources.structuredMetaKeys.get(parentPath); + + if (parentResource) { + _key = parentResource.key + '::child::' + _key; + } + } + } + + if (_key) { + if (!resources.headsMap.has(_key)) { + var _resource2 = { + type: 'meta', + key: _key, + props: assign({}, props), + flushed: false + }; + resources.headsMap.set(_key, _resource2); + + if (_key === 'charSet') { + resources.charset = _resource2; + } else { + if (propertyPath) { + resources.structuredMetaKeys.set(propertyPath, _resource2); + } + + resources.headResources.add(_resource2); + } + } + + return true; + } + + return false; + } + } + + return false; +} // Construct a resource from link props. function resourcesFromLink(props) { if (!currentResources) { @@ -2466,10 +2571,12 @@ function resourcesFromLink(props) { var rel = props.rel, href = props.href; - if (!href || typeof href !== 'string') { + if (!href || typeof href !== 'string' || !rel || typeof rel !== 'string') { return false; } + var key = ''; + switch (rel) { case 'stylesheet': { @@ -2504,26 +2611,26 @@ function resourcesFromLink(props) { } else { // We are able to convert this link element to a resource exclusively. We construct the relevant Resource // and return true indicating that this link was fully consumed. - var resource = resources.stylesMap.get(href); + var _resource3 = resources.stylesMap.get(href); - if (resource) { + if (_resource3) { { var resourceProps = stylePropsFromRawProps(href, precedence, props); - adoptPreloadPropsForStyleProps(resourceProps, resource.hint.props); - validateStyleResourceDifference(resource.props, resourceProps); + adoptPreloadPropsForStyleProps(resourceProps, _resource3.hint.props); + validateStyleResourceDifference(_resource3.props, resourceProps); } } else { var _resourceProps = stylePropsFromRawProps(href, precedence, props); - resource = createStyleResource( // $FlowFixMe[incompatible-call] found when upgrading Flow + _resource3 = createStyleResource( // $FlowFixMe[incompatible-call] found when upgrading Flow currentResources, href, precedence, _resourceProps); - resources.usedStylePreloads.add(resource.hint); + resources.usedStylePreloads.add(_resource3.hint); } if (resources.boundaryResources) { - resources.boundaryResources.add(resource); + resources.boundaryResources.add(_resource3); } else { - resource.set.add(resource); + _resource3.set.add(_resource3); } return true; @@ -2543,33 +2650,33 @@ function resourcesFromLink(props) { validateLinkPropsForPreloadResource(props); } - var _resource2 = resources.preloadsMap.get(href); + var _resource4 = resources.preloadsMap.get(href); - if (_resource2) { + if (_resource4) { { - var originallyImplicit = _resource2._dev_implicit_construction === true; + var originallyImplicit = _resource4._dev_implicit_construction === true; var latestProps = preloadPropsFromRawProps(href, as, props); - validatePreloadResourceDifference(_resource2.props, originallyImplicit, latestProps, false); + validatePreloadResourceDifference(_resource4.props, originallyImplicit, latestProps, false); } } else { - _resource2 = createPreloadResource(resources, href, as, preloadPropsFromRawProps(href, as, props)); + _resource4 = createPreloadResource(resources, href, as, preloadPropsFromRawProps(href, as, props)); switch (as) { case 'script': { - resources.explicitScriptPreloads.add(_resource2); + resources.explicitScriptPreloads.add(_resource4); break; } case 'style': { - resources.explicitStylePreloads.add(_resource2); + resources.explicitStylePreloads.add(_resource4); break; } case 'font': { - resources.fontPreloads.add(_resource2); + resources.fontPreloads.add(_resource4); break; } } @@ -2579,11 +2686,43 @@ function resourcesFromLink(props) { } } - return false; + break; } } - return false; + if (props.onLoad || props.onError) { + return false; + } + + var sizes = typeof props.sizes === 'string' ? props.sizes : ''; + var media = typeof props.media === 'string' ? props.media : ''; + key = 'rel:' + rel + '::href:' + href + '::sizes:' + sizes + '::media:' + media; + var resource = resources.headsMap.get(key); + + if (!resource) { + resource = { + type: 'link', + props: assign({}, props), + flushed: false + }; + resources.headsMap.set(key, resource); + + switch (rel) { + case 'preconnect': + case 'dns-prefetch': + { + resources.preconnects.add(resource); + break; + } + + default: + { + resources.headResources.add(resource); + } + } + } + + return true; } // Construct a resource from link props. function resourcesFromScript(props) { @@ -3505,6 +3644,22 @@ function pushStartTextArea(target, props, responseState) { return null; } +function pushMeta(target, props, responseState, textEmbedded) { + if ( resourcesFromElement('meta', props)) { + if (textEmbedded) { + // This link follows text but we aren't writing a tag. while not as efficient as possible we need + // to be safe and assume text will follow by inserting a textSeparator + target.push(textSeparator); + } // We have converted this link exclusively to a resource and no longer + // need to emit it + + + return null; + } + + return pushSelfClosing(target, props, 'meta', responseState); +} + function pushLink(target, props, responseState, textEmbedded) { if ( resourcesFromLink(props)) { if (textEmbedded) { @@ -3615,7 +3770,30 @@ function pushStartMenuItem(target, props, responseState) { return null; } -function pushStartTitle(target, props, responseState) { +function pushTitle(target, props, responseState) { + { + var children = props.children; + var childForValidation = Array.isArray(children) && children.length < 2 ? children[0] || null : children; + + if (Array.isArray(children) && children.length > 1) { + error('A title element received an array with more than 1 element as children. ' + 'In browsers title Elements can only have Text Nodes as children. If ' + 'the children being rendered output more than a single text node in aggregate the browser ' + 'will display markup and comments as text in the title and hydration will likely fail and ' + 'fall back to client rendering'); + } else if (childForValidation != null && childForValidation.$$typeof != null) { + error('A title element received a React element for children. ' + 'In the browser title Elements can only have Text Nodes as children. If ' + 'the children being rendered output more than a single text node in aggregate the browser ' + 'will display markup and comments as text in the title and hydration will likely fail and ' + 'fall back to client rendering'); + } else if (childForValidation != null && typeof childForValidation !== 'string' && typeof childForValidation !== 'number') { + error('A title element received a value that was not a string or number for children. ' + 'In the browser title Elements can only have Text Nodes as children. If ' + 'the children being rendered output more than a single text node in aggregate the browser ' + 'will display markup and comments as text in the title and hydration will likely fail and ' + 'fall back to client rendering'); + } + } + + if ( resourcesFromElement('title', props)) { + // We have converted this link exclusively to a resource and no longer + // need to emit it + return null; + } + + return pushTitleImpl(target, props, responseState); +} + +function pushTitleImpl(target, props, responseState) { target.push(startChunkForTag('title')); var children = null; @@ -3644,20 +3822,14 @@ function pushStartTitle(target, props, responseState) { } target.push(endOfStartTag); + var child = Array.isArray(children) && children.length < 2 ? children[0] || null : children; - { - var child = Array.isArray(children) && children.length < 2 ? children[0] || null : children; - - if (Array.isArray(children) && children.length > 1) { - error('A title element received an array with more than 1 element as children. ' + 'In browsers title Elements can only have Text Nodes as children. If ' + 'the children being rendered output more than a single text node in aggregate the browser ' + 'will display markup and comments as text in the title and hydration will likely fail and ' + 'fall back to client rendering'); - } else if (child != null && child.$$typeof != null) { - error('A title element received a React element for children. ' + 'In the browser title Elements can only have Text Nodes as children. If ' + 'the children being rendered output more than a single text node in aggregate the browser ' + 'will display markup and comments as text in the title and hydration will likely fail and ' + 'fall back to client rendering'); - } else if (child != null && typeof child !== 'string' && typeof child !== 'number') { - error('A title element received a value that was not a string or number for children. ' + 'In the browser title Elements can only have Text Nodes as children. If ' + 'the children being rendered output more than a single text node in aggregate the browser ' + 'will display markup and comments as text in the title and hydration will likely fail and ' + 'fall back to client rendering'); - } + if (typeof child === 'string' || typeof child === 'number') { + target.push(stringToChunk(escapeTextForBrowser(child))); } - return children; + target.push(endTag1, stringToChunk('title'), endTag2); + return null; } function pushStartHead(target, preamble, props, tag, responseState) { @@ -3677,7 +3849,7 @@ function pushStartHtml(target, preamble, props, tag, responseState, formatContex return pushStartGenericElement(target, props, tag, responseState); } -function pushStartScript(target, props, responseState, textEmbedded) { +function pushScript(target, props, responseState, textEmbedded) { if ( resourcesFromScript(props)) { if (textEmbedded) { // This link follows text but we aren't writing a tag. while not as efficient as possible we need @@ -3690,7 +3862,56 @@ function pushStartScript(target, props, responseState, textEmbedded) { return null; } - return pushStartGenericElement(target, props, 'script', responseState); + return pushScriptImpl(target, props, responseState); +} + +function pushScriptImpl(target, props, responseState) { + target.push(startChunkForTag('script')); + var children = null; + var innerHTML = null; + + for (var propKey in props) { + if (hasOwnProperty.call(props, propKey)) { + var propValue = props[propKey]; + + if (propValue == null) { + continue; + } + + switch (propKey) { + case 'children': + children = propValue; + break; + + case 'dangerouslySetInnerHTML': + innerHTML = propValue; + break; + + default: + pushAttribute(target, responseState, propKey, propValue); + break; + } + } + } + + target.push(endOfStartTag); + + { + if (children != null && typeof children !== 'string') { + var descriptiveStatement = typeof children === 'number' ? 'a number for children' : Array.isArray(children) ? 'an array for children' : 'something unexpected for children'; + + error('A script element was rendered with %s. If script element has children it must be a single string.' + ' Consider using dangerouslySetInnerHTML or passing a plain string as children.', descriptiveStatement); + } + } + + pushInnerHTML(target, innerHTML, children); + + if (typeof children === 'string') { + target.push(stringToChunk(encodeHTMLTextNode(children))); + } + + target.push(endTag1, stringToChunk('script'), endTag2); + return null; } function pushStartGenericElement(target, props, tag, responseState) { @@ -3935,13 +4156,16 @@ function pushStartInstance(target, preamble, type, props, responseState, formatC return pushStartMenuItem(target, props, responseState); case 'title': - return pushStartTitle(target, props, responseState); + return pushTitle(target, props, responseState) ; case 'link': return pushLink(target, props, responseState, textEmbedded); case 'script': - return pushStartScript(target, props, responseState, textEmbedded); + return pushScript(target, props, responseState, textEmbedded) ; + + case 'meta': + return pushMeta(target, props, responseState, textEmbedded); // Newline eating tags case 'listing': @@ -3959,7 +4183,6 @@ function pushStartInstance(target, preamble, type, props, responseState, formatC case 'hr': case 'img': case 'keygen': - case 'meta': case 'param': case 'source': case 'track': @@ -4007,9 +4230,16 @@ var endTag1 = stringToPrecomputedChunk(''); function pushEndInstance(target, postamble, type, props) { switch (type) { + // When float is on we expect title and script tags to always be pushed in + // a unit and never return children. when we end up pushing the end tag we + // want to ensure there is no extra closing tag pushed + case 'title': + case 'script': // Omitted close tags // TODO: Instead of repeating this switch we could try to pass a flag from above. // That would require returning a tuple. Which might be ok if it gets inlined. + // eslint-disable-next-line-no-fallthrough + case 'area': case 'base': case 'br': @@ -4460,13 +4690,29 @@ function writeInitialResources(destination, resources, responseState) { } var target = []; - var fontPreloads = resources.fontPreloads, + var charset = resources.charset, + preconnects = resources.preconnects, + fontPreloads = resources.fontPreloads, precedences = resources.precedences, usedStylePreloads = resources.usedStylePreloads, scripts = resources.scripts, usedScriptPreloads = resources.usedScriptPreloads, explicitStylePreloads = resources.explicitStylePreloads, - explicitScriptPreloads = resources.explicitScriptPreloads; + explicitScriptPreloads = resources.explicitScriptPreloads, + headResources = resources.headResources; + + if (charset) { + pushSelfClosing(target, charset.props, 'meta', responseState); + charset.flushed = true; + resources.charset = null; + } + + preconnects.forEach(function (r) { + // font preload Resources should not already be flushed so we elide this check + pushLinkImpl(target, r.props, responseState); + r.flushed = true; + }); + preconnects.clear(); fontPreloads.forEach(function (r) { // font preload Resources should not already be flushed so we elide this check pushLinkImpl(target, r.props, responseState); @@ -4485,15 +4731,14 @@ function writeInitialResources(destination, resources, responseState) { }); p.clear(); } else { - target.push(precedencePlaceholderStart, escapeTextForBrowser(stringToChunk(precedence)), precedencePlaceholderEnd); + target.push(precedencePlaceholderStart, stringToChunk(escapeTextForBrowser(precedence)), precedencePlaceholderEnd); } }); usedStylePreloads.forEach(flushLinkResource); usedStylePreloads.clear(); scripts.forEach(function (r) { // should never be flushed already - pushStartGenericElement(target, r.props, 'script', responseState); - pushEndInstance(target, target, 'script', r.props); + pushScriptImpl(target, r.props, responseState); r.flushed = true; r.hint.flushed = true; }); @@ -4504,6 +4749,30 @@ function writeInitialResources(destination, resources, responseState) { explicitStylePreloads.clear(); explicitScriptPreloads.forEach(flushLinkResource); explicitScriptPreloads.clear(); + headResources.forEach(function (r) { + switch (r.type) { + case 'title': + { + pushTitleImpl(target, r.props, responseState); + break; + } + + case 'meta': + { + pushSelfClosing(target, r.props, 'meta', responseState); + break; + } + + case 'link': + { + pushLinkImpl(target, r.props, responseState); + break; + } + } + + r.flushed = true; + }); + headResources.clear(); var i; var r = true; @@ -4526,12 +4795,28 @@ function writeImmediateResources(destination, resources, responseState) { } var target = []; - var fontPreloads = resources.fontPreloads, + var charset = resources.charset, + preconnects = resources.preconnects, + fontPreloads = resources.fontPreloads, usedStylePreloads = resources.usedStylePreloads, scripts = resources.scripts, usedScriptPreloads = resources.usedScriptPreloads, explicitStylePreloads = resources.explicitStylePreloads, - explicitScriptPreloads = resources.explicitScriptPreloads; + explicitScriptPreloads = resources.explicitScriptPreloads, + headResources = resources.headResources; + + if (charset) { + pushSelfClosing(target, charset.props, 'meta', responseState); + charset.flushed = true; + resources.charset = null; + } + + preconnects.forEach(function (r) { + // font preload Resources should not already be flushed so we elide this check + pushLinkImpl(target, r.props, responseState); + r.flushed = true; + }); + preconnects.clear(); fontPreloads.forEach(function (r) { // font preload Resources should not already be flushed so we elide this check pushLinkImpl(target, r.props, responseState); @@ -4554,6 +4839,30 @@ function writeImmediateResources(destination, resources, responseState) { explicitStylePreloads.clear(); explicitScriptPreloads.forEach(flushLinkResource); explicitScriptPreloads.clear(); + headResources.forEach(function (r) { + switch (r.type) { + case 'title': + { + pushTitleImpl(target, r.props, responseState); + break; + } + + case 'meta': + { + pushSelfClosing(target, r.props, 'meta', responseState); + break; + } + + case 'link': + { + pushLinkImpl(target, r.props, responseState); + break; + } + } + + r.flushed = true; + }); + headResources.clear(); var i; var r = true; @@ -6270,12 +6579,8 @@ function createThenableState() { // suspends again, we'll reuse the same state. return []; } -function trackSuspendedWakeable(wakeable) { - // If this wakeable isn't already a thenable, turn it into one now. Then, - // when we resume the work loop, we can check if its status is - // still pending. - // TODO: Get rid of the Wakeable type? It's superseded by UntrackedThenable. - var thenable = wakeable; // We use an expando to track the status and result of a thenable so that we +function trackUsedThenable(thenableState, thenable, index) { + thenableState[index] = thenable; // 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. // @@ -6320,11 +6625,6 @@ function trackSuspendedWakeable(wakeable) { } } } -function trackUsedThenable(thenableState, thenable, index) { - // This is only a separate function from trackSuspendedWakeable for symmetry - // with Fiber. - thenableState[index] = thenable; -} function getPreviouslyUsedThenableAtIndex(thenableState, index) { if (thenableState !== null) { var thenable = thenableState[index]; @@ -6750,6 +7050,7 @@ function throwOnUseEventCall() { } function useEvent(callback) { + // $FlowIgnore[incompatible-return] return throwOnUseEventCall; } // TODO Decide on how to implement this hook for server rendering. // If a mutation occurs during render, consider triggering a Suspense boundary @@ -6803,7 +7104,7 @@ function use(usable) { var thenable = usable; // Track the position of the thenable within this fiber. var index = thenableIndexCounter; - thenableIndexCounter += 1; + thenableIndexCounter += 1; // TODO: Unify this switch statement with the one in trackUsedThenable. switch (thenable.status) { case 'fulfilled': @@ -6823,6 +7124,12 @@ function use(usable) { var prevThenableAtIndex = getPreviouslyUsedThenableAtIndex(thenableState, index); if (prevThenableAtIndex !== null) { + if (thenable !== prevThenableAtIndex) { + // Avoid an unhandled rejection errors for the Promises that we'll + // intentionally ignore. + thenable.then(noop, noop); + } + switch (prevThenableAtIndex.status) { case 'fulfilled': { @@ -7955,7 +8262,6 @@ function spawnNewSuspendedTask(request, task, thenableState, x) { segment.lastPushedText = false; var newTask = createTask(request, thenableState, task.node, task.blockedBoundary, newSegment, task.abortSet, task.legacyContext, task.context, task.treeContext); - trackSuspendedWakeable(x); { if (task.componentStack !== null) { @@ -8277,8 +8583,6 @@ function retryTask(request, task) { // Something suspended again, let's pick it back up later. var ping = task.ping; x.then(ping, ping); - var wakeable = x; - trackSuspendedWakeable(wakeable); task.thenableState = getThenableStateAfterSuspending(); } else { task.abortSet.delete(task); diff --git a/packages/next/compiled/react-dom/cjs/react-dom-server-legacy.browser.production.min.js b/packages/next/compiled/react-dom/cjs/react-dom-server-legacy.browser.production.min.js index 9a2ff6f7d6467..3089b24a82328 100644 --- a/packages/next/compiled/react-dom/cjs/react-dom-server-legacy.browser.production.min.js +++ b/packages/next/compiled/react-dom/cjs/react-dom-server-legacy.browser.production.min.js @@ -7,110 +7,115 @@ * 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 aa=require("react"),ba=require("react-dom");function m(a){for(var b="https://reactjs.org/docs/error-decoder.html?invariant="+a,c=1;c