diff --git a/package.json b/package.json
index 7e0d87687836d..1aa811421fd79 100644
--- a/package.json
+++ b/package.json
@@ -192,14 +192,14 @@
"random-seed": "0.3.0",
"react": "18.2.0",
"react-17": "npm:react@17.0.2",
- "react-builtin": "npm:react@18.3.0-canary-09285d5a7-20230925",
+ "react-builtin": "npm:react@18.3.0-canary-d900fadbf-20230929",
"react-dom": "18.2.0",
"react-dom-17": "npm:react-dom@17.0.2",
- "react-dom-builtin": "npm:react-dom@18.3.0-canary-09285d5a7-20230925",
- "react-dom-experimental-builtin": "npm:react-dom@0.0.0-experimental-09285d5a7-20230925",
- "react-experimental-builtin": "npm:react@0.0.0-experimental-09285d5a7-20230925",
- "react-server-dom-webpack": "18.3.0-canary-09285d5a7-20230925",
- "react-server-dom-webpack-experimental": "npm:react-server-dom-webpack@0.0.0-experimental-09285d5a7-20230925",
+ "react-dom-builtin": "npm:react-dom@18.3.0-canary-d900fadbf-20230929",
+ "react-dom-experimental-builtin": "npm:react-dom@0.0.0-experimental-d900fadbf-20230929",
+ "react-experimental-builtin": "npm:react@0.0.0-experimental-d900fadbf-20230929",
+ "react-server-dom-webpack": "18.3.0-canary-d900fadbf-20230929",
+ "react-server-dom-webpack-experimental": "npm:react-server-dom-webpack@0.0.0-experimental-d900fadbf-20230929",
"react-ssr-prepass": "1.0.8",
"react-virtualized": "9.22.3",
"relay-compiler": "13.0.2",
@@ -209,8 +209,8 @@
"resolve-from": "5.0.0",
"sass": "1.54.0",
"satori": "0.10.6",
- "scheduler-builtin": "npm:scheduler@0.24.0-canary-09285d5a7-20230925",
- "scheduler-experimental-builtin": "npm:scheduler@0.0.0-experimental-09285d5a7-20230925",
+ "scheduler-builtin": "npm:scheduler@0.24.0-canary-d900fadbf-20230929",
+ "scheduler-experimental-builtin": "npm:scheduler@0.0.0-experimental-d900fadbf-20230929",
"seedrandom": "3.0.5",
"selenium-webdriver": "4.0.0-beta.4",
"semver": "7.3.7",
diff --git a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.development.js b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.development.js
index 63b0610dee700..d19392ed8d2cd 100644
--- a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.development.js
+++ b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-legacy.browser.development.js
@@ -17,7 +17,7 @@ if (process.env.NODE_ENV !== "production") {
var React = require("next/dist/compiled/react-experimental");
var ReactDOM = require('react-dom');
-var ReactVersion = '18.3.0-experimental-09285d5a7-20230925';
+var ReactVersion = '18.3.0-experimental-d900fadbf-20230929';
var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
@@ -1651,9 +1651,27 @@ var SentFormReplayingRuntime
= 16; // Per request, global state that is not contextual to the rendering subtree.
// This cannot be resumed and therefore should only contain things that are
// temporary working state or are never used in the prerender pass.
-// Per response, global state that is not contextual to the rendering subtree.
+// Credentials here are things that affect whether a browser will make a request
+// as well as things that affect which connection the browser will use for that request.
+// We want these to be aligned across preloads and resources because otherwise the preload
+// will be wasted.
+// We investigated whether referrerPolicy should be included here but from experimentation
+// it seems that browsers do not treat this as part of the http cache key and does not affect
+// which connection is used.
+
+var EXISTS = null; // This constant is to mark preloads that have no unique credentials
+// to convey. It should never be checked by identity and we should not
+// assume Preload values in ResumableState equal this value because they
+// will have come from some parsed input.
+
+var PRELOAD_NO_CREDS = [];
+
+{
+ Object.freeze(PRELOAD_NO_CREDS);
+} // Per response, global state that is not contextual to the rendering subtree.
// This is resumable and therefore should be serializable.
+
var dataElementQuotedEnd = stringToPrecomputedChunk('">');
var startInlineScript = stringToPrecomputedChunk('');
@@ -1760,12 +1778,16 @@ function createRenderState$1(resumableState, nonce, bootstrapScriptContent, boot
fontPreloads: new Set(),
highImagePreloads: new Set(),
// usedImagePreloads: new Set(),
- precedences: new Map(),
- stylePrecedences: new Map(),
+ styles: new Map(),
bootstrapScripts: new Set(),
scripts: new Set(),
bulkPreloads: new Set(),
- preloadsMap: new Map(),
+ preloads: {
+ images: new Map(),
+ stylesheets: new Map(),
+ scripts: new Map(),
+ moduleScripts: new Map()
+ },
nonce: nonce,
// like a module global for currently rendering boundary
boundaryResources: null,
@@ -1775,17 +1797,32 @@ function createRenderState$1(resumableState, nonce, bootstrapScriptContent, boot
if (bootstrapScripts !== undefined) {
for (var i = 0; i < bootstrapScripts.length; i++) {
var scriptConfig = bootstrapScripts[i];
- var src = typeof scriptConfig === 'string' ? scriptConfig : scriptConfig.src;
- var integrity = typeof scriptConfig === 'string' ? undefined : scriptConfig.integrity;
- var crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
- preloadBootstrapScript(resumableState, renderState, src, nonce, integrity, crossOrigin);
+ var src = void 0,
+ crossOrigin = void 0,
+ integrity = void 0;
+ var props = {
+ rel: 'preload',
+ as: 'script',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
+
+ if (typeof scriptConfig === 'string') {
+ props.href = src = scriptConfig;
+ } else {
+ props.href = src = scriptConfig.src;
+ props.integrity = integrity = typeof scriptConfig.integrity === 'string' ? scriptConfig.integrity : undefined;
+ props.crossOrigin = crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
+
+ preloadBootstrapScriptOrModule(resumableState, renderState, src, props);
bootstrapChunks.push(startScriptSrc, stringToChunk(escapeTextForBrowser(src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (integrity) {
+ if (typeof integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(integrity)));
}
@@ -1801,20 +1838,32 @@ function createRenderState$1(resumableState, nonce, bootstrapScriptContent, boot
for (var _i = 0; _i < bootstrapModules.length; _i++) {
var _scriptConfig = bootstrapModules[_i];
- var _src = typeof _scriptConfig === 'string' ? _scriptConfig : _scriptConfig.src;
+ var _src = void 0,
+ _crossOrigin = void 0,
+ _integrity = void 0;
- var _integrity = typeof _scriptConfig === 'string' ? undefined : _scriptConfig.integrity;
+ var _props = {
+ rel: 'modulepreload',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
- var _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ if (typeof _scriptConfig === 'string') {
+ _props.href = _src = _scriptConfig;
+ } else {
+ _props.href = _src = _scriptConfig.src;
+ _props.integrity = _integrity = typeof _scriptConfig.integrity === 'string' ? _scriptConfig.integrity : undefined;
+ _props.crossOrigin = _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
- preloadBootstrapModule(resumableState, renderState, _src, nonce, _integrity, _crossOrigin);
+ preloadBootstrapScriptOrModule(resumableState, renderState, _src, _props);
bootstrapChunks.push(startModuleSrc, stringToChunk(escapeTextForBrowser(_src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (_integrity) {
+ if (typeof _integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(_integrity)));
}
@@ -1847,10 +1896,18 @@ function createResumableState(identifierPrefix, externalRuntimeConfig) {
hasHtml: false,
// @TODO add bootstrap script to implicit preloads
// persistent
- preloadsMap: {},
- preconnectsMap: {},
- stylesMap: {},
- scriptsMap: {}
+ unknownResources: {},
+ dnsResources: {},
+ connectResources: {
+ default: {},
+ anonymous: {},
+ credentials: {}
+ },
+ imageResources: {},
+ styleResources: {},
+ scriptResources: {},
+ moduleUnknownResources: {},
+ moduleScriptResources: {}
};
} // Constants for the insertion mode we're currently writing in. We don't encode all HTML5 insertion
// modes. We only include the variants as they matter for the sake of our purposes.
@@ -3208,7 +3265,7 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
if (props.rel === 'stylesheet') {
// This may hoistable as a Stylesheet Resource, otherwise it will emit in place
- var key = getResourceKey('style', href);
+ var key = getResourceKey(href);
if (typeof precedence !== 'string' || props.disabled != null || props.onLoad || props.onError) {
// This stylesheet is either not opted into Resource semantics or has conflicting properties which
@@ -3229,67 +3286,58 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
return pushLinkImpl(target, props);
} else {
// This stylesheet refers to a Resource and we create a new one if necessary
- var stylesInPrecedence = renderState.precedences.get(precedence);
-
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- var resourceProps = stylesheetPropsFromRawProps(props);
- var state = NoState;
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
+
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS; // If this is the first time we've encountered this precedence we need
+ // to create a StyleQueue
+
+ if (!styleQueue) {
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
+ }
- if (resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = resumableState.preloadsMap[key];
- adoptPreloadPropsForStylesheetProps(resourceProps, preloadProps);
- var preloadResource = renderState.preloadsMap.get(key);
+ var resource = {
+ state: PENDING$1,
+ props: stylesheetPropsFromRawProps(props)
+ };
- if (preloadResource) {
- // If we already had a preload we don't want that resource to flush directly.
- // We let the newly created resource govern flushing.
- preloadResource.state |= Blocked;
+ if (resourceState) {
+ // When resourceState is truty it is a Preload state. We cast it for clarity
+ var preloadState = resourceState;
- if (preloadResource.state & Flushed) {
- state = PreloadFlushed;
- }
- } else {
- // If we resumed then we assume that this was already flushed
- // by the shell.
- state = PreloadFlushed;
+ if (preloadState.length === 2) {
+ adoptPreloadCredentials(resource.props, preloadState);
}
- }
- var resource = {
- type: 'stylesheet',
- chunks: [],
- state: state,
- props: resourceProps
- };
- resumableState.stylesMap[key] = null;
-
- if (!stylesInPrecedence) {
- stylesInPrecedence = new Map();
- renderState.precedences.set(precedence, stylesInPrecedence);
- var emptyStyleResource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: []
- }
- };
- stylesInPrecedence.set('', emptyStyleResource);
+ var preloadResource = renderState.preloads.stylesheets.get(key);
- {
- if (renderState.stylePrecedences.has(precedence)) {
- error('React constructed an empty style resource when a style resource already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ if (preloadResource && preloadResource.length > 0) {
+ // The Preload for this resource was created in this render pass and has not flushed yet so
+ // we need to clear it to avoid it flushing.
+ preloadResource.length = 0;
+ } else {
+ // Either the preload resource from this render already flushed in this render pass
+ // or the preload flushed in a prior pass (prerender). In either case we need to mark
+ // this resource as already having been preloaded.
+ resource.state = PRELOADED;
}
+ } // We add the newly created resource to our StyleQueue and if necessary
+ // track the resource with the currently rendering boundary
- renderState.stylePrecedences.set(precedence, emptyStyleResource);
- }
- stylesInPrecedence.set(key, resource);
+ styleQueue.sheets.set(key, resource);
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.stylesheets.add(resource);
}
} else {
// We need to track whether this boundary should wait on this resource or not.
@@ -3297,12 +3345,12 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
- if (stylesInPrecedence) {
- var _resource = stylesInPrecedence.get(key);
+ if (styleQueue) {
+ var _resource = styleQueue.sheets.get(key);
if (_resource) {
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(_resource);
+ renderState.boundaryResources.stylesheets.add(_resource);
}
}
}
@@ -3400,47 +3448,47 @@ function pushStyle(target, props, resumableState, renderState, textEmbedded, ins
}
}
- var key = getResourceKey('style', href);
- var resource = renderState.stylePrecedences.get(precedence);
+ var key = getResourceKey(href);
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- if (!resource) {
- resource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: [href]
- }
- };
- renderState.stylePrecedences.set(precedence, resource);
- var stylesInPrecedence = new Map();
- stylesInPrecedence.set('', resource);
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS;
- {
- if (renderState.precedences.has(precedence)) {
- error('React constructed a new style precedence set when one already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ {
+ if (resourceState) {
+ error('React encountered a hoistable style tag for the same href as a preload: "%s". When using a style tag to inline styles you should not also preload it as a stylsheet.', href);
}
+ }
- renderState.precedences.set(precedence, stylesInPrecedence);
+ if (!styleQueue) {
+ // This is the first time we've encountered this precedence we need
+ // to create a StyleQueue.
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [stringToChunk(escapeTextForBrowser(href))],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
} else {
- resource.props.hrefs.push(href);
+ // We have seen this precedence before and need to track this href
+ styleQueue.hrefs.push(stringToChunk(escapeTextForBrowser(href)));
}
- resumableState.stylesMap[key] = null;
- pushStyleContents(resource.chunks, props);
+ pushStyleContents(styleQueue.rules, props);
}
- if (resource) {
+ if (styleQueue) {
// We need to track whether this boundary should wait on this resource or not.
// Typically this resource should always exist since we either had it or just created
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.styles.add(styleQueue);
}
}
@@ -3529,36 +3577,35 @@ function pushStyleContents(target, props) {
return;
}
-function getImagePreloadKey(href, imageSrcSet, imageSizes) {
- var uniquePart = '';
-
- if (typeof imageSrcSet === 'string' && imageSrcSet !== '') {
- uniquePart += '[' + imageSrcSet + ']';
-
- if (typeof imageSizes === 'string') {
- uniquePart += '[' + imageSizes + ']';
- }
- } else {
- uniquePart += '[][]' + href;
- }
-
- return getResourceKey('image', uniquePart);
-}
-
function pushImg(target, props, resumableState, renderState, pictureTagInScope) {
var src = props.src,
srcSet = props.srcSet;
- if (props.loading !== 'lazy' && (typeof src === 'string' || typeof srcSet === 'string') && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
+ if (props.loading !== 'lazy' && (src || srcSet) && (typeof src === 'string' || src == null) && (typeof srcSet === 'string' || srcSet == null) && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
!(typeof src === 'string' && src[4] === ':' && (src[0] === 'd' || src[0] === 'D') && (src[1] === 'a' || src[1] === 'A') && (src[2] === 't' || src[2] === 'T') && (src[3] === 'a' || src[3] === 'A')) && !(typeof srcSet === 'string' && srcSet[4] === ':' && (srcSet[0] === 'd' || srcSet[0] === 'D') && (srcSet[1] === 'a' || srcSet[1] === 'A') && (srcSet[2] === 't' || srcSet[2] === 'T') && (srcSet[3] === 'a' || srcSet[3] === 'A'))) {
// We have a suspensey image and ought to preload it to optimize the loading of display blocking
// resumableState.
- var sizes = props.sizes;
- var key = getImagePreloadKey(src, srcSet, sizes);
- var resource;
+ var sizes = typeof props.sizes === 'string' ? props.sizes : undefined;
+ var key = getImageResourceKey(src, srcSet, sizes);
+ var promotablePreloads = renderState.preloads.images;
+ var resource = promotablePreloads.get(key);
+
+ if (resource) {
+ // We consider whether this preload can be promoted to higher priority flushing queue.
+ // The only time a resource will exist here is if it was created during this render
+ // and was not already in the high priority queue.
+ if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
+ // Delete the resource from the map since we are promoting it and don't want to
+ // reenter this branch in a second pass for duplicate img hrefs.
+ promotablePreloads.delete(key); // $FlowFixMe - Flow should understand that this is a Resource if the condition was true
- if (!resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = {
+ renderState.highImagePreloads.add(resource);
+ }
+ } else if (!resumableState.imageResources.hasOwnProperty(key)) {
+ // We must construct a new preload resource
+ resumableState.imageResources[key] = PRELOAD_NO_CREDS;
+ resource = [];
+ pushLinkImpl(resource, {
rel: 'preload',
as: 'image',
// There is a bug in Safari where imageSrcSet is not respected on preload links
@@ -3573,25 +3620,15 @@ function pushImg(target, props, resumableState, renderState, pictureTagInScope)
type: props.type,
fetchPriority: props.fetchPriority,
referrerPolicy: props.referrerPolicy
- };
- resource = {
- type: 'preload',
- chunks: [],
- state: NoState,
- props: preloadProps
- };
- resumableState.preloadsMap[key] = preloadProps;
- renderState.preloadsMap.set(key, resource);
- pushLinkImpl(resource.chunks, preloadProps);
- } else {
- resource = renderState.preloadsMap.get(key);
- }
+ });
- if (resource) {
if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
renderState.highImagePreloads.add(resource);
} else {
- renderState.bulkPreloads.add(resource);
+ renderState.bulkPreloads.add(resource); // We can bump the priority up if the same img is rendered later
+ // with fetchPriority="high"
+
+ promotablePreloads.set(key, resource);
}
}
}
@@ -3765,35 +3802,50 @@ function pushScript(target, props, resumableState, renderState, textEmbedded, in
}
var src = props.src;
- var key = getResourceKey('script', src); // We can make this ');
@@ -1761,12 +1779,16 @@ function createRenderState$1(resumableState, nonce, bootstrapScriptContent, boot
fontPreloads: new Set(),
highImagePreloads: new Set(),
// usedImagePreloads: new Set(),
- precedences: new Map(),
- stylePrecedences: new Map(),
+ styles: new Map(),
bootstrapScripts: new Set(),
scripts: new Set(),
bulkPreloads: new Set(),
- preloadsMap: new Map(),
+ preloads: {
+ images: new Map(),
+ stylesheets: new Map(),
+ scripts: new Map(),
+ moduleScripts: new Map()
+ },
nonce: nonce,
// like a module global for currently rendering boundary
boundaryResources: null,
@@ -1776,17 +1798,32 @@ function createRenderState$1(resumableState, nonce, bootstrapScriptContent, boot
if (bootstrapScripts !== undefined) {
for (var i = 0; i < bootstrapScripts.length; i++) {
var scriptConfig = bootstrapScripts[i];
- var src = typeof scriptConfig === 'string' ? scriptConfig : scriptConfig.src;
- var integrity = typeof scriptConfig === 'string' ? undefined : scriptConfig.integrity;
- var crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
- preloadBootstrapScript(resumableState, renderState, src, nonce, integrity, crossOrigin);
+ var src = void 0,
+ crossOrigin = void 0,
+ integrity = void 0;
+ var props = {
+ rel: 'preload',
+ as: 'script',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
+
+ if (typeof scriptConfig === 'string') {
+ props.href = src = scriptConfig;
+ } else {
+ props.href = src = scriptConfig.src;
+ props.integrity = integrity = typeof scriptConfig.integrity === 'string' ? scriptConfig.integrity : undefined;
+ props.crossOrigin = crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
+
+ preloadBootstrapScriptOrModule(resumableState, renderState, src, props);
bootstrapChunks.push(startScriptSrc, stringToChunk(escapeTextForBrowser(src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (integrity) {
+ if (typeof integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(integrity)));
}
@@ -1802,20 +1839,32 @@ function createRenderState$1(resumableState, nonce, bootstrapScriptContent, boot
for (var _i = 0; _i < bootstrapModules.length; _i++) {
var _scriptConfig = bootstrapModules[_i];
- var _src = typeof _scriptConfig === 'string' ? _scriptConfig : _scriptConfig.src;
+ var _src = void 0,
+ _crossOrigin = void 0,
+ _integrity = void 0;
- var _integrity = typeof _scriptConfig === 'string' ? undefined : _scriptConfig.integrity;
+ var _props = {
+ rel: 'modulepreload',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
- var _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ if (typeof _scriptConfig === 'string') {
+ _props.href = _src = _scriptConfig;
+ } else {
+ _props.href = _src = _scriptConfig.src;
+ _props.integrity = _integrity = typeof _scriptConfig.integrity === 'string' ? _scriptConfig.integrity : undefined;
+ _props.crossOrigin = _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
- preloadBootstrapModule(resumableState, renderState, _src, nonce, _integrity, _crossOrigin);
+ preloadBootstrapScriptOrModule(resumableState, renderState, _src, _props);
bootstrapChunks.push(startModuleSrc, stringToChunk(escapeTextForBrowser(_src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (_integrity) {
+ if (typeof _integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(_integrity)));
}
@@ -1848,10 +1897,18 @@ function createResumableState(identifierPrefix, externalRuntimeConfig) {
hasHtml: false,
// @TODO add bootstrap script to implicit preloads
// persistent
- preloadsMap: {},
- preconnectsMap: {},
- stylesMap: {},
- scriptsMap: {}
+ unknownResources: {},
+ dnsResources: {},
+ connectResources: {
+ default: {},
+ anonymous: {},
+ credentials: {}
+ },
+ imageResources: {},
+ styleResources: {},
+ scriptResources: {},
+ moduleUnknownResources: {},
+ moduleScriptResources: {}
};
} // Constants for the insertion mode we're currently writing in. We don't encode all HTML5 insertion
// modes. We only include the variants as they matter for the sake of our purposes.
@@ -3209,7 +3266,7 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
if (props.rel === 'stylesheet') {
// This may hoistable as a Stylesheet Resource, otherwise it will emit in place
- var key = getResourceKey('style', href);
+ var key = getResourceKey(href);
if (typeof precedence !== 'string' || props.disabled != null || props.onLoad || props.onError) {
// This stylesheet is either not opted into Resource semantics or has conflicting properties which
@@ -3230,67 +3287,58 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
return pushLinkImpl(target, props);
} else {
// This stylesheet refers to a Resource and we create a new one if necessary
- var stylesInPrecedence = renderState.precedences.get(precedence);
-
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- var resourceProps = stylesheetPropsFromRawProps(props);
- var state = NoState;
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
+
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS; // If this is the first time we've encountered this precedence we need
+ // to create a StyleQueue
+
+ if (!styleQueue) {
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
+ }
- if (resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = resumableState.preloadsMap[key];
- adoptPreloadPropsForStylesheetProps(resourceProps, preloadProps);
- var preloadResource = renderState.preloadsMap.get(key);
+ var resource = {
+ state: PENDING$1,
+ props: stylesheetPropsFromRawProps(props)
+ };
- if (preloadResource) {
- // If we already had a preload we don't want that resource to flush directly.
- // We let the newly created resource govern flushing.
- preloadResource.state |= Blocked;
+ if (resourceState) {
+ // When resourceState is truty it is a Preload state. We cast it for clarity
+ var preloadState = resourceState;
- if (preloadResource.state & Flushed) {
- state = PreloadFlushed;
- }
- } else {
- // If we resumed then we assume that this was already flushed
- // by the shell.
- state = PreloadFlushed;
+ if (preloadState.length === 2) {
+ adoptPreloadCredentials(resource.props, preloadState);
}
- }
- var resource = {
- type: 'stylesheet',
- chunks: [],
- state: state,
- props: resourceProps
- };
- resumableState.stylesMap[key] = null;
-
- if (!stylesInPrecedence) {
- stylesInPrecedence = new Map();
- renderState.precedences.set(precedence, stylesInPrecedence);
- var emptyStyleResource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: []
- }
- };
- stylesInPrecedence.set('', emptyStyleResource);
+ var preloadResource = renderState.preloads.stylesheets.get(key);
- {
- if (renderState.stylePrecedences.has(precedence)) {
- error('React constructed an empty style resource when a style resource already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ if (preloadResource && preloadResource.length > 0) {
+ // The Preload for this resource was created in this render pass and has not flushed yet so
+ // we need to clear it to avoid it flushing.
+ preloadResource.length = 0;
+ } else {
+ // Either the preload resource from this render already flushed in this render pass
+ // or the preload flushed in a prior pass (prerender). In either case we need to mark
+ // this resource as already having been preloaded.
+ resource.state = PRELOADED;
}
+ } // We add the newly created resource to our StyleQueue and if necessary
+ // track the resource with the currently rendering boundary
- renderState.stylePrecedences.set(precedence, emptyStyleResource);
- }
- stylesInPrecedence.set(key, resource);
+ styleQueue.sheets.set(key, resource);
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.stylesheets.add(resource);
}
} else {
// We need to track whether this boundary should wait on this resource or not.
@@ -3298,12 +3346,12 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
- if (stylesInPrecedence) {
- var _resource = stylesInPrecedence.get(key);
+ if (styleQueue) {
+ var _resource = styleQueue.sheets.get(key);
if (_resource) {
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(_resource);
+ renderState.boundaryResources.stylesheets.add(_resource);
}
}
}
@@ -3401,47 +3449,47 @@ function pushStyle(target, props, resumableState, renderState, textEmbedded, ins
}
}
- var key = getResourceKey('style', href);
- var resource = renderState.stylePrecedences.get(precedence);
+ var key = getResourceKey(href);
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- if (!resource) {
- resource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: [href]
- }
- };
- renderState.stylePrecedences.set(precedence, resource);
- var stylesInPrecedence = new Map();
- stylesInPrecedence.set('', resource);
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS;
- {
- if (renderState.precedences.has(precedence)) {
- error('React constructed a new style precedence set when one already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ {
+ if (resourceState) {
+ error('React encountered a hoistable style tag for the same href as a preload: "%s". When using a style tag to inline styles you should not also preload it as a stylsheet.', href);
}
+ }
- renderState.precedences.set(precedence, stylesInPrecedence);
+ if (!styleQueue) {
+ // This is the first time we've encountered this precedence we need
+ // to create a StyleQueue.
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [stringToChunk(escapeTextForBrowser(href))],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
} else {
- resource.props.hrefs.push(href);
+ // We have seen this precedence before and need to track this href
+ styleQueue.hrefs.push(stringToChunk(escapeTextForBrowser(href)));
}
- resumableState.stylesMap[key] = null;
- pushStyleContents(resource.chunks, props);
+ pushStyleContents(styleQueue.rules, props);
}
- if (resource) {
+ if (styleQueue) {
// We need to track whether this boundary should wait on this resource or not.
// Typically this resource should always exist since we either had it or just created
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.styles.add(styleQueue);
}
}
@@ -3530,36 +3578,35 @@ function pushStyleContents(target, props) {
return;
}
-function getImagePreloadKey(href, imageSrcSet, imageSizes) {
- var uniquePart = '';
-
- if (typeof imageSrcSet === 'string' && imageSrcSet !== '') {
- uniquePart += '[' + imageSrcSet + ']';
-
- if (typeof imageSizes === 'string') {
- uniquePart += '[' + imageSizes + ']';
- }
- } else {
- uniquePart += '[][]' + href;
- }
-
- return getResourceKey('image', uniquePart);
-}
-
function pushImg(target, props, resumableState, renderState, pictureTagInScope) {
var src = props.src,
srcSet = props.srcSet;
- if (props.loading !== 'lazy' && (typeof src === 'string' || typeof srcSet === 'string') && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
+ if (props.loading !== 'lazy' && (src || srcSet) && (typeof src === 'string' || src == null) && (typeof srcSet === 'string' || srcSet == null) && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
!(typeof src === 'string' && src[4] === ':' && (src[0] === 'd' || src[0] === 'D') && (src[1] === 'a' || src[1] === 'A') && (src[2] === 't' || src[2] === 'T') && (src[3] === 'a' || src[3] === 'A')) && !(typeof srcSet === 'string' && srcSet[4] === ':' && (srcSet[0] === 'd' || srcSet[0] === 'D') && (srcSet[1] === 'a' || srcSet[1] === 'A') && (srcSet[2] === 't' || srcSet[2] === 'T') && (srcSet[3] === 'a' || srcSet[3] === 'A'))) {
// We have a suspensey image and ought to preload it to optimize the loading of display blocking
// resumableState.
- var sizes = props.sizes;
- var key = getImagePreloadKey(src, srcSet, sizes);
- var resource;
+ var sizes = typeof props.sizes === 'string' ? props.sizes : undefined;
+ var key = getImageResourceKey(src, srcSet, sizes);
+ var promotablePreloads = renderState.preloads.images;
+ var resource = promotablePreloads.get(key);
+
+ if (resource) {
+ // We consider whether this preload can be promoted to higher priority flushing queue.
+ // The only time a resource will exist here is if it was created during this render
+ // and was not already in the high priority queue.
+ if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
+ // Delete the resource from the map since we are promoting it and don't want to
+ // reenter this branch in a second pass for duplicate img hrefs.
+ promotablePreloads.delete(key); // $FlowFixMe - Flow should understand that this is a Resource if the condition was true
- if (!resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = {
+ renderState.highImagePreloads.add(resource);
+ }
+ } else if (!resumableState.imageResources.hasOwnProperty(key)) {
+ // We must construct a new preload resource
+ resumableState.imageResources[key] = PRELOAD_NO_CREDS;
+ resource = [];
+ pushLinkImpl(resource, {
rel: 'preload',
as: 'image',
// There is a bug in Safari where imageSrcSet is not respected on preload links
@@ -3574,25 +3621,15 @@ function pushImg(target, props, resumableState, renderState, pictureTagInScope)
type: props.type,
fetchPriority: props.fetchPriority,
referrerPolicy: props.referrerPolicy
- };
- resource = {
- type: 'preload',
- chunks: [],
- state: NoState,
- props: preloadProps
- };
- resumableState.preloadsMap[key] = preloadProps;
- renderState.preloadsMap.set(key, resource);
- pushLinkImpl(resource.chunks, preloadProps);
- } else {
- resource = renderState.preloadsMap.get(key);
- }
+ });
- if (resource) {
if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
renderState.highImagePreloads.add(resource);
} else {
- renderState.bulkPreloads.add(resource);
+ renderState.bulkPreloads.add(resource); // We can bump the priority up if the same img is rendered later
+ // with fetchPriority="high"
+
+ promotablePreloads.set(key, resource);
}
}
}
@@ -3766,35 +3803,50 @@ function pushScript(target, props, resumableState, renderState, textEmbedded, in
}
var src = props.src;
- var key = getResourceKey('script', src); // We can make this ');
@@ -1840,12 +1858,16 @@ function createRenderState(resumableState, nonce, bootstrapScriptContent, bootst
fontPreloads: new Set(),
highImagePreloads: new Set(),
// usedImagePreloads: new Set(),
- precedences: new Map(),
- stylePrecedences: new Map(),
+ styles: new Map(),
bootstrapScripts: new Set(),
scripts: new Set(),
bulkPreloads: new Set(),
- preloadsMap: new Map(),
+ preloads: {
+ images: new Map(),
+ stylesheets: new Map(),
+ scripts: new Map(),
+ moduleScripts: new Map()
+ },
nonce: nonce,
// like a module global for currently rendering boundary
boundaryResources: null,
@@ -1855,17 +1877,32 @@ function createRenderState(resumableState, nonce, bootstrapScriptContent, bootst
if (bootstrapScripts !== undefined) {
for (var i = 0; i < bootstrapScripts.length; i++) {
var scriptConfig = bootstrapScripts[i];
- var src = typeof scriptConfig === 'string' ? scriptConfig : scriptConfig.src;
- var integrity = typeof scriptConfig === 'string' ? undefined : scriptConfig.integrity;
- var crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
- preloadBootstrapScript(resumableState, renderState, src, nonce, integrity, crossOrigin);
+ var src = void 0,
+ crossOrigin = void 0,
+ integrity = void 0;
+ var props = {
+ rel: 'preload',
+ as: 'script',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
+
+ if (typeof scriptConfig === 'string') {
+ props.href = src = scriptConfig;
+ } else {
+ props.href = src = scriptConfig.src;
+ props.integrity = integrity = typeof scriptConfig.integrity === 'string' ? scriptConfig.integrity : undefined;
+ props.crossOrigin = crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
+
+ preloadBootstrapScriptOrModule(resumableState, renderState, src, props);
bootstrapChunks.push(startScriptSrc, stringToChunk(escapeTextForBrowser(src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (integrity) {
+ if (typeof integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(integrity)));
}
@@ -1881,20 +1918,32 @@ function createRenderState(resumableState, nonce, bootstrapScriptContent, bootst
for (var _i = 0; _i < bootstrapModules.length; _i++) {
var _scriptConfig = bootstrapModules[_i];
- var _src = typeof _scriptConfig === 'string' ? _scriptConfig : _scriptConfig.src;
+ var _src = void 0,
+ _crossOrigin = void 0,
+ _integrity = void 0;
- var _integrity = typeof _scriptConfig === 'string' ? undefined : _scriptConfig.integrity;
+ var _props = {
+ rel: 'modulepreload',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
- var _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ if (typeof _scriptConfig === 'string') {
+ _props.href = _src = _scriptConfig;
+ } else {
+ _props.href = _src = _scriptConfig.src;
+ _props.integrity = _integrity = typeof _scriptConfig.integrity === 'string' ? _scriptConfig.integrity : undefined;
+ _props.crossOrigin = _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
- preloadBootstrapModule(resumableState, renderState, _src, nonce, _integrity, _crossOrigin);
+ preloadBootstrapScriptOrModule(resumableState, renderState, _src, _props);
bootstrapChunks.push(startModuleSrc, stringToChunk(escapeTextForBrowser(_src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (_integrity) {
+ if (typeof _integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(_integrity)));
}
@@ -1931,10 +1980,18 @@ function createResumableState(identifierPrefix, externalRuntimeConfig) {
hasHtml: false,
// @TODO add bootstrap script to implicit preloads
// persistent
- preloadsMap: {},
- preconnectsMap: {},
- stylesMap: {},
- scriptsMap: {}
+ unknownResources: {},
+ dnsResources: {},
+ connectResources: {
+ default: {},
+ anonymous: {},
+ credentials: {}
+ },
+ imageResources: {},
+ styleResources: {},
+ scriptResources: {},
+ moduleUnknownResources: {},
+ moduleScriptResources: {}
};
} // Constants for the insertion mode we're currently writing in. We don't encode all HTML5 insertion
// modes. We only include the variants as they matter for the sake of our purposes.
@@ -3292,7 +3349,7 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
if (props.rel === 'stylesheet') {
// This may hoistable as a Stylesheet Resource, otherwise it will emit in place
- var key = getResourceKey('style', href);
+ var key = getResourceKey(href);
if (typeof precedence !== 'string' || props.disabled != null || props.onLoad || props.onError) {
// This stylesheet is either not opted into Resource semantics or has conflicting properties which
@@ -3313,67 +3370,58 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
return pushLinkImpl(target, props);
} else {
// This stylesheet refers to a Resource and we create a new one if necessary
- var stylesInPrecedence = renderState.precedences.get(precedence);
-
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- var resourceProps = stylesheetPropsFromRawProps(props);
- var state = NoState;
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
+
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS; // If this is the first time we've encountered this precedence we need
+ // to create a StyleQueue
+
+ if (!styleQueue) {
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
+ }
- if (resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = resumableState.preloadsMap[key];
- adoptPreloadPropsForStylesheetProps(resourceProps, preloadProps);
- var preloadResource = renderState.preloadsMap.get(key);
+ var resource = {
+ state: PENDING$1,
+ props: stylesheetPropsFromRawProps(props)
+ };
- if (preloadResource) {
- // If we already had a preload we don't want that resource to flush directly.
- // We let the newly created resource govern flushing.
- preloadResource.state |= Blocked;
+ if (resourceState) {
+ // When resourceState is truty it is a Preload state. We cast it for clarity
+ var preloadState = resourceState;
- if (preloadResource.state & Flushed) {
- state = PreloadFlushed;
- }
- } else {
- // If we resumed then we assume that this was already flushed
- // by the shell.
- state = PreloadFlushed;
+ if (preloadState.length === 2) {
+ adoptPreloadCredentials(resource.props, preloadState);
}
- }
- var resource = {
- type: 'stylesheet',
- chunks: [],
- state: state,
- props: resourceProps
- };
- resumableState.stylesMap[key] = null;
-
- if (!stylesInPrecedence) {
- stylesInPrecedence = new Map();
- renderState.precedences.set(precedence, stylesInPrecedence);
- var emptyStyleResource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: []
- }
- };
- stylesInPrecedence.set('', emptyStyleResource);
+ var preloadResource = renderState.preloads.stylesheets.get(key);
- {
- if (renderState.stylePrecedences.has(precedence)) {
- error('React constructed an empty style resource when a style resource already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ if (preloadResource && preloadResource.length > 0) {
+ // The Preload for this resource was created in this render pass and has not flushed yet so
+ // we need to clear it to avoid it flushing.
+ preloadResource.length = 0;
+ } else {
+ // Either the preload resource from this render already flushed in this render pass
+ // or the preload flushed in a prior pass (prerender). In either case we need to mark
+ // this resource as already having been preloaded.
+ resource.state = PRELOADED;
}
+ } // We add the newly created resource to our StyleQueue and if necessary
+ // track the resource with the currently rendering boundary
- renderState.stylePrecedences.set(precedence, emptyStyleResource);
- }
- stylesInPrecedence.set(key, resource);
+ styleQueue.sheets.set(key, resource);
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.stylesheets.add(resource);
}
} else {
// We need to track whether this boundary should wait on this resource or not.
@@ -3381,12 +3429,12 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
- if (stylesInPrecedence) {
- var _resource = stylesInPrecedence.get(key);
+ if (styleQueue) {
+ var _resource = styleQueue.sheets.get(key);
if (_resource) {
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(_resource);
+ renderState.boundaryResources.stylesheets.add(_resource);
}
}
}
@@ -3484,47 +3532,47 @@ function pushStyle(target, props, resumableState, renderState, textEmbedded, ins
}
}
- var key = getResourceKey('style', href);
- var resource = renderState.stylePrecedences.get(precedence);
+ var key = getResourceKey(href);
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- if (!resource) {
- resource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: [href]
- }
- };
- renderState.stylePrecedences.set(precedence, resource);
- var stylesInPrecedence = new Map();
- stylesInPrecedence.set('', resource);
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS;
- {
- if (renderState.precedences.has(precedence)) {
- error('React constructed a new style precedence set when one already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ {
+ if (resourceState) {
+ error('React encountered a hoistable style tag for the same href as a preload: "%s". When using a style tag to inline styles you should not also preload it as a stylsheet.', href);
}
+ }
- renderState.precedences.set(precedence, stylesInPrecedence);
+ if (!styleQueue) {
+ // This is the first time we've encountered this precedence we need
+ // to create a StyleQueue.
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [stringToChunk(escapeTextForBrowser(href))],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
} else {
- resource.props.hrefs.push(href);
+ // We have seen this precedence before and need to track this href
+ styleQueue.hrefs.push(stringToChunk(escapeTextForBrowser(href)));
}
- resumableState.stylesMap[key] = null;
- pushStyleContents(resource.chunks, props);
+ pushStyleContents(styleQueue.rules, props);
}
- if (resource) {
+ if (styleQueue) {
// We need to track whether this boundary should wait on this resource or not.
// Typically this resource should always exist since we either had it or just created
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.styles.add(styleQueue);
}
}
@@ -3613,36 +3661,35 @@ function pushStyleContents(target, props) {
return;
}
-function getImagePreloadKey(href, imageSrcSet, imageSizes) {
- var uniquePart = '';
-
- if (typeof imageSrcSet === 'string' && imageSrcSet !== '') {
- uniquePart += '[' + imageSrcSet + ']';
-
- if (typeof imageSizes === 'string') {
- uniquePart += '[' + imageSizes + ']';
- }
- } else {
- uniquePart += '[][]' + href;
- }
-
- return getResourceKey('image', uniquePart);
-}
-
function pushImg(target, props, resumableState, renderState, pictureTagInScope) {
var src = props.src,
srcSet = props.srcSet;
- if (props.loading !== 'lazy' && (typeof src === 'string' || typeof srcSet === 'string') && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
+ if (props.loading !== 'lazy' && (src || srcSet) && (typeof src === 'string' || src == null) && (typeof srcSet === 'string' || srcSet == null) && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
!(typeof src === 'string' && src[4] === ':' && (src[0] === 'd' || src[0] === 'D') && (src[1] === 'a' || src[1] === 'A') && (src[2] === 't' || src[2] === 'T') && (src[3] === 'a' || src[3] === 'A')) && !(typeof srcSet === 'string' && srcSet[4] === ':' && (srcSet[0] === 'd' || srcSet[0] === 'D') && (srcSet[1] === 'a' || srcSet[1] === 'A') && (srcSet[2] === 't' || srcSet[2] === 'T') && (srcSet[3] === 'a' || srcSet[3] === 'A'))) {
// We have a suspensey image and ought to preload it to optimize the loading of display blocking
// resumableState.
- var sizes = props.sizes;
- var key = getImagePreloadKey(src, srcSet, sizes);
- var resource;
+ var sizes = typeof props.sizes === 'string' ? props.sizes : undefined;
+ var key = getImageResourceKey(src, srcSet, sizes);
+ var promotablePreloads = renderState.preloads.images;
+ var resource = promotablePreloads.get(key);
+
+ if (resource) {
+ // We consider whether this preload can be promoted to higher priority flushing queue.
+ // The only time a resource will exist here is if it was created during this render
+ // and was not already in the high priority queue.
+ if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
+ // Delete the resource from the map since we are promoting it and don't want to
+ // reenter this branch in a second pass for duplicate img hrefs.
+ promotablePreloads.delete(key); // $FlowFixMe - Flow should understand that this is a Resource if the condition was true
- if (!resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = {
+ renderState.highImagePreloads.add(resource);
+ }
+ } else if (!resumableState.imageResources.hasOwnProperty(key)) {
+ // We must construct a new preload resource
+ resumableState.imageResources[key] = PRELOAD_NO_CREDS;
+ resource = [];
+ pushLinkImpl(resource, {
rel: 'preload',
as: 'image',
// There is a bug in Safari where imageSrcSet is not respected on preload links
@@ -3657,25 +3704,15 @@ function pushImg(target, props, resumableState, renderState, pictureTagInScope)
type: props.type,
fetchPriority: props.fetchPriority,
referrerPolicy: props.referrerPolicy
- };
- resource = {
- type: 'preload',
- chunks: [],
- state: NoState,
- props: preloadProps
- };
- resumableState.preloadsMap[key] = preloadProps;
- renderState.preloadsMap.set(key, resource);
- pushLinkImpl(resource.chunks, preloadProps);
- } else {
- resource = renderState.preloadsMap.get(key);
- }
+ });
- if (resource) {
if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
renderState.highImagePreloads.add(resource);
} else {
- renderState.bulkPreloads.add(resource);
+ renderState.bulkPreloads.add(resource); // We can bump the priority up if the same img is rendered later
+ // with fetchPriority="high"
+
+ promotablePreloads.set(key, resource);
}
}
}
@@ -3849,35 +3886,50 @@ function pushScript(target, props, resumableState, renderState, textEmbedded, in
}
var src = props.src;
- var key = getResourceKey('script', src); // We can make this ');
@@ -1840,12 +1858,16 @@ function createRenderState(resumableState, nonce, bootstrapScriptContent, bootst
fontPreloads: new Set(),
highImagePreloads: new Set(),
// usedImagePreloads: new Set(),
- precedences: new Map(),
- stylePrecedences: new Map(),
+ styles: new Map(),
bootstrapScripts: new Set(),
scripts: new Set(),
bulkPreloads: new Set(),
- preloadsMap: new Map(),
+ preloads: {
+ images: new Map(),
+ stylesheets: new Map(),
+ scripts: new Map(),
+ moduleScripts: new Map()
+ },
nonce: nonce,
// like a module global for currently rendering boundary
boundaryResources: null,
@@ -1855,17 +1877,32 @@ function createRenderState(resumableState, nonce, bootstrapScriptContent, bootst
if (bootstrapScripts !== undefined) {
for (var i = 0; i < bootstrapScripts.length; i++) {
var scriptConfig = bootstrapScripts[i];
- var src = typeof scriptConfig === 'string' ? scriptConfig : scriptConfig.src;
- var integrity = typeof scriptConfig === 'string' ? undefined : scriptConfig.integrity;
- var crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
- preloadBootstrapScript(resumableState, renderState, src, nonce, integrity, crossOrigin);
+ var src = void 0,
+ crossOrigin = void 0,
+ integrity = void 0;
+ var props = {
+ rel: 'preload',
+ as: 'script',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
+
+ if (typeof scriptConfig === 'string') {
+ props.href = src = scriptConfig;
+ } else {
+ props.href = src = scriptConfig.src;
+ props.integrity = integrity = typeof scriptConfig.integrity === 'string' ? scriptConfig.integrity : undefined;
+ props.crossOrigin = crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
+
+ preloadBootstrapScriptOrModule(resumableState, renderState, src, props);
bootstrapChunks.push(startScriptSrc, stringToChunk(escapeTextForBrowser(src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (integrity) {
+ if (typeof integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(integrity)));
}
@@ -1881,20 +1918,32 @@ function createRenderState(resumableState, nonce, bootstrapScriptContent, bootst
for (var _i = 0; _i < bootstrapModules.length; _i++) {
var _scriptConfig = bootstrapModules[_i];
- var _src = typeof _scriptConfig === 'string' ? _scriptConfig : _scriptConfig.src;
+ var _src = void 0,
+ _crossOrigin = void 0,
+ _integrity = void 0;
- var _integrity = typeof _scriptConfig === 'string' ? undefined : _scriptConfig.integrity;
+ var _props = {
+ rel: 'modulepreload',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
- var _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ if (typeof _scriptConfig === 'string') {
+ _props.href = _src = _scriptConfig;
+ } else {
+ _props.href = _src = _scriptConfig.src;
+ _props.integrity = _integrity = typeof _scriptConfig.integrity === 'string' ? _scriptConfig.integrity : undefined;
+ _props.crossOrigin = _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
- preloadBootstrapModule(resumableState, renderState, _src, nonce, _integrity, _crossOrigin);
+ preloadBootstrapScriptOrModule(resumableState, renderState, _src, _props);
bootstrapChunks.push(startModuleSrc, stringToChunk(escapeTextForBrowser(_src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (_integrity) {
+ if (typeof _integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(_integrity)));
}
@@ -1931,10 +1980,18 @@ function createResumableState(identifierPrefix, externalRuntimeConfig) {
hasHtml: false,
// @TODO add bootstrap script to implicit preloads
// persistent
- preloadsMap: {},
- preconnectsMap: {},
- stylesMap: {},
- scriptsMap: {}
+ unknownResources: {},
+ dnsResources: {},
+ connectResources: {
+ default: {},
+ anonymous: {},
+ credentials: {}
+ },
+ imageResources: {},
+ styleResources: {},
+ scriptResources: {},
+ moduleUnknownResources: {},
+ moduleScriptResources: {}
};
} // Constants for the insertion mode we're currently writing in. We don't encode all HTML5 insertion
// modes. We only include the variants as they matter for the sake of our purposes.
@@ -3292,7 +3349,7 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
if (props.rel === 'stylesheet') {
// This may hoistable as a Stylesheet Resource, otherwise it will emit in place
- var key = getResourceKey('style', href);
+ var key = getResourceKey(href);
if (typeof precedence !== 'string' || props.disabled != null || props.onLoad || props.onError) {
// This stylesheet is either not opted into Resource semantics or has conflicting properties which
@@ -3313,67 +3370,58 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
return pushLinkImpl(target, props);
} else {
// This stylesheet refers to a Resource and we create a new one if necessary
- var stylesInPrecedence = renderState.precedences.get(precedence);
-
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- var resourceProps = stylesheetPropsFromRawProps(props);
- var state = NoState;
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
+
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS; // If this is the first time we've encountered this precedence we need
+ // to create a StyleQueue
+
+ if (!styleQueue) {
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
+ }
- if (resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = resumableState.preloadsMap[key];
- adoptPreloadPropsForStylesheetProps(resourceProps, preloadProps);
- var preloadResource = renderState.preloadsMap.get(key);
+ var resource = {
+ state: PENDING$1,
+ props: stylesheetPropsFromRawProps(props)
+ };
- if (preloadResource) {
- // If we already had a preload we don't want that resource to flush directly.
- // We let the newly created resource govern flushing.
- preloadResource.state |= Blocked;
+ if (resourceState) {
+ // When resourceState is truty it is a Preload state. We cast it for clarity
+ var preloadState = resourceState;
- if (preloadResource.state & Flushed) {
- state = PreloadFlushed;
- }
- } else {
- // If we resumed then we assume that this was already flushed
- // by the shell.
- state = PreloadFlushed;
+ if (preloadState.length === 2) {
+ adoptPreloadCredentials(resource.props, preloadState);
}
- }
- var resource = {
- type: 'stylesheet',
- chunks: [],
- state: state,
- props: resourceProps
- };
- resumableState.stylesMap[key] = null;
-
- if (!stylesInPrecedence) {
- stylesInPrecedence = new Map();
- renderState.precedences.set(precedence, stylesInPrecedence);
- var emptyStyleResource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: []
- }
- };
- stylesInPrecedence.set('', emptyStyleResource);
+ var preloadResource = renderState.preloads.stylesheets.get(key);
- {
- if (renderState.stylePrecedences.has(precedence)) {
- error('React constructed an empty style resource when a style resource already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ if (preloadResource && preloadResource.length > 0) {
+ // The Preload for this resource was created in this render pass and has not flushed yet so
+ // we need to clear it to avoid it flushing.
+ preloadResource.length = 0;
+ } else {
+ // Either the preload resource from this render already flushed in this render pass
+ // or the preload flushed in a prior pass (prerender). In either case we need to mark
+ // this resource as already having been preloaded.
+ resource.state = PRELOADED;
}
+ } // We add the newly created resource to our StyleQueue and if necessary
+ // track the resource with the currently rendering boundary
- renderState.stylePrecedences.set(precedence, emptyStyleResource);
- }
- stylesInPrecedence.set(key, resource);
+ styleQueue.sheets.set(key, resource);
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.stylesheets.add(resource);
}
} else {
// We need to track whether this boundary should wait on this resource or not.
@@ -3381,12 +3429,12 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
- if (stylesInPrecedence) {
- var _resource = stylesInPrecedence.get(key);
+ if (styleQueue) {
+ var _resource = styleQueue.sheets.get(key);
if (_resource) {
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(_resource);
+ renderState.boundaryResources.stylesheets.add(_resource);
}
}
}
@@ -3484,47 +3532,47 @@ function pushStyle(target, props, resumableState, renderState, textEmbedded, ins
}
}
- var key = getResourceKey('style', href);
- var resource = renderState.stylePrecedences.get(precedence);
+ var key = getResourceKey(href);
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- if (!resource) {
- resource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: [href]
- }
- };
- renderState.stylePrecedences.set(precedence, resource);
- var stylesInPrecedence = new Map();
- stylesInPrecedence.set('', resource);
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS;
- {
- if (renderState.precedences.has(precedence)) {
- error('React constructed a new style precedence set when one already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ {
+ if (resourceState) {
+ error('React encountered a hoistable style tag for the same href as a preload: "%s". When using a style tag to inline styles you should not also preload it as a stylsheet.', href);
}
+ }
- renderState.precedences.set(precedence, stylesInPrecedence);
+ if (!styleQueue) {
+ // This is the first time we've encountered this precedence we need
+ // to create a StyleQueue.
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [stringToChunk(escapeTextForBrowser(href))],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
} else {
- resource.props.hrefs.push(href);
+ // We have seen this precedence before and need to track this href
+ styleQueue.hrefs.push(stringToChunk(escapeTextForBrowser(href)));
}
- resumableState.stylesMap[key] = null;
- pushStyleContents(resource.chunks, props);
+ pushStyleContents(styleQueue.rules, props);
}
- if (resource) {
+ if (styleQueue) {
// We need to track whether this boundary should wait on this resource or not.
// Typically this resource should always exist since we either had it or just created
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.styles.add(styleQueue);
}
}
@@ -3613,36 +3661,35 @@ function pushStyleContents(target, props) {
return;
}
-function getImagePreloadKey(href, imageSrcSet, imageSizes) {
- var uniquePart = '';
-
- if (typeof imageSrcSet === 'string' && imageSrcSet !== '') {
- uniquePart += '[' + imageSrcSet + ']';
-
- if (typeof imageSizes === 'string') {
- uniquePart += '[' + imageSizes + ']';
- }
- } else {
- uniquePart += '[][]' + href;
- }
-
- return getResourceKey('image', uniquePart);
-}
-
function pushImg(target, props, resumableState, renderState, pictureTagInScope) {
var src = props.src,
srcSet = props.srcSet;
- if (props.loading !== 'lazy' && (typeof src === 'string' || typeof srcSet === 'string') && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
+ if (props.loading !== 'lazy' && (src || srcSet) && (typeof src === 'string' || src == null) && (typeof srcSet === 'string' || srcSet == null) && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
!(typeof src === 'string' && src[4] === ':' && (src[0] === 'd' || src[0] === 'D') && (src[1] === 'a' || src[1] === 'A') && (src[2] === 't' || src[2] === 'T') && (src[3] === 'a' || src[3] === 'A')) && !(typeof srcSet === 'string' && srcSet[4] === ':' && (srcSet[0] === 'd' || srcSet[0] === 'D') && (srcSet[1] === 'a' || srcSet[1] === 'A') && (srcSet[2] === 't' || srcSet[2] === 'T') && (srcSet[3] === 'a' || srcSet[3] === 'A'))) {
// We have a suspensey image and ought to preload it to optimize the loading of display blocking
// resumableState.
- var sizes = props.sizes;
- var key = getImagePreloadKey(src, srcSet, sizes);
- var resource;
+ var sizes = typeof props.sizes === 'string' ? props.sizes : undefined;
+ var key = getImageResourceKey(src, srcSet, sizes);
+ var promotablePreloads = renderState.preloads.images;
+ var resource = promotablePreloads.get(key);
+
+ if (resource) {
+ // We consider whether this preload can be promoted to higher priority flushing queue.
+ // The only time a resource will exist here is if it was created during this render
+ // and was not already in the high priority queue.
+ if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
+ // Delete the resource from the map since we are promoting it and don't want to
+ // reenter this branch in a second pass for duplicate img hrefs.
+ promotablePreloads.delete(key); // $FlowFixMe - Flow should understand that this is a Resource if the condition was true
- if (!resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = {
+ renderState.highImagePreloads.add(resource);
+ }
+ } else if (!resumableState.imageResources.hasOwnProperty(key)) {
+ // We must construct a new preload resource
+ resumableState.imageResources[key] = PRELOAD_NO_CREDS;
+ resource = [];
+ pushLinkImpl(resource, {
rel: 'preload',
as: 'image',
// There is a bug in Safari where imageSrcSet is not respected on preload links
@@ -3657,25 +3704,15 @@ function pushImg(target, props, resumableState, renderState, pictureTagInScope)
type: props.type,
fetchPriority: props.fetchPriority,
referrerPolicy: props.referrerPolicy
- };
- resource = {
- type: 'preload',
- chunks: [],
- state: NoState,
- props: preloadProps
- };
- resumableState.preloadsMap[key] = preloadProps;
- renderState.preloadsMap.set(key, resource);
- pushLinkImpl(resource.chunks, preloadProps);
- } else {
- resource = renderState.preloadsMap.get(key);
- }
+ });
- if (resource) {
if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
renderState.highImagePreloads.add(resource);
} else {
- renderState.bulkPreloads.add(resource);
+ renderState.bulkPreloads.add(resource); // We can bump the priority up if the same img is rendered later
+ // with fetchPriority="high"
+
+ promotablePreloads.set(key, resource);
}
}
}
@@ -3849,35 +3886,50 @@ function pushScript(target, props, resumableState, renderState, textEmbedded, in
}
var src = props.src;
- var key = getResourceKey('script', src); // We can make this ');
@@ -1823,12 +1841,16 @@ function createRenderState(resumableState, nonce, bootstrapScriptContent, bootst
fontPreloads: new Set(),
highImagePreloads: new Set(),
// usedImagePreloads: new Set(),
- precedences: new Map(),
- stylePrecedences: new Map(),
+ styles: new Map(),
bootstrapScripts: new Set(),
scripts: new Set(),
bulkPreloads: new Set(),
- preloadsMap: new Map(),
+ preloads: {
+ images: new Map(),
+ stylesheets: new Map(),
+ scripts: new Map(),
+ moduleScripts: new Map()
+ },
nonce: nonce,
// like a module global for currently rendering boundary
boundaryResources: null,
@@ -1838,17 +1860,32 @@ function createRenderState(resumableState, nonce, bootstrapScriptContent, bootst
if (bootstrapScripts !== undefined) {
for (var i = 0; i < bootstrapScripts.length; i++) {
var scriptConfig = bootstrapScripts[i];
- var src = typeof scriptConfig === 'string' ? scriptConfig : scriptConfig.src;
- var integrity = typeof scriptConfig === 'string' ? undefined : scriptConfig.integrity;
- var crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
- preloadBootstrapScript(resumableState, renderState, src, nonce, integrity, crossOrigin);
+ var src = void 0,
+ crossOrigin = void 0,
+ integrity = void 0;
+ var props = {
+ rel: 'preload',
+ as: 'script',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
+
+ if (typeof scriptConfig === 'string') {
+ props.href = src = scriptConfig;
+ } else {
+ props.href = src = scriptConfig.src;
+ props.integrity = integrity = typeof scriptConfig.integrity === 'string' ? scriptConfig.integrity : undefined;
+ props.crossOrigin = crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
+
+ preloadBootstrapScriptOrModule(resumableState, renderState, src, props);
bootstrapChunks.push(startScriptSrc, stringToChunk(escapeTextForBrowser(src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (integrity) {
+ if (typeof integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(integrity)));
}
@@ -1864,20 +1901,32 @@ function createRenderState(resumableState, nonce, bootstrapScriptContent, bootst
for (var _i = 0; _i < bootstrapModules.length; _i++) {
var _scriptConfig = bootstrapModules[_i];
- var _src = typeof _scriptConfig === 'string' ? _scriptConfig : _scriptConfig.src;
+ var _src = void 0,
+ _crossOrigin = void 0,
+ _integrity = void 0;
- var _integrity = typeof _scriptConfig === 'string' ? undefined : _scriptConfig.integrity;
+ var _props = {
+ rel: 'modulepreload',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
- var _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ if (typeof _scriptConfig === 'string') {
+ _props.href = _src = _scriptConfig;
+ } else {
+ _props.href = _src = _scriptConfig.src;
+ _props.integrity = _integrity = typeof _scriptConfig.integrity === 'string' ? _scriptConfig.integrity : undefined;
+ _props.crossOrigin = _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
- preloadBootstrapModule(resumableState, renderState, _src, nonce, _integrity, _crossOrigin);
+ preloadBootstrapScriptOrModule(resumableState, renderState, _src, _props);
bootstrapChunks.push(startModuleSrc, stringToChunk(escapeTextForBrowser(_src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (_integrity) {
+ if (typeof _integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(_integrity)));
}
@@ -1914,10 +1963,18 @@ function createResumableState(identifierPrefix, externalRuntimeConfig) {
hasHtml: false,
// @TODO add bootstrap script to implicit preloads
// persistent
- preloadsMap: {},
- preconnectsMap: {},
- stylesMap: {},
- scriptsMap: {}
+ unknownResources: {},
+ dnsResources: {},
+ connectResources: {
+ default: {},
+ anonymous: {},
+ credentials: {}
+ },
+ imageResources: {},
+ styleResources: {},
+ scriptResources: {},
+ moduleUnknownResources: {},
+ moduleScriptResources: {}
};
} // Constants for the insertion mode we're currently writing in. We don't encode all HTML5 insertion
// modes. We only include the variants as they matter for the sake of our purposes.
@@ -3275,7 +3332,7 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
if (props.rel === 'stylesheet') {
// This may hoistable as a Stylesheet Resource, otherwise it will emit in place
- var key = getResourceKey('style', href);
+ var key = getResourceKey(href);
if (typeof precedence !== 'string' || props.disabled != null || props.onLoad || props.onError) {
// This stylesheet is either not opted into Resource semantics or has conflicting properties which
@@ -3296,67 +3353,58 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
return pushLinkImpl(target, props);
} else {
// This stylesheet refers to a Resource and we create a new one if necessary
- var stylesInPrecedence = renderState.precedences.get(precedence);
-
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- var resourceProps = stylesheetPropsFromRawProps(props);
- var state = NoState;
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
+
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS; // If this is the first time we've encountered this precedence we need
+ // to create a StyleQueue
+
+ if (!styleQueue) {
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
+ }
- if (resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = resumableState.preloadsMap[key];
- adoptPreloadPropsForStylesheetProps(resourceProps, preloadProps);
- var preloadResource = renderState.preloadsMap.get(key);
+ var resource = {
+ state: PENDING$1,
+ props: stylesheetPropsFromRawProps(props)
+ };
- if (preloadResource) {
- // If we already had a preload we don't want that resource to flush directly.
- // We let the newly created resource govern flushing.
- preloadResource.state |= Blocked;
+ if (resourceState) {
+ // When resourceState is truty it is a Preload state. We cast it for clarity
+ var preloadState = resourceState;
- if (preloadResource.state & Flushed) {
- state = PreloadFlushed;
- }
- } else {
- // If we resumed then we assume that this was already flushed
- // by the shell.
- state = PreloadFlushed;
+ if (preloadState.length === 2) {
+ adoptPreloadCredentials(resource.props, preloadState);
}
- }
- var resource = {
- type: 'stylesheet',
- chunks: [],
- state: state,
- props: resourceProps
- };
- resumableState.stylesMap[key] = null;
-
- if (!stylesInPrecedence) {
- stylesInPrecedence = new Map();
- renderState.precedences.set(precedence, stylesInPrecedence);
- var emptyStyleResource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: []
- }
- };
- stylesInPrecedence.set('', emptyStyleResource);
+ var preloadResource = renderState.preloads.stylesheets.get(key);
- {
- if (renderState.stylePrecedences.has(precedence)) {
- error('React constructed an empty style resource when a style resource already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ if (preloadResource && preloadResource.length > 0) {
+ // The Preload for this resource was created in this render pass and has not flushed yet so
+ // we need to clear it to avoid it flushing.
+ preloadResource.length = 0;
+ } else {
+ // Either the preload resource from this render already flushed in this render pass
+ // or the preload flushed in a prior pass (prerender). In either case we need to mark
+ // this resource as already having been preloaded.
+ resource.state = PRELOADED;
}
+ } // We add the newly created resource to our StyleQueue and if necessary
+ // track the resource with the currently rendering boundary
- renderState.stylePrecedences.set(precedence, emptyStyleResource);
- }
- stylesInPrecedence.set(key, resource);
+ styleQueue.sheets.set(key, resource);
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.stylesheets.add(resource);
}
} else {
// We need to track whether this boundary should wait on this resource or not.
@@ -3364,12 +3412,12 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
- if (stylesInPrecedence) {
- var _resource = stylesInPrecedence.get(key);
+ if (styleQueue) {
+ var _resource = styleQueue.sheets.get(key);
if (_resource) {
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(_resource);
+ renderState.boundaryResources.stylesheets.add(_resource);
}
}
}
@@ -3467,47 +3515,47 @@ function pushStyle(target, props, resumableState, renderState, textEmbedded, ins
}
}
- var key = getResourceKey('style', href);
- var resource = renderState.stylePrecedences.get(precedence);
+ var key = getResourceKey(href);
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- if (!resource) {
- resource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: [href]
- }
- };
- renderState.stylePrecedences.set(precedence, resource);
- var stylesInPrecedence = new Map();
- stylesInPrecedence.set('', resource);
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS;
- {
- if (renderState.precedences.has(precedence)) {
- error('React constructed a new style precedence set when one already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ {
+ if (resourceState) {
+ error('React encountered a hoistable style tag for the same href as a preload: "%s". When using a style tag to inline styles you should not also preload it as a stylsheet.', href);
}
+ }
- renderState.precedences.set(precedence, stylesInPrecedence);
+ if (!styleQueue) {
+ // This is the first time we've encountered this precedence we need
+ // to create a StyleQueue.
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [stringToChunk(escapeTextForBrowser(href))],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
} else {
- resource.props.hrefs.push(href);
+ // We have seen this precedence before and need to track this href
+ styleQueue.hrefs.push(stringToChunk(escapeTextForBrowser(href)));
}
- resumableState.stylesMap[key] = null;
- pushStyleContents(resource.chunks, props);
+ pushStyleContents(styleQueue.rules, props);
}
- if (resource) {
+ if (styleQueue) {
// We need to track whether this boundary should wait on this resource or not.
// Typically this resource should always exist since we either had it or just created
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.styles.add(styleQueue);
}
}
@@ -3596,36 +3644,35 @@ function pushStyleContents(target, props) {
return;
}
-function getImagePreloadKey(href, imageSrcSet, imageSizes) {
- var uniquePart = '';
-
- if (typeof imageSrcSet === 'string' && imageSrcSet !== '') {
- uniquePart += '[' + imageSrcSet + ']';
-
- if (typeof imageSizes === 'string') {
- uniquePart += '[' + imageSizes + ']';
- }
- } else {
- uniquePart += '[][]' + href;
- }
-
- return getResourceKey('image', uniquePart);
-}
-
function pushImg(target, props, resumableState, renderState, pictureTagInScope) {
var src = props.src,
srcSet = props.srcSet;
- if (props.loading !== 'lazy' && (typeof src === 'string' || typeof srcSet === 'string') && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
+ if (props.loading !== 'lazy' && (src || srcSet) && (typeof src === 'string' || src == null) && (typeof srcSet === 'string' || srcSet == null) && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
!(typeof src === 'string' && src[4] === ':' && (src[0] === 'd' || src[0] === 'D') && (src[1] === 'a' || src[1] === 'A') && (src[2] === 't' || src[2] === 'T') && (src[3] === 'a' || src[3] === 'A')) && !(typeof srcSet === 'string' && srcSet[4] === ':' && (srcSet[0] === 'd' || srcSet[0] === 'D') && (srcSet[1] === 'a' || srcSet[1] === 'A') && (srcSet[2] === 't' || srcSet[2] === 'T') && (srcSet[3] === 'a' || srcSet[3] === 'A'))) {
// We have a suspensey image and ought to preload it to optimize the loading of display blocking
// resumableState.
- var sizes = props.sizes;
- var key = getImagePreloadKey(src, srcSet, sizes);
- var resource;
+ var sizes = typeof props.sizes === 'string' ? props.sizes : undefined;
+ var key = getImageResourceKey(src, srcSet, sizes);
+ var promotablePreloads = renderState.preloads.images;
+ var resource = promotablePreloads.get(key);
+
+ if (resource) {
+ // We consider whether this preload can be promoted to higher priority flushing queue.
+ // The only time a resource will exist here is if it was created during this render
+ // and was not already in the high priority queue.
+ if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
+ // Delete the resource from the map since we are promoting it and don't want to
+ // reenter this branch in a second pass for duplicate img hrefs.
+ promotablePreloads.delete(key); // $FlowFixMe - Flow should understand that this is a Resource if the condition was true
- if (!resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = {
+ renderState.highImagePreloads.add(resource);
+ }
+ } else if (!resumableState.imageResources.hasOwnProperty(key)) {
+ // We must construct a new preload resource
+ resumableState.imageResources[key] = PRELOAD_NO_CREDS;
+ resource = [];
+ pushLinkImpl(resource, {
rel: 'preload',
as: 'image',
// There is a bug in Safari where imageSrcSet is not respected on preload links
@@ -3640,25 +3687,15 @@ function pushImg(target, props, resumableState, renderState, pictureTagInScope)
type: props.type,
fetchPriority: props.fetchPriority,
referrerPolicy: props.referrerPolicy
- };
- resource = {
- type: 'preload',
- chunks: [],
- state: NoState,
- props: preloadProps
- };
- resumableState.preloadsMap[key] = preloadProps;
- renderState.preloadsMap.set(key, resource);
- pushLinkImpl(resource.chunks, preloadProps);
- } else {
- resource = renderState.preloadsMap.get(key);
- }
+ });
- if (resource) {
if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
renderState.highImagePreloads.add(resource);
} else {
- renderState.bulkPreloads.add(resource);
+ renderState.bulkPreloads.add(resource); // We can bump the priority up if the same img is rendered later
+ // with fetchPriority="high"
+
+ promotablePreloads.set(key, resource);
}
}
}
@@ -3832,35 +3869,50 @@ function pushScript(target, props, resumableState, renderState, textEmbedded, in
}
var src = props.src;
- var key = getResourceKey('script', src); // We can make this ');
@@ -1630,12 +1648,16 @@ function createRenderState$1(resumableState, nonce, bootstrapScriptContent, boot
fontPreloads: new Set(),
highImagePreloads: new Set(),
// usedImagePreloads: new Set(),
- precedences: new Map(),
- stylePrecedences: new Map(),
+ styles: new Map(),
bootstrapScripts: new Set(),
scripts: new Set(),
bulkPreloads: new Set(),
- preloadsMap: new Map(),
+ preloads: {
+ images: new Map(),
+ stylesheets: new Map(),
+ scripts: new Map(),
+ moduleScripts: new Map()
+ },
nonce: nonce,
// like a module global for currently rendering boundary
boundaryResources: null,
@@ -1645,17 +1667,32 @@ function createRenderState$1(resumableState, nonce, bootstrapScriptContent, boot
if (bootstrapScripts !== undefined) {
for (var i = 0; i < bootstrapScripts.length; i++) {
var scriptConfig = bootstrapScripts[i];
- var src = typeof scriptConfig === 'string' ? scriptConfig : scriptConfig.src;
- var integrity = typeof scriptConfig === 'string' ? undefined : scriptConfig.integrity;
- var crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
- preloadBootstrapScript(resumableState, renderState, src, nonce, integrity, crossOrigin);
+ var src = void 0,
+ crossOrigin = void 0,
+ integrity = void 0;
+ var props = {
+ rel: 'preload',
+ as: 'script',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
+
+ if (typeof scriptConfig === 'string') {
+ props.href = src = scriptConfig;
+ } else {
+ props.href = src = scriptConfig.src;
+ props.integrity = integrity = typeof scriptConfig.integrity === 'string' ? scriptConfig.integrity : undefined;
+ props.crossOrigin = crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
+
+ preloadBootstrapScriptOrModule(resumableState, renderState, src, props);
bootstrapChunks.push(startScriptSrc, stringToChunk(escapeTextForBrowser(src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (integrity) {
+ if (typeof integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(integrity)));
}
@@ -1671,20 +1708,32 @@ function createRenderState$1(resumableState, nonce, bootstrapScriptContent, boot
for (var _i = 0; _i < bootstrapModules.length; _i++) {
var _scriptConfig = bootstrapModules[_i];
- var _src = typeof _scriptConfig === 'string' ? _scriptConfig : _scriptConfig.src;
+ var _src = void 0,
+ _crossOrigin = void 0,
+ _integrity = void 0;
- var _integrity = typeof _scriptConfig === 'string' ? undefined : _scriptConfig.integrity;
+ var _props = {
+ rel: 'modulepreload',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
- var _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ if (typeof _scriptConfig === 'string') {
+ _props.href = _src = _scriptConfig;
+ } else {
+ _props.href = _src = _scriptConfig.src;
+ _props.integrity = _integrity = typeof _scriptConfig.integrity === 'string' ? _scriptConfig.integrity : undefined;
+ _props.crossOrigin = _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
- preloadBootstrapModule(resumableState, renderState, _src, nonce, _integrity, _crossOrigin);
+ preloadBootstrapScriptOrModule(resumableState, renderState, _src, _props);
bootstrapChunks.push(startModuleSrc, stringToChunk(escapeTextForBrowser(_src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (_integrity) {
+ if (typeof _integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(_integrity)));
}
@@ -1717,10 +1766,18 @@ function createResumableState(identifierPrefix, externalRuntimeConfig) {
hasHtml: false,
// @TODO add bootstrap script to implicit preloads
// persistent
- preloadsMap: {},
- preconnectsMap: {},
- stylesMap: {},
- scriptsMap: {}
+ unknownResources: {},
+ dnsResources: {},
+ connectResources: {
+ default: {},
+ anonymous: {},
+ credentials: {}
+ },
+ imageResources: {},
+ styleResources: {},
+ scriptResources: {},
+ moduleUnknownResources: {},
+ moduleScriptResources: {}
};
} // Constants for the insertion mode we're currently writing in. We don't encode all HTML5 insertion
// modes. We only include the variants as they matter for the sake of our purposes.
@@ -2938,7 +2995,7 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
if (props.rel === 'stylesheet') {
// This may hoistable as a Stylesheet Resource, otherwise it will emit in place
- var key = getResourceKey('style', href);
+ var key = getResourceKey(href);
if (typeof precedence !== 'string' || props.disabled != null || props.onLoad || props.onError) {
// This stylesheet is either not opted into Resource semantics or has conflicting properties which
@@ -2959,67 +3016,58 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
return pushLinkImpl(target, props);
} else {
// This stylesheet refers to a Resource and we create a new one if necessary
- var stylesInPrecedence = renderState.precedences.get(precedence);
-
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- var resourceProps = stylesheetPropsFromRawProps(props);
- var state = NoState;
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
+
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS; // If this is the first time we've encountered this precedence we need
+ // to create a StyleQueue
+
+ if (!styleQueue) {
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
+ }
- if (resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = resumableState.preloadsMap[key];
- adoptPreloadPropsForStylesheetProps(resourceProps, preloadProps);
- var preloadResource = renderState.preloadsMap.get(key);
+ var resource = {
+ state: PENDING$1,
+ props: stylesheetPropsFromRawProps(props)
+ };
- if (preloadResource) {
- // If we already had a preload we don't want that resource to flush directly.
- // We let the newly created resource govern flushing.
- preloadResource.state |= Blocked;
+ if (resourceState) {
+ // When resourceState is truty it is a Preload state. We cast it for clarity
+ var preloadState = resourceState;
- if (preloadResource.state & Flushed) {
- state = PreloadFlushed;
- }
- } else {
- // If we resumed then we assume that this was already flushed
- // by the shell.
- state = PreloadFlushed;
+ if (preloadState.length === 2) {
+ adoptPreloadCredentials(resource.props, preloadState);
}
- }
- var resource = {
- type: 'stylesheet',
- chunks: [],
- state: state,
- props: resourceProps
- };
- resumableState.stylesMap[key] = null;
-
- if (!stylesInPrecedence) {
- stylesInPrecedence = new Map();
- renderState.precedences.set(precedence, stylesInPrecedence);
- var emptyStyleResource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: []
- }
- };
- stylesInPrecedence.set('', emptyStyleResource);
+ var preloadResource = renderState.preloads.stylesheets.get(key);
- {
- if (renderState.stylePrecedences.has(precedence)) {
- error('React constructed an empty style resource when a style resource already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ if (preloadResource && preloadResource.length > 0) {
+ // The Preload for this resource was created in this render pass and has not flushed yet so
+ // we need to clear it to avoid it flushing.
+ preloadResource.length = 0;
+ } else {
+ // Either the preload resource from this render already flushed in this render pass
+ // or the preload flushed in a prior pass (prerender). In either case we need to mark
+ // this resource as already having been preloaded.
+ resource.state = PRELOADED;
}
+ } // We add the newly created resource to our StyleQueue and if necessary
+ // track the resource with the currently rendering boundary
- renderState.stylePrecedences.set(precedence, emptyStyleResource);
- }
- stylesInPrecedence.set(key, resource);
+ styleQueue.sheets.set(key, resource);
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.stylesheets.add(resource);
}
} else {
// We need to track whether this boundary should wait on this resource or not.
@@ -3027,12 +3075,12 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
- if (stylesInPrecedence) {
- var _resource = stylesInPrecedence.get(key);
+ if (styleQueue) {
+ var _resource = styleQueue.sheets.get(key);
if (_resource) {
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(_resource);
+ renderState.boundaryResources.stylesheets.add(_resource);
}
}
}
@@ -3130,47 +3178,47 @@ function pushStyle(target, props, resumableState, renderState, textEmbedded, ins
}
}
- var key = getResourceKey('style', href);
- var resource = renderState.stylePrecedences.get(precedence);
+ var key = getResourceKey(href);
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- if (!resource) {
- resource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: [href]
- }
- };
- renderState.stylePrecedences.set(precedence, resource);
- var stylesInPrecedence = new Map();
- stylesInPrecedence.set('', resource);
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS;
- {
- if (renderState.precedences.has(precedence)) {
- error('React constructed a new style precedence set when one already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ {
+ if (resourceState) {
+ error('React encountered a hoistable style tag for the same href as a preload: "%s". When using a style tag to inline styles you should not also preload it as a stylsheet.', href);
}
+ }
- renderState.precedences.set(precedence, stylesInPrecedence);
+ if (!styleQueue) {
+ // This is the first time we've encountered this precedence we need
+ // to create a StyleQueue.
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [stringToChunk(escapeTextForBrowser(href))],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
} else {
- resource.props.hrefs.push(href);
+ // We have seen this precedence before and need to track this href
+ styleQueue.hrefs.push(stringToChunk(escapeTextForBrowser(href)));
}
- resumableState.stylesMap[key] = null;
- pushStyleContents(resource.chunks, props);
+ pushStyleContents(styleQueue.rules, props);
}
- if (resource) {
+ if (styleQueue) {
// We need to track whether this boundary should wait on this resource or not.
// Typically this resource should always exist since we either had it or just created
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.styles.add(styleQueue);
}
}
@@ -3259,36 +3307,35 @@ function pushStyleContents(target, props) {
return;
}
-function getImagePreloadKey(href, imageSrcSet, imageSizes) {
- var uniquePart = '';
-
- if (typeof imageSrcSet === 'string' && imageSrcSet !== '') {
- uniquePart += '[' + imageSrcSet + ']';
-
- if (typeof imageSizes === 'string') {
- uniquePart += '[' + imageSizes + ']';
- }
- } else {
- uniquePart += '[][]' + href;
- }
-
- return getResourceKey('image', uniquePart);
-}
-
function pushImg(target, props, resumableState, renderState, pictureTagInScope) {
var src = props.src,
srcSet = props.srcSet;
- if (props.loading !== 'lazy' && (typeof src === 'string' || typeof srcSet === 'string') && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
+ if (props.loading !== 'lazy' && (src || srcSet) && (typeof src === 'string' || src == null) && (typeof srcSet === 'string' || srcSet == null) && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
!(typeof src === 'string' && src[4] === ':' && (src[0] === 'd' || src[0] === 'D') && (src[1] === 'a' || src[1] === 'A') && (src[2] === 't' || src[2] === 'T') && (src[3] === 'a' || src[3] === 'A')) && !(typeof srcSet === 'string' && srcSet[4] === ':' && (srcSet[0] === 'd' || srcSet[0] === 'D') && (srcSet[1] === 'a' || srcSet[1] === 'A') && (srcSet[2] === 't' || srcSet[2] === 'T') && (srcSet[3] === 'a' || srcSet[3] === 'A'))) {
// We have a suspensey image and ought to preload it to optimize the loading of display blocking
// resumableState.
- var sizes = props.sizes;
- var key = getImagePreloadKey(src, srcSet, sizes);
- var resource;
+ var sizes = typeof props.sizes === 'string' ? props.sizes : undefined;
+ var key = getImageResourceKey(src, srcSet, sizes);
+ var promotablePreloads = renderState.preloads.images;
+ var resource = promotablePreloads.get(key);
- if (!resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = {
+ if (resource) {
+ // We consider whether this preload can be promoted to higher priority flushing queue.
+ // The only time a resource will exist here is if it was created during this render
+ // and was not already in the high priority queue.
+ if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
+ // Delete the resource from the map since we are promoting it and don't want to
+ // reenter this branch in a second pass for duplicate img hrefs.
+ promotablePreloads.delete(key); // $FlowFixMe - Flow should understand that this is a Resource if the condition was true
+
+ renderState.highImagePreloads.add(resource);
+ }
+ } else if (!resumableState.imageResources.hasOwnProperty(key)) {
+ // We must construct a new preload resource
+ resumableState.imageResources[key] = PRELOAD_NO_CREDS;
+ resource = [];
+ pushLinkImpl(resource, {
rel: 'preload',
as: 'image',
// There is a bug in Safari where imageSrcSet is not respected on preload links
@@ -3303,25 +3350,15 @@ function pushImg(target, props, resumableState, renderState, pictureTagInScope)
type: props.type,
fetchPriority: props.fetchPriority,
referrerPolicy: props.referrerPolicy
- };
- resource = {
- type: 'preload',
- chunks: [],
- state: NoState,
- props: preloadProps
- };
- resumableState.preloadsMap[key] = preloadProps;
- renderState.preloadsMap.set(key, resource);
- pushLinkImpl(resource.chunks, preloadProps);
- } else {
- resource = renderState.preloadsMap.get(key);
- }
+ });
- if (resource) {
if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
renderState.highImagePreloads.add(resource);
} else {
- renderState.bulkPreloads.add(resource);
+ renderState.bulkPreloads.add(resource); // We can bump the priority up if the same img is rendered later
+ // with fetchPriority="high"
+
+ promotablePreloads.set(key, resource);
}
}
}
@@ -3495,35 +3532,50 @@ function pushScript(target, props, resumableState, renderState, textEmbedded, in
}
var src = props.src;
- var key = getResourceKey('script', src); // We can make this ');
@@ -1631,12 +1649,16 @@ function createRenderState$1(resumableState, nonce, bootstrapScriptContent, boot
fontPreloads: new Set(),
highImagePreloads: new Set(),
// usedImagePreloads: new Set(),
- precedences: new Map(),
- stylePrecedences: new Map(),
+ styles: new Map(),
bootstrapScripts: new Set(),
scripts: new Set(),
bulkPreloads: new Set(),
- preloadsMap: new Map(),
+ preloads: {
+ images: new Map(),
+ stylesheets: new Map(),
+ scripts: new Map(),
+ moduleScripts: new Map()
+ },
nonce: nonce,
// like a module global for currently rendering boundary
boundaryResources: null,
@@ -1646,17 +1668,32 @@ function createRenderState$1(resumableState, nonce, bootstrapScriptContent, boot
if (bootstrapScripts !== undefined) {
for (var i = 0; i < bootstrapScripts.length; i++) {
var scriptConfig = bootstrapScripts[i];
- var src = typeof scriptConfig === 'string' ? scriptConfig : scriptConfig.src;
- var integrity = typeof scriptConfig === 'string' ? undefined : scriptConfig.integrity;
- var crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
- preloadBootstrapScript(resumableState, renderState, src, nonce, integrity, crossOrigin);
+ var src = void 0,
+ crossOrigin = void 0,
+ integrity = void 0;
+ var props = {
+ rel: 'preload',
+ as: 'script',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
+
+ if (typeof scriptConfig === 'string') {
+ props.href = src = scriptConfig;
+ } else {
+ props.href = src = scriptConfig.src;
+ props.integrity = integrity = typeof scriptConfig.integrity === 'string' ? scriptConfig.integrity : undefined;
+ props.crossOrigin = crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
+
+ preloadBootstrapScriptOrModule(resumableState, renderState, src, props);
bootstrapChunks.push(startScriptSrc, stringToChunk(escapeTextForBrowser(src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (integrity) {
+ if (typeof integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(integrity)));
}
@@ -1672,20 +1709,32 @@ function createRenderState$1(resumableState, nonce, bootstrapScriptContent, boot
for (var _i = 0; _i < bootstrapModules.length; _i++) {
var _scriptConfig = bootstrapModules[_i];
- var _src = typeof _scriptConfig === 'string' ? _scriptConfig : _scriptConfig.src;
+ var _src = void 0,
+ _crossOrigin = void 0,
+ _integrity = void 0;
- var _integrity = typeof _scriptConfig === 'string' ? undefined : _scriptConfig.integrity;
+ var _props = {
+ rel: 'modulepreload',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
- var _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ if (typeof _scriptConfig === 'string') {
+ _props.href = _src = _scriptConfig;
+ } else {
+ _props.href = _src = _scriptConfig.src;
+ _props.integrity = _integrity = typeof _scriptConfig.integrity === 'string' ? _scriptConfig.integrity : undefined;
+ _props.crossOrigin = _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
- preloadBootstrapModule(resumableState, renderState, _src, nonce, _integrity, _crossOrigin);
+ preloadBootstrapScriptOrModule(resumableState, renderState, _src, _props);
bootstrapChunks.push(startModuleSrc, stringToChunk(escapeTextForBrowser(_src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (_integrity) {
+ if (typeof _integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(_integrity)));
}
@@ -1718,10 +1767,18 @@ function createResumableState(identifierPrefix, externalRuntimeConfig) {
hasHtml: false,
// @TODO add bootstrap script to implicit preloads
// persistent
- preloadsMap: {},
- preconnectsMap: {},
- stylesMap: {},
- scriptsMap: {}
+ unknownResources: {},
+ dnsResources: {},
+ connectResources: {
+ default: {},
+ anonymous: {},
+ credentials: {}
+ },
+ imageResources: {},
+ styleResources: {},
+ scriptResources: {},
+ moduleUnknownResources: {},
+ moduleScriptResources: {}
};
} // Constants for the insertion mode we're currently writing in. We don't encode all HTML5 insertion
// modes. We only include the variants as they matter for the sake of our purposes.
@@ -2939,7 +2996,7 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
if (props.rel === 'stylesheet') {
// This may hoistable as a Stylesheet Resource, otherwise it will emit in place
- var key = getResourceKey('style', href);
+ var key = getResourceKey(href);
if (typeof precedence !== 'string' || props.disabled != null || props.onLoad || props.onError) {
// This stylesheet is either not opted into Resource semantics or has conflicting properties which
@@ -2960,67 +3017,58 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
return pushLinkImpl(target, props);
} else {
// This stylesheet refers to a Resource and we create a new one if necessary
- var stylesInPrecedence = renderState.precedences.get(precedence);
-
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- var resourceProps = stylesheetPropsFromRawProps(props);
- var state = NoState;
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
+
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS; // If this is the first time we've encountered this precedence we need
+ // to create a StyleQueue
+
+ if (!styleQueue) {
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
+ }
- if (resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = resumableState.preloadsMap[key];
- adoptPreloadPropsForStylesheetProps(resourceProps, preloadProps);
- var preloadResource = renderState.preloadsMap.get(key);
+ var resource = {
+ state: PENDING$1,
+ props: stylesheetPropsFromRawProps(props)
+ };
- if (preloadResource) {
- // If we already had a preload we don't want that resource to flush directly.
- // We let the newly created resource govern flushing.
- preloadResource.state |= Blocked;
+ if (resourceState) {
+ // When resourceState is truty it is a Preload state. We cast it for clarity
+ var preloadState = resourceState;
- if (preloadResource.state & Flushed) {
- state = PreloadFlushed;
- }
- } else {
- // If we resumed then we assume that this was already flushed
- // by the shell.
- state = PreloadFlushed;
+ if (preloadState.length === 2) {
+ adoptPreloadCredentials(resource.props, preloadState);
}
- }
- var resource = {
- type: 'stylesheet',
- chunks: [],
- state: state,
- props: resourceProps
- };
- resumableState.stylesMap[key] = null;
-
- if (!stylesInPrecedence) {
- stylesInPrecedence = new Map();
- renderState.precedences.set(precedence, stylesInPrecedence);
- var emptyStyleResource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: []
- }
- };
- stylesInPrecedence.set('', emptyStyleResource);
+ var preloadResource = renderState.preloads.stylesheets.get(key);
- {
- if (renderState.stylePrecedences.has(precedence)) {
- error('React constructed an empty style resource when a style resource already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ if (preloadResource && preloadResource.length > 0) {
+ // The Preload for this resource was created in this render pass and has not flushed yet so
+ // we need to clear it to avoid it flushing.
+ preloadResource.length = 0;
+ } else {
+ // Either the preload resource from this render already flushed in this render pass
+ // or the preload flushed in a prior pass (prerender). In either case we need to mark
+ // this resource as already having been preloaded.
+ resource.state = PRELOADED;
}
+ } // We add the newly created resource to our StyleQueue and if necessary
+ // track the resource with the currently rendering boundary
- renderState.stylePrecedences.set(precedence, emptyStyleResource);
- }
- stylesInPrecedence.set(key, resource);
+ styleQueue.sheets.set(key, resource);
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.stylesheets.add(resource);
}
} else {
// We need to track whether this boundary should wait on this resource or not.
@@ -3028,12 +3076,12 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
- if (stylesInPrecedence) {
- var _resource = stylesInPrecedence.get(key);
+ if (styleQueue) {
+ var _resource = styleQueue.sheets.get(key);
if (_resource) {
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(_resource);
+ renderState.boundaryResources.stylesheets.add(_resource);
}
}
}
@@ -3131,47 +3179,47 @@ function pushStyle(target, props, resumableState, renderState, textEmbedded, ins
}
}
- var key = getResourceKey('style', href);
- var resource = renderState.stylePrecedences.get(precedence);
+ var key = getResourceKey(href);
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- if (!resource) {
- resource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: [href]
- }
- };
- renderState.stylePrecedences.set(precedence, resource);
- var stylesInPrecedence = new Map();
- stylesInPrecedence.set('', resource);
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS;
- {
- if (renderState.precedences.has(precedence)) {
- error('React constructed a new style precedence set when one already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ {
+ if (resourceState) {
+ error('React encountered a hoistable style tag for the same href as a preload: "%s". When using a style tag to inline styles you should not also preload it as a stylsheet.', href);
}
+ }
- renderState.precedences.set(precedence, stylesInPrecedence);
+ if (!styleQueue) {
+ // This is the first time we've encountered this precedence we need
+ // to create a StyleQueue.
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [stringToChunk(escapeTextForBrowser(href))],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
} else {
- resource.props.hrefs.push(href);
+ // We have seen this precedence before and need to track this href
+ styleQueue.hrefs.push(stringToChunk(escapeTextForBrowser(href)));
}
- resumableState.stylesMap[key] = null;
- pushStyleContents(resource.chunks, props);
+ pushStyleContents(styleQueue.rules, props);
}
- if (resource) {
+ if (styleQueue) {
// We need to track whether this boundary should wait on this resource or not.
// Typically this resource should always exist since we either had it or just created
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.styles.add(styleQueue);
}
}
@@ -3260,36 +3308,35 @@ function pushStyleContents(target, props) {
return;
}
-function getImagePreloadKey(href, imageSrcSet, imageSizes) {
- var uniquePart = '';
-
- if (typeof imageSrcSet === 'string' && imageSrcSet !== '') {
- uniquePart += '[' + imageSrcSet + ']';
-
- if (typeof imageSizes === 'string') {
- uniquePart += '[' + imageSizes + ']';
- }
- } else {
- uniquePart += '[][]' + href;
- }
-
- return getResourceKey('image', uniquePart);
-}
-
function pushImg(target, props, resumableState, renderState, pictureTagInScope) {
var src = props.src,
srcSet = props.srcSet;
- if (props.loading !== 'lazy' && (typeof src === 'string' || typeof srcSet === 'string') && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
+ if (props.loading !== 'lazy' && (src || srcSet) && (typeof src === 'string' || src == null) && (typeof srcSet === 'string' || srcSet == null) && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
!(typeof src === 'string' && src[4] === ':' && (src[0] === 'd' || src[0] === 'D') && (src[1] === 'a' || src[1] === 'A') && (src[2] === 't' || src[2] === 'T') && (src[3] === 'a' || src[3] === 'A')) && !(typeof srcSet === 'string' && srcSet[4] === ':' && (srcSet[0] === 'd' || srcSet[0] === 'D') && (srcSet[1] === 'a' || srcSet[1] === 'A') && (srcSet[2] === 't' || srcSet[2] === 'T') && (srcSet[3] === 'a' || srcSet[3] === 'A'))) {
// We have a suspensey image and ought to preload it to optimize the loading of display blocking
// resumableState.
- var sizes = props.sizes;
- var key = getImagePreloadKey(src, srcSet, sizes);
- var resource;
+ var sizes = typeof props.sizes === 'string' ? props.sizes : undefined;
+ var key = getImageResourceKey(src, srcSet, sizes);
+ var promotablePreloads = renderState.preloads.images;
+ var resource = promotablePreloads.get(key);
- if (!resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = {
+ if (resource) {
+ // We consider whether this preload can be promoted to higher priority flushing queue.
+ // The only time a resource will exist here is if it was created during this render
+ // and was not already in the high priority queue.
+ if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
+ // Delete the resource from the map since we are promoting it and don't want to
+ // reenter this branch in a second pass for duplicate img hrefs.
+ promotablePreloads.delete(key); // $FlowFixMe - Flow should understand that this is a Resource if the condition was true
+
+ renderState.highImagePreloads.add(resource);
+ }
+ } else if (!resumableState.imageResources.hasOwnProperty(key)) {
+ // We must construct a new preload resource
+ resumableState.imageResources[key] = PRELOAD_NO_CREDS;
+ resource = [];
+ pushLinkImpl(resource, {
rel: 'preload',
as: 'image',
// There is a bug in Safari where imageSrcSet is not respected on preload links
@@ -3304,25 +3351,15 @@ function pushImg(target, props, resumableState, renderState, pictureTagInScope)
type: props.type,
fetchPriority: props.fetchPriority,
referrerPolicy: props.referrerPolicy
- };
- resource = {
- type: 'preload',
- chunks: [],
- state: NoState,
- props: preloadProps
- };
- resumableState.preloadsMap[key] = preloadProps;
- renderState.preloadsMap.set(key, resource);
- pushLinkImpl(resource.chunks, preloadProps);
- } else {
- resource = renderState.preloadsMap.get(key);
- }
+ });
- if (resource) {
if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
renderState.highImagePreloads.add(resource);
} else {
- renderState.bulkPreloads.add(resource);
+ renderState.bulkPreloads.add(resource); // We can bump the priority up if the same img is rendered later
+ // with fetchPriority="high"
+
+ promotablePreloads.set(key, resource);
}
}
}
@@ -3496,35 +3533,50 @@ function pushScript(target, props, resumableState, renderState, textEmbedded, in
}
var src = props.src;
- var key = getResourceKey('script', src); // We can make this ');
@@ -1711,12 +1729,16 @@ function createRenderState(resumableState, nonce, bootstrapScriptContent, bootst
fontPreloads: new Set(),
highImagePreloads: new Set(),
// usedImagePreloads: new Set(),
- precedences: new Map(),
- stylePrecedences: new Map(),
+ styles: new Map(),
bootstrapScripts: new Set(),
scripts: new Set(),
bulkPreloads: new Set(),
- preloadsMap: new Map(),
+ preloads: {
+ images: new Map(),
+ stylesheets: new Map(),
+ scripts: new Map(),
+ moduleScripts: new Map()
+ },
nonce: nonce,
// like a module global for currently rendering boundary
boundaryResources: null,
@@ -1726,17 +1748,32 @@ function createRenderState(resumableState, nonce, bootstrapScriptContent, bootst
if (bootstrapScripts !== undefined) {
for (var i = 0; i < bootstrapScripts.length; i++) {
var scriptConfig = bootstrapScripts[i];
- var src = typeof scriptConfig === 'string' ? scriptConfig : scriptConfig.src;
- var integrity = typeof scriptConfig === 'string' ? undefined : scriptConfig.integrity;
- var crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
- preloadBootstrapScript(resumableState, renderState, src, nonce, integrity, crossOrigin);
+ var src = void 0,
+ crossOrigin = void 0,
+ integrity = void 0;
+ var props = {
+ rel: 'preload',
+ as: 'script',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
+
+ if (typeof scriptConfig === 'string') {
+ props.href = src = scriptConfig;
+ } else {
+ props.href = src = scriptConfig.src;
+ props.integrity = integrity = typeof scriptConfig.integrity === 'string' ? scriptConfig.integrity : undefined;
+ props.crossOrigin = crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
+
+ preloadBootstrapScriptOrModule(resumableState, renderState, src, props);
bootstrapChunks.push(startScriptSrc, stringToChunk(escapeTextForBrowser(src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (integrity) {
+ if (typeof integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(integrity)));
}
@@ -1752,20 +1789,32 @@ function createRenderState(resumableState, nonce, bootstrapScriptContent, bootst
for (var _i = 0; _i < bootstrapModules.length; _i++) {
var _scriptConfig = bootstrapModules[_i];
- var _src = typeof _scriptConfig === 'string' ? _scriptConfig : _scriptConfig.src;
+ var _src = void 0,
+ _crossOrigin = void 0,
+ _integrity = void 0;
- var _integrity = typeof _scriptConfig === 'string' ? undefined : _scriptConfig.integrity;
+ var _props = {
+ rel: 'modulepreload',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
- var _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ if (typeof _scriptConfig === 'string') {
+ _props.href = _src = _scriptConfig;
+ } else {
+ _props.href = _src = _scriptConfig.src;
+ _props.integrity = _integrity = typeof _scriptConfig.integrity === 'string' ? _scriptConfig.integrity : undefined;
+ _props.crossOrigin = _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
- preloadBootstrapModule(resumableState, renderState, _src, nonce, _integrity, _crossOrigin);
+ preloadBootstrapScriptOrModule(resumableState, renderState, _src, _props);
bootstrapChunks.push(startModuleSrc, stringToChunk(escapeTextForBrowser(_src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (_integrity) {
+ if (typeof _integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(_integrity)));
}
@@ -1798,10 +1847,18 @@ function createResumableState(identifierPrefix, externalRuntimeConfig) {
hasHtml: false,
// @TODO add bootstrap script to implicit preloads
// persistent
- preloadsMap: {},
- preconnectsMap: {},
- stylesMap: {},
- scriptsMap: {}
+ unknownResources: {},
+ dnsResources: {},
+ connectResources: {
+ default: {},
+ anonymous: {},
+ credentials: {}
+ },
+ imageResources: {},
+ styleResources: {},
+ scriptResources: {},
+ moduleUnknownResources: {},
+ moduleScriptResources: {}
};
} // Constants for the insertion mode we're currently writing in. We don't encode all HTML5 insertion
// modes. We only include the variants as they matter for the sake of our purposes.
@@ -3021,7 +3078,7 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
if (props.rel === 'stylesheet') {
// This may hoistable as a Stylesheet Resource, otherwise it will emit in place
- var key = getResourceKey('style', href);
+ var key = getResourceKey(href);
if (typeof precedence !== 'string' || props.disabled != null || props.onLoad || props.onError) {
// This stylesheet is either not opted into Resource semantics or has conflicting properties which
@@ -3042,67 +3099,58 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
return pushLinkImpl(target, props);
} else {
// This stylesheet refers to a Resource and we create a new one if necessary
- var stylesInPrecedence = renderState.precedences.get(precedence);
-
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- var resourceProps = stylesheetPropsFromRawProps(props);
- var state = NoState;
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
+
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS; // If this is the first time we've encountered this precedence we need
+ // to create a StyleQueue
+
+ if (!styleQueue) {
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
+ }
- if (resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = resumableState.preloadsMap[key];
- adoptPreloadPropsForStylesheetProps(resourceProps, preloadProps);
- var preloadResource = renderState.preloadsMap.get(key);
+ var resource = {
+ state: PENDING$1,
+ props: stylesheetPropsFromRawProps(props)
+ };
- if (preloadResource) {
- // If we already had a preload we don't want that resource to flush directly.
- // We let the newly created resource govern flushing.
- preloadResource.state |= Blocked;
+ if (resourceState) {
+ // When resourceState is truty it is a Preload state. We cast it for clarity
+ var preloadState = resourceState;
- if (preloadResource.state & Flushed) {
- state = PreloadFlushed;
- }
- } else {
- // If we resumed then we assume that this was already flushed
- // by the shell.
- state = PreloadFlushed;
+ if (preloadState.length === 2) {
+ adoptPreloadCredentials(resource.props, preloadState);
}
- }
- var resource = {
- type: 'stylesheet',
- chunks: [],
- state: state,
- props: resourceProps
- };
- resumableState.stylesMap[key] = null;
-
- if (!stylesInPrecedence) {
- stylesInPrecedence = new Map();
- renderState.precedences.set(precedence, stylesInPrecedence);
- var emptyStyleResource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: []
- }
- };
- stylesInPrecedence.set('', emptyStyleResource);
+ var preloadResource = renderState.preloads.stylesheets.get(key);
- {
- if (renderState.stylePrecedences.has(precedence)) {
- error('React constructed an empty style resource when a style resource already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ if (preloadResource && preloadResource.length > 0) {
+ // The Preload for this resource was created in this render pass and has not flushed yet so
+ // we need to clear it to avoid it flushing.
+ preloadResource.length = 0;
+ } else {
+ // Either the preload resource from this render already flushed in this render pass
+ // or the preload flushed in a prior pass (prerender). In either case we need to mark
+ // this resource as already having been preloaded.
+ resource.state = PRELOADED;
}
+ } // We add the newly created resource to our StyleQueue and if necessary
+ // track the resource with the currently rendering boundary
- renderState.stylePrecedences.set(precedence, emptyStyleResource);
- }
- stylesInPrecedence.set(key, resource);
+ styleQueue.sheets.set(key, resource);
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.stylesheets.add(resource);
}
} else {
// We need to track whether this boundary should wait on this resource or not.
@@ -3110,12 +3158,12 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
- if (stylesInPrecedence) {
- var _resource = stylesInPrecedence.get(key);
+ if (styleQueue) {
+ var _resource = styleQueue.sheets.get(key);
if (_resource) {
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(_resource);
+ renderState.boundaryResources.stylesheets.add(_resource);
}
}
}
@@ -3213,47 +3261,47 @@ function pushStyle(target, props, resumableState, renderState, textEmbedded, ins
}
}
- var key = getResourceKey('style', href);
- var resource = renderState.stylePrecedences.get(precedence);
+ var key = getResourceKey(href);
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- if (!resource) {
- resource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: [href]
- }
- };
- renderState.stylePrecedences.set(precedence, resource);
- var stylesInPrecedence = new Map();
- stylesInPrecedence.set('', resource);
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS;
- {
- if (renderState.precedences.has(precedence)) {
- error('React constructed a new style precedence set when one already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ {
+ if (resourceState) {
+ error('React encountered a hoistable style tag for the same href as a preload: "%s". When using a style tag to inline styles you should not also preload it as a stylsheet.', href);
}
+ }
- renderState.precedences.set(precedence, stylesInPrecedence);
+ if (!styleQueue) {
+ // This is the first time we've encountered this precedence we need
+ // to create a StyleQueue.
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [stringToChunk(escapeTextForBrowser(href))],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
} else {
- resource.props.hrefs.push(href);
+ // We have seen this precedence before and need to track this href
+ styleQueue.hrefs.push(stringToChunk(escapeTextForBrowser(href)));
}
- resumableState.stylesMap[key] = null;
- pushStyleContents(resource.chunks, props);
+ pushStyleContents(styleQueue.rules, props);
}
- if (resource) {
+ if (styleQueue) {
// We need to track whether this boundary should wait on this resource or not.
// Typically this resource should always exist since we either had it or just created
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.styles.add(styleQueue);
}
}
@@ -3342,36 +3390,35 @@ function pushStyleContents(target, props) {
return;
}
-function getImagePreloadKey(href, imageSrcSet, imageSizes) {
- var uniquePart = '';
-
- if (typeof imageSrcSet === 'string' && imageSrcSet !== '') {
- uniquePart += '[' + imageSrcSet + ']';
-
- if (typeof imageSizes === 'string') {
- uniquePart += '[' + imageSizes + ']';
- }
- } else {
- uniquePart += '[][]' + href;
- }
-
- return getResourceKey('image', uniquePart);
-}
-
function pushImg(target, props, resumableState, renderState, pictureTagInScope) {
var src = props.src,
srcSet = props.srcSet;
- if (props.loading !== 'lazy' && (typeof src === 'string' || typeof srcSet === 'string') && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
+ if (props.loading !== 'lazy' && (src || srcSet) && (typeof src === 'string' || src == null) && (typeof srcSet === 'string' || srcSet == null) && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
!(typeof src === 'string' && src[4] === ':' && (src[0] === 'd' || src[0] === 'D') && (src[1] === 'a' || src[1] === 'A') && (src[2] === 't' || src[2] === 'T') && (src[3] === 'a' || src[3] === 'A')) && !(typeof srcSet === 'string' && srcSet[4] === ':' && (srcSet[0] === 'd' || srcSet[0] === 'D') && (srcSet[1] === 'a' || srcSet[1] === 'A') && (srcSet[2] === 't' || srcSet[2] === 'T') && (srcSet[3] === 'a' || srcSet[3] === 'A'))) {
// We have a suspensey image and ought to preload it to optimize the loading of display blocking
// resumableState.
- var sizes = props.sizes;
- var key = getImagePreloadKey(src, srcSet, sizes);
- var resource;
+ var sizes = typeof props.sizes === 'string' ? props.sizes : undefined;
+ var key = getImageResourceKey(src, srcSet, sizes);
+ var promotablePreloads = renderState.preloads.images;
+ var resource = promotablePreloads.get(key);
- if (!resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = {
+ if (resource) {
+ // We consider whether this preload can be promoted to higher priority flushing queue.
+ // The only time a resource will exist here is if it was created during this render
+ // and was not already in the high priority queue.
+ if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
+ // Delete the resource from the map since we are promoting it and don't want to
+ // reenter this branch in a second pass for duplicate img hrefs.
+ promotablePreloads.delete(key); // $FlowFixMe - Flow should understand that this is a Resource if the condition was true
+
+ renderState.highImagePreloads.add(resource);
+ }
+ } else if (!resumableState.imageResources.hasOwnProperty(key)) {
+ // We must construct a new preload resource
+ resumableState.imageResources[key] = PRELOAD_NO_CREDS;
+ resource = [];
+ pushLinkImpl(resource, {
rel: 'preload',
as: 'image',
// There is a bug in Safari where imageSrcSet is not respected on preload links
@@ -3386,25 +3433,15 @@ function pushImg(target, props, resumableState, renderState, pictureTagInScope)
type: props.type,
fetchPriority: props.fetchPriority,
referrerPolicy: props.referrerPolicy
- };
- resource = {
- type: 'preload',
- chunks: [],
- state: NoState,
- props: preloadProps
- };
- resumableState.preloadsMap[key] = preloadProps;
- renderState.preloadsMap.set(key, resource);
- pushLinkImpl(resource.chunks, preloadProps);
- } else {
- resource = renderState.preloadsMap.get(key);
- }
+ });
- if (resource) {
if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
renderState.highImagePreloads.add(resource);
} else {
- renderState.bulkPreloads.add(resource);
+ renderState.bulkPreloads.add(resource); // We can bump the priority up if the same img is rendered later
+ // with fetchPriority="high"
+
+ promotablePreloads.set(key, resource);
}
}
}
@@ -3578,35 +3615,50 @@ function pushScript(target, props, resumableState, renderState, textEmbedded, in
}
var src = props.src;
- var key = getResourceKey('script', src); // We can make this ');
@@ -1711,12 +1729,16 @@ function createRenderState(resumableState, nonce, bootstrapScriptContent, bootst
fontPreloads: new Set(),
highImagePreloads: new Set(),
// usedImagePreloads: new Set(),
- precedences: new Map(),
- stylePrecedences: new Map(),
+ styles: new Map(),
bootstrapScripts: new Set(),
scripts: new Set(),
bulkPreloads: new Set(),
- preloadsMap: new Map(),
+ preloads: {
+ images: new Map(),
+ stylesheets: new Map(),
+ scripts: new Map(),
+ moduleScripts: new Map()
+ },
nonce: nonce,
// like a module global for currently rendering boundary
boundaryResources: null,
@@ -1726,17 +1748,32 @@ function createRenderState(resumableState, nonce, bootstrapScriptContent, bootst
if (bootstrapScripts !== undefined) {
for (var i = 0; i < bootstrapScripts.length; i++) {
var scriptConfig = bootstrapScripts[i];
- var src = typeof scriptConfig === 'string' ? scriptConfig : scriptConfig.src;
- var integrity = typeof scriptConfig === 'string' ? undefined : scriptConfig.integrity;
- var crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
- preloadBootstrapScript(resumableState, renderState, src, nonce, integrity, crossOrigin);
+ var src = void 0,
+ crossOrigin = void 0,
+ integrity = void 0;
+ var props = {
+ rel: 'preload',
+ as: 'script',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
+
+ if (typeof scriptConfig === 'string') {
+ props.href = src = scriptConfig;
+ } else {
+ props.href = src = scriptConfig.src;
+ props.integrity = integrity = typeof scriptConfig.integrity === 'string' ? scriptConfig.integrity : undefined;
+ props.crossOrigin = crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
+
+ preloadBootstrapScriptOrModule(resumableState, renderState, src, props);
bootstrapChunks.push(startScriptSrc, stringToChunk(escapeTextForBrowser(src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (integrity) {
+ if (typeof integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(integrity)));
}
@@ -1752,20 +1789,32 @@ function createRenderState(resumableState, nonce, bootstrapScriptContent, bootst
for (var _i = 0; _i < bootstrapModules.length; _i++) {
var _scriptConfig = bootstrapModules[_i];
- var _src = typeof _scriptConfig === 'string' ? _scriptConfig : _scriptConfig.src;
+ var _src = void 0,
+ _crossOrigin = void 0,
+ _integrity = void 0;
- var _integrity = typeof _scriptConfig === 'string' ? undefined : _scriptConfig.integrity;
+ var _props = {
+ rel: 'modulepreload',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
- var _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ if (typeof _scriptConfig === 'string') {
+ _props.href = _src = _scriptConfig;
+ } else {
+ _props.href = _src = _scriptConfig.src;
+ _props.integrity = _integrity = typeof _scriptConfig.integrity === 'string' ? _scriptConfig.integrity : undefined;
+ _props.crossOrigin = _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
- preloadBootstrapModule(resumableState, renderState, _src, nonce, _integrity, _crossOrigin);
+ preloadBootstrapScriptOrModule(resumableState, renderState, _src, _props);
bootstrapChunks.push(startModuleSrc, stringToChunk(escapeTextForBrowser(_src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (_integrity) {
+ if (typeof _integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(_integrity)));
}
@@ -1798,10 +1847,18 @@ function createResumableState(identifierPrefix, externalRuntimeConfig) {
hasHtml: false,
// @TODO add bootstrap script to implicit preloads
// persistent
- preloadsMap: {},
- preconnectsMap: {},
- stylesMap: {},
- scriptsMap: {}
+ unknownResources: {},
+ dnsResources: {},
+ connectResources: {
+ default: {},
+ anonymous: {},
+ credentials: {}
+ },
+ imageResources: {},
+ styleResources: {},
+ scriptResources: {},
+ moduleUnknownResources: {},
+ moduleScriptResources: {}
};
} // Constants for the insertion mode we're currently writing in. We don't encode all HTML5 insertion
// modes. We only include the variants as they matter for the sake of our purposes.
@@ -3021,7 +3078,7 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
if (props.rel === 'stylesheet') {
// This may hoistable as a Stylesheet Resource, otherwise it will emit in place
- var key = getResourceKey('style', href);
+ var key = getResourceKey(href);
if (typeof precedence !== 'string' || props.disabled != null || props.onLoad || props.onError) {
// This stylesheet is either not opted into Resource semantics or has conflicting properties which
@@ -3042,67 +3099,58 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
return pushLinkImpl(target, props);
} else {
// This stylesheet refers to a Resource and we create a new one if necessary
- var stylesInPrecedence = renderState.precedences.get(precedence);
-
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- var resourceProps = stylesheetPropsFromRawProps(props);
- var state = NoState;
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
+
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS; // If this is the first time we've encountered this precedence we need
+ // to create a StyleQueue
+
+ if (!styleQueue) {
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
+ }
- if (resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = resumableState.preloadsMap[key];
- adoptPreloadPropsForStylesheetProps(resourceProps, preloadProps);
- var preloadResource = renderState.preloadsMap.get(key);
+ var resource = {
+ state: PENDING$1,
+ props: stylesheetPropsFromRawProps(props)
+ };
- if (preloadResource) {
- // If we already had a preload we don't want that resource to flush directly.
- // We let the newly created resource govern flushing.
- preloadResource.state |= Blocked;
+ if (resourceState) {
+ // When resourceState is truty it is a Preload state. We cast it for clarity
+ var preloadState = resourceState;
- if (preloadResource.state & Flushed) {
- state = PreloadFlushed;
- }
- } else {
- // If we resumed then we assume that this was already flushed
- // by the shell.
- state = PreloadFlushed;
+ if (preloadState.length === 2) {
+ adoptPreloadCredentials(resource.props, preloadState);
}
- }
- var resource = {
- type: 'stylesheet',
- chunks: [],
- state: state,
- props: resourceProps
- };
- resumableState.stylesMap[key] = null;
-
- if (!stylesInPrecedence) {
- stylesInPrecedence = new Map();
- renderState.precedences.set(precedence, stylesInPrecedence);
- var emptyStyleResource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: []
- }
- };
- stylesInPrecedence.set('', emptyStyleResource);
+ var preloadResource = renderState.preloads.stylesheets.get(key);
- {
- if (renderState.stylePrecedences.has(precedence)) {
- error('React constructed an empty style resource when a style resource already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ if (preloadResource && preloadResource.length > 0) {
+ // The Preload for this resource was created in this render pass and has not flushed yet so
+ // we need to clear it to avoid it flushing.
+ preloadResource.length = 0;
+ } else {
+ // Either the preload resource from this render already flushed in this render pass
+ // or the preload flushed in a prior pass (prerender). In either case we need to mark
+ // this resource as already having been preloaded.
+ resource.state = PRELOADED;
}
+ } // We add the newly created resource to our StyleQueue and if necessary
+ // track the resource with the currently rendering boundary
- renderState.stylePrecedences.set(precedence, emptyStyleResource);
- }
- stylesInPrecedence.set(key, resource);
+ styleQueue.sheets.set(key, resource);
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.stylesheets.add(resource);
}
} else {
// We need to track whether this boundary should wait on this resource or not.
@@ -3110,12 +3158,12 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
- if (stylesInPrecedence) {
- var _resource = stylesInPrecedence.get(key);
+ if (styleQueue) {
+ var _resource = styleQueue.sheets.get(key);
if (_resource) {
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(_resource);
+ renderState.boundaryResources.stylesheets.add(_resource);
}
}
}
@@ -3213,47 +3261,47 @@ function pushStyle(target, props, resumableState, renderState, textEmbedded, ins
}
}
- var key = getResourceKey('style', href);
- var resource = renderState.stylePrecedences.get(precedence);
+ var key = getResourceKey(href);
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- if (!resource) {
- resource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: [href]
- }
- };
- renderState.stylePrecedences.set(precedence, resource);
- var stylesInPrecedence = new Map();
- stylesInPrecedence.set('', resource);
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS;
- {
- if (renderState.precedences.has(precedence)) {
- error('React constructed a new style precedence set when one already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ {
+ if (resourceState) {
+ error('React encountered a hoistable style tag for the same href as a preload: "%s". When using a style tag to inline styles you should not also preload it as a stylsheet.', href);
}
+ }
- renderState.precedences.set(precedence, stylesInPrecedence);
+ if (!styleQueue) {
+ // This is the first time we've encountered this precedence we need
+ // to create a StyleQueue.
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [stringToChunk(escapeTextForBrowser(href))],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
} else {
- resource.props.hrefs.push(href);
+ // We have seen this precedence before and need to track this href
+ styleQueue.hrefs.push(stringToChunk(escapeTextForBrowser(href)));
}
- resumableState.stylesMap[key] = null;
- pushStyleContents(resource.chunks, props);
+ pushStyleContents(styleQueue.rules, props);
}
- if (resource) {
+ if (styleQueue) {
// We need to track whether this boundary should wait on this resource or not.
// Typically this resource should always exist since we either had it or just created
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.styles.add(styleQueue);
}
}
@@ -3342,36 +3390,35 @@ function pushStyleContents(target, props) {
return;
}
-function getImagePreloadKey(href, imageSrcSet, imageSizes) {
- var uniquePart = '';
-
- if (typeof imageSrcSet === 'string' && imageSrcSet !== '') {
- uniquePart += '[' + imageSrcSet + ']';
-
- if (typeof imageSizes === 'string') {
- uniquePart += '[' + imageSizes + ']';
- }
- } else {
- uniquePart += '[][]' + href;
- }
-
- return getResourceKey('image', uniquePart);
-}
-
function pushImg(target, props, resumableState, renderState, pictureTagInScope) {
var src = props.src,
srcSet = props.srcSet;
- if (props.loading !== 'lazy' && (typeof src === 'string' || typeof srcSet === 'string') && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
+ if (props.loading !== 'lazy' && (src || srcSet) && (typeof src === 'string' || src == null) && (typeof srcSet === 'string' || srcSet == null) && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
!(typeof src === 'string' && src[4] === ':' && (src[0] === 'd' || src[0] === 'D') && (src[1] === 'a' || src[1] === 'A') && (src[2] === 't' || src[2] === 'T') && (src[3] === 'a' || src[3] === 'A')) && !(typeof srcSet === 'string' && srcSet[4] === ':' && (srcSet[0] === 'd' || srcSet[0] === 'D') && (srcSet[1] === 'a' || srcSet[1] === 'A') && (srcSet[2] === 't' || srcSet[2] === 'T') && (srcSet[3] === 'a' || srcSet[3] === 'A'))) {
// We have a suspensey image and ought to preload it to optimize the loading of display blocking
// resumableState.
- var sizes = props.sizes;
- var key = getImagePreloadKey(src, srcSet, sizes);
- var resource;
+ var sizes = typeof props.sizes === 'string' ? props.sizes : undefined;
+ var key = getImageResourceKey(src, srcSet, sizes);
+ var promotablePreloads = renderState.preloads.images;
+ var resource = promotablePreloads.get(key);
- if (!resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = {
+ if (resource) {
+ // We consider whether this preload can be promoted to higher priority flushing queue.
+ // The only time a resource will exist here is if it was created during this render
+ // and was not already in the high priority queue.
+ if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
+ // Delete the resource from the map since we are promoting it and don't want to
+ // reenter this branch in a second pass for duplicate img hrefs.
+ promotablePreloads.delete(key); // $FlowFixMe - Flow should understand that this is a Resource if the condition was true
+
+ renderState.highImagePreloads.add(resource);
+ }
+ } else if (!resumableState.imageResources.hasOwnProperty(key)) {
+ // We must construct a new preload resource
+ resumableState.imageResources[key] = PRELOAD_NO_CREDS;
+ resource = [];
+ pushLinkImpl(resource, {
rel: 'preload',
as: 'image',
// There is a bug in Safari where imageSrcSet is not respected on preload links
@@ -3386,25 +3433,15 @@ function pushImg(target, props, resumableState, renderState, pictureTagInScope)
type: props.type,
fetchPriority: props.fetchPriority,
referrerPolicy: props.referrerPolicy
- };
- resource = {
- type: 'preload',
- chunks: [],
- state: NoState,
- props: preloadProps
- };
- resumableState.preloadsMap[key] = preloadProps;
- renderState.preloadsMap.set(key, resource);
- pushLinkImpl(resource.chunks, preloadProps);
- } else {
- resource = renderState.preloadsMap.get(key);
- }
+ });
- if (resource) {
if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
renderState.highImagePreloads.add(resource);
} else {
- renderState.bulkPreloads.add(resource);
+ renderState.bulkPreloads.add(resource); // We can bump the priority up if the same img is rendered later
+ // with fetchPriority="high"
+
+ promotablePreloads.set(key, resource);
}
}
}
@@ -3578,35 +3615,50 @@ function pushScript(target, props, resumableState, renderState, textEmbedded, in
}
var src = props.src;
- var key = getResourceKey('script', src); // We can make this ');
@@ -1780,12 +1798,16 @@ function createRenderState(resumableState, nonce, bootstrapScriptContent, bootst
fontPreloads: new Set(),
highImagePreloads: new Set(),
// usedImagePreloads: new Set(),
- precedences: new Map(),
- stylePrecedences: new Map(),
+ styles: new Map(),
bootstrapScripts: new Set(),
scripts: new Set(),
bulkPreloads: new Set(),
- preloadsMap: new Map(),
+ preloads: {
+ images: new Map(),
+ stylesheets: new Map(),
+ scripts: new Map(),
+ moduleScripts: new Map()
+ },
nonce: nonce,
// like a module global for currently rendering boundary
boundaryResources: null,
@@ -1795,17 +1817,32 @@ function createRenderState(resumableState, nonce, bootstrapScriptContent, bootst
if (bootstrapScripts !== undefined) {
for (var i = 0; i < bootstrapScripts.length; i++) {
var scriptConfig = bootstrapScripts[i];
- var src = typeof scriptConfig === 'string' ? scriptConfig : scriptConfig.src;
- var integrity = typeof scriptConfig === 'string' ? undefined : scriptConfig.integrity;
- var crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
- preloadBootstrapScript(resumableState, renderState, src, nonce, integrity, crossOrigin);
+ var src = void 0,
+ crossOrigin = void 0,
+ integrity = void 0;
+ var props = {
+ rel: 'preload',
+ as: 'script',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
+
+ if (typeof scriptConfig === 'string') {
+ props.href = src = scriptConfig;
+ } else {
+ props.href = src = scriptConfig.src;
+ props.integrity = integrity = typeof scriptConfig.integrity === 'string' ? scriptConfig.integrity : undefined;
+ props.crossOrigin = crossOrigin = typeof scriptConfig === 'string' || scriptConfig.crossOrigin == null ? undefined : scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
+
+ preloadBootstrapScriptOrModule(resumableState, renderState, src, props);
bootstrapChunks.push(startScriptSrc, stringToChunk(escapeTextForBrowser(src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (integrity) {
+ if (typeof integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(integrity)));
}
@@ -1821,20 +1858,32 @@ function createRenderState(resumableState, nonce, bootstrapScriptContent, bootst
for (var _i = 0; _i < bootstrapModules.length; _i++) {
var _scriptConfig = bootstrapModules[_i];
- var _src = typeof _scriptConfig === 'string' ? _scriptConfig : _scriptConfig.src;
+ var _src = void 0,
+ _crossOrigin = void 0,
+ _integrity = void 0;
- var _integrity = typeof _scriptConfig === 'string' ? undefined : _scriptConfig.integrity;
+ var _props = {
+ rel: 'modulepreload',
+ fetchPriority: 'low',
+ nonce: nonce
+ };
- var _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ if (typeof _scriptConfig === 'string') {
+ _props.href = _src = _scriptConfig;
+ } else {
+ _props.href = _src = _scriptConfig.src;
+ _props.integrity = _integrity = typeof _scriptConfig.integrity === 'string' ? _scriptConfig.integrity : undefined;
+ _props.crossOrigin = _crossOrigin = typeof _scriptConfig === 'string' || _scriptConfig.crossOrigin == null ? undefined : _scriptConfig.crossOrigin === 'use-credentials' ? 'use-credentials' : '';
+ }
- preloadBootstrapModule(resumableState, renderState, _src, nonce, _integrity, _crossOrigin);
+ preloadBootstrapScriptOrModule(resumableState, renderState, _src, _props);
bootstrapChunks.push(startModuleSrc, stringToChunk(escapeTextForBrowser(_src)));
if (nonce) {
bootstrapChunks.push(scriptNonce, stringToChunk(escapeTextForBrowser(nonce)));
}
- if (_integrity) {
+ if (typeof _integrity === 'string') {
bootstrapChunks.push(scriptIntegirty, stringToChunk(escapeTextForBrowser(_integrity)));
}
@@ -1867,10 +1916,18 @@ function createResumableState(identifierPrefix, externalRuntimeConfig) {
hasHtml: false,
// @TODO add bootstrap script to implicit preloads
// persistent
- preloadsMap: {},
- preconnectsMap: {},
- stylesMap: {},
- scriptsMap: {}
+ unknownResources: {},
+ dnsResources: {},
+ connectResources: {
+ default: {},
+ anonymous: {},
+ credentials: {}
+ },
+ imageResources: {},
+ styleResources: {},
+ scriptResources: {},
+ moduleUnknownResources: {},
+ moduleScriptResources: {}
};
} // Constants for the insertion mode we're currently writing in. We don't encode all HTML5 insertion
// modes. We only include the variants as they matter for the sake of our purposes.
@@ -3090,7 +3147,7 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
if (props.rel === 'stylesheet') {
// This may hoistable as a Stylesheet Resource, otherwise it will emit in place
- var key = getResourceKey('style', href);
+ var key = getResourceKey(href);
if (typeof precedence !== 'string' || props.disabled != null || props.onLoad || props.onError) {
// This stylesheet is either not opted into Resource semantics or has conflicting properties which
@@ -3111,67 +3168,58 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
return pushLinkImpl(target, props);
} else {
// This stylesheet refers to a Resource and we create a new one if necessary
- var stylesInPrecedence = renderState.precedences.get(precedence);
-
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- var resourceProps = stylesheetPropsFromRawProps(props);
- var state = NoState;
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
+
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS; // If this is the first time we've encountered this precedence we need
+ // to create a StyleQueue
+
+ if (!styleQueue) {
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
+ }
- if (resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = resumableState.preloadsMap[key];
- adoptPreloadPropsForStylesheetProps(resourceProps, preloadProps);
- var preloadResource = renderState.preloadsMap.get(key);
+ var resource = {
+ state: PENDING$1,
+ props: stylesheetPropsFromRawProps(props)
+ };
- if (preloadResource) {
- // If we already had a preload we don't want that resource to flush directly.
- // We let the newly created resource govern flushing.
- preloadResource.state |= Blocked;
+ if (resourceState) {
+ // When resourceState is truty it is a Preload state. We cast it for clarity
+ var preloadState = resourceState;
- if (preloadResource.state & Flushed) {
- state = PreloadFlushed;
- }
- } else {
- // If we resumed then we assume that this was already flushed
- // by the shell.
- state = PreloadFlushed;
+ if (preloadState.length === 2) {
+ adoptPreloadCredentials(resource.props, preloadState);
}
- }
- var resource = {
- type: 'stylesheet',
- chunks: [],
- state: state,
- props: resourceProps
- };
- resumableState.stylesMap[key] = null;
-
- if (!stylesInPrecedence) {
- stylesInPrecedence = new Map();
- renderState.precedences.set(precedence, stylesInPrecedence);
- var emptyStyleResource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: []
- }
- };
- stylesInPrecedence.set('', emptyStyleResource);
+ var preloadResource = renderState.preloads.stylesheets.get(key);
- {
- if (renderState.stylePrecedences.has(precedence)) {
- error('React constructed an empty style resource when a style resource already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ if (preloadResource && preloadResource.length > 0) {
+ // The Preload for this resource was created in this render pass and has not flushed yet so
+ // we need to clear it to avoid it flushing.
+ preloadResource.length = 0;
+ } else {
+ // Either the preload resource from this render already flushed in this render pass
+ // or the preload flushed in a prior pass (prerender). In either case we need to mark
+ // this resource as already having been preloaded.
+ resource.state = PRELOADED;
}
+ } // We add the newly created resource to our StyleQueue and if necessary
+ // track the resource with the currently rendering boundary
- renderState.stylePrecedences.set(precedence, emptyStyleResource);
- }
- stylesInPrecedence.set(key, resource);
+ styleQueue.sheets.set(key, resource);
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.stylesheets.add(resource);
}
} else {
// We need to track whether this boundary should wait on this resource or not.
@@ -3179,12 +3227,12 @@ function pushLink(target, props, resumableState, renderState, textEmbedded, inse
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
- if (stylesInPrecedence) {
- var _resource = stylesInPrecedence.get(key);
+ if (styleQueue) {
+ var _resource = styleQueue.sheets.get(key);
if (_resource) {
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(_resource);
+ renderState.boundaryResources.stylesheets.add(_resource);
}
}
}
@@ -3282,47 +3330,47 @@ function pushStyle(target, props, resumableState, renderState, textEmbedded, ins
}
}
- var key = getResourceKey('style', href);
- var resource = renderState.stylePrecedences.get(precedence);
+ var key = getResourceKey(href);
+ var styleQueue = renderState.styles.get(precedence);
+ var hasKey = resumableState.styleResources.hasOwnProperty(key);
+ var resourceState = hasKey ? resumableState.styleResources[key] : undefined;
- if (!resumableState.stylesMap.hasOwnProperty(key)) {
- if (!resource) {
- resource = {
- type: 'style',
- chunks: [],
- state: NoState,
- props: {
- precedence: precedence,
- hrefs: [href]
- }
- };
- renderState.stylePrecedences.set(precedence, resource);
- var stylesInPrecedence = new Map();
- stylesInPrecedence.set('', resource);
+ if (resourceState !== EXISTS) {
+ // We are going to create this resource now so it is marked as Exists
+ resumableState.styleResources[key] = EXISTS;
- {
- if (renderState.precedences.has(precedence)) {
- error('React constructed a new style precedence set when one already exists for this precedence: "%s". This is a bug in React.', precedence);
- }
+ {
+ if (resourceState) {
+ error('React encountered a hoistable style tag for the same href as a preload: "%s". When using a style tag to inline styles you should not also preload it as a stylsheet.', href);
}
+ }
- renderState.precedences.set(precedence, stylesInPrecedence);
+ if (!styleQueue) {
+ // This is the first time we've encountered this precedence we need
+ // to create a StyleQueue.
+ styleQueue = {
+ precedence: stringToChunk(escapeTextForBrowser(precedence)),
+ rules: [],
+ hrefs: [stringToChunk(escapeTextForBrowser(href))],
+ sheets: new Map()
+ };
+ renderState.styles.set(precedence, styleQueue);
} else {
- resource.props.hrefs.push(href);
+ // We have seen this precedence before and need to track this href
+ styleQueue.hrefs.push(stringToChunk(escapeTextForBrowser(href)));
}
- resumableState.stylesMap[key] = null;
- pushStyleContents(resource.chunks, props);
+ pushStyleContents(styleQueue.rules, props);
}
- if (resource) {
+ if (styleQueue) {
// We need to track whether this boundary should wait on this resource or not.
// Typically this resource should always exist since we either had it or just created
// it. However, it's possible when you resume that the style has already been emitted
// and then it wouldn't be recreated in the RenderState and there's no need to track
// it again since we should've hoisted it to the shell already.
if (renderState.boundaryResources) {
- renderState.boundaryResources.add(resource);
+ renderState.boundaryResources.styles.add(styleQueue);
}
}
@@ -3411,36 +3459,35 @@ function pushStyleContents(target, props) {
return;
}
-function getImagePreloadKey(href, imageSrcSet, imageSizes) {
- var uniquePart = '';
-
- if (typeof imageSrcSet === 'string' && imageSrcSet !== '') {
- uniquePart += '[' + imageSrcSet + ']';
-
- if (typeof imageSizes === 'string') {
- uniquePart += '[' + imageSizes + ']';
- }
- } else {
- uniquePart += '[][]' + href;
- }
-
- return getResourceKey('image', uniquePart);
-}
-
function pushImg(target, props, resumableState, renderState, pictureTagInScope) {
var src = props.src,
srcSet = props.srcSet;
- if (props.loading !== 'lazy' && (typeof src === 'string' || typeof srcSet === 'string') && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
+ if (props.loading !== 'lazy' && (src || srcSet) && (typeof src === 'string' || src == null) && (typeof srcSet === 'string' || srcSet == null) && props.fetchPriority !== 'low' && pictureTagInScope === false && // We exclude data URIs in src and srcSet since these should not be preloaded
!(typeof src === 'string' && src[4] === ':' && (src[0] === 'd' || src[0] === 'D') && (src[1] === 'a' || src[1] === 'A') && (src[2] === 't' || src[2] === 'T') && (src[3] === 'a' || src[3] === 'A')) && !(typeof srcSet === 'string' && srcSet[4] === ':' && (srcSet[0] === 'd' || srcSet[0] === 'D') && (srcSet[1] === 'a' || srcSet[1] === 'A') && (srcSet[2] === 't' || srcSet[2] === 'T') && (srcSet[3] === 'a' || srcSet[3] === 'A'))) {
// We have a suspensey image and ought to preload it to optimize the loading of display blocking
// resumableState.
- var sizes = props.sizes;
- var key = getImagePreloadKey(src, srcSet, sizes);
- var resource;
+ var sizes = typeof props.sizes === 'string' ? props.sizes : undefined;
+ var key = getImageResourceKey(src, srcSet, sizes);
+ var promotablePreloads = renderState.preloads.images;
+ var resource = promotablePreloads.get(key);
- if (!resumableState.preloadsMap.hasOwnProperty(key)) {
- var preloadProps = {
+ if (resource) {
+ // We consider whether this preload can be promoted to higher priority flushing queue.
+ // The only time a resource will exist here is if it was created during this render
+ // and was not already in the high priority queue.
+ if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
+ // Delete the resource from the map since we are promoting it and don't want to
+ // reenter this branch in a second pass for duplicate img hrefs.
+ promotablePreloads.delete(key); // $FlowFixMe - Flow should understand that this is a Resource if the condition was true
+
+ renderState.highImagePreloads.add(resource);
+ }
+ } else if (!resumableState.imageResources.hasOwnProperty(key)) {
+ // We must construct a new preload resource
+ resumableState.imageResources[key] = PRELOAD_NO_CREDS;
+ resource = [];
+ pushLinkImpl(resource, {
rel: 'preload',
as: 'image',
// There is a bug in Safari where imageSrcSet is not respected on preload links
@@ -3455,25 +3502,15 @@ function pushImg(target, props, resumableState, renderState, pictureTagInScope)
type: props.type,
fetchPriority: props.fetchPriority,
referrerPolicy: props.referrerPolicy
- };
- resource = {
- type: 'preload',
- chunks: [],
- state: NoState,
- props: preloadProps
- };
- resumableState.preloadsMap[key] = preloadProps;
- renderState.preloadsMap.set(key, resource);
- pushLinkImpl(resource.chunks, preloadProps);
- } else {
- resource = renderState.preloadsMap.get(key);
- }
+ });
- if (resource) {
if (props.fetchPriority === 'high' || renderState.highImagePreloads.size < 10) {
renderState.highImagePreloads.add(resource);
} else {
- renderState.bulkPreloads.add(resource);
+ renderState.bulkPreloads.add(resource); // We can bump the priority up if the same img is rendered later
+ // with fetchPriority="high"
+
+ promotablePreloads.set(key, resource);
}
}
}
@@ -3647,35 +3684,50 @@ function pushScript(target, props, resumableState, renderState, textEmbedded, in
}
var src = props.src;
- var key = getResourceKey('script', src); // We can make this