@@ -2367,6 +2367,67 @@ function pushStyleContents(
23672367 return ;
23682368}
23692369
2370+ function getImagePreloadKey (
2371+ href : string ,
2372+ imageSrcSet : ?string ,
2373+ imageSizes : ?string ,
2374+ ) {
2375+ let uniquePart = '';
2376+ if ( typeof imageSrcSet === 'string' && imageSrcSet !== '' ) {
2377+ uniquePart += '[ ' + imageSrcSet + ' ] ';
2378+ if ( typeof imageSizes === 'string' ) {
2379+ uniquePart += '[ ' + imageSizes + ' ] ';
2380+ }
2381+ } else {
2382+ uniquePart += '[][]' + href ;
2383+ }
2384+ return getResourceKey ( 'image' , uniquePart ) ;
2385+ }
2386+
2387+ function pushImg (
2388+ target : Array < Chunk | PrecomputedChunk > ,
2389+ props : Object ,
2390+ resources : Resources ,
2391+ ) : null {
2392+ if ( props . loading !== 'lazy' && typeof props . src === 'string' ) {
2393+ // We have a suspensey image and ought to preload it to optimize the loading of display blocking
2394+ // resources.
2395+ const { src , imageSrcSet , imageSizes } = props ;
2396+ const key = getImagePreloadKey ( src , imageSrcSet , imageSizes ) ;
2397+ let resource = resources . preloadsMap . get ( key ) ;
2398+ if ( ! resource ) {
2399+ resource = {
2400+ type : 'preload ',
2401+ chunks : [ ] ,
2402+ state : NoState ,
2403+ props : {
2404+ rel : 'preload' ,
2405+ as : 'image' ,
2406+ // There is a bug in Safari where imageSrcSet is not respected on preload links
2407+ // so we omit the href here if we have imageSrcSet b/c safari will load the wrong image.
2408+ // This harms older browers that do not support imageSrcSet by making their preloads not work
2409+ // but this population is shrinking fast and is already small so we accept this tradeoff.
2410+ href : imageSrcSet ? undefined : src ,
2411+ imageSrcSet ,
2412+ imageSizes ,
2413+ crossOrigin : props . crossOrigin ,
2414+ integrity : props . integrity ,
2415+ type : props . type ,
2416+ fetchPriority : props . fetchPriority ,
2417+ referrerPolicy : props . referrerPolicy ,
2418+ } ,
2419+ } ;
2420+ resources . preloadsMap . set ( key , resource ) ;
2421+ if ( __DEV__ ) {
2422+ markAsRenderedResourceDEV ( resource , props ) ;
2423+ }
2424+ pushLinkImpl ( resource . chunks , resource . props ) ;
2425+ }
2426+ resources . suspenseyImages . add ( resource ) ;
2427+ }
2428+ return pushSelfClosing ( target , props , 'img' ) ;
2429+ }
2430+
23702431function pushSelfClosing (
23712432 target : Array < Chunk | PrecomputedChunk > ,
23722433 props : Object ,
@@ -3169,14 +3230,16 @@ export function pushStartInstance(
31693230 case 'pre ': {
31703231 return pushStartPreformattedElement ( target , props , type ) ;
31713232 }
3233+ case 'img ': {
3234+ return pushImg ( target , props , resources ) ;
3235+ }
31723236 // Omitted close tags
31733237 case 'base ':
31743238 case 'area ':
31753239 case 'br ':
31763240 case 'col ':
31773241 case 'embed ':
31783242 case 'hr ':
3179- case 'img ':
31803243 case 'keygen ':
31813244 case 'param ':
31823245 case 'source ':
@@ -4239,6 +4302,9 @@ export function writePreamble(
42394302 resources . fontPreloads . forEach ( flushResourceInPreamble , destination ) ;
42404303 resources . fontPreloads . clear ( ) ;
42414304
4305+ resources . suspenseyImages . forEach ( flushResourceInPreamble , destination ) ;
4306+ resources . suspenseyImages . clear ( ) ;
4307+
42424308 // Flush unblocked stylesheets by precedence
42434309 resources . precedences . forEach ( flushAllStylesInPreamble , destination ) ;
42444310
@@ -4305,6 +4371,9 @@ export function writeHoistables(
43054371 resources . fontPreloads . forEach ( flushResourceLate , destination ) ;
43064372 resources . fontPreloads . clear ( ) ;
43074373
4374+ resources . suspenseyImages . forEach ( flushResourceInPreamble , destination ) ;
4375+ resources . suspenseyImages . clear ( ) ;
4376+
43084377 // Preload any stylesheets. these will emit in a render instruction that follows this
43094378 // but we want to kick off preloading as soon as possible
43104379 resources . precedences . forEach ( preloadLateStyles , destination ) ;
@@ -4856,6 +4925,7 @@ export type Resources = {
48564925 // Flushing queues for Resource dependencies
48574926 preconnects : Set < PreconnectResource > ,
48584927 fontPreloads : Set < PreloadResource > ,
4928+ suspenseyImages : Set < PreloadResource > ,
48594929 // usedImagePreloads: Set<PreloadResource>,
48604930 precedences : Map < string , Set < StyleResource> > ,
48614931 stylePrecedences : Map < string , StyleTagResource> ,
@@ -4880,6 +4950,7 @@ export function createResources(): Resources {
48804950 // cleared on flush
48814951 preconnects : new Set ( ) ,
48824952 fontPreloads : new Set ( ) ,
4953+ suspenseyImages : new Set ( ) ,
48834954 // usedImagePreloads: new Set(),
48844955 precedences : new Map ( ) ,
48854956 stylePrecedences : new Map ( ) ,
@@ -5083,16 +5154,7 @@ export function preload(href: string, options: PreloadOptions) {
50835154 // both. This is to prevent identical calls with the same srcSet and sizes to be duplicated
50845155 // by varying the href. this is an edge case but it is the most correct behavior.
50855156 const { imageSrcSet, imageSizes} = options ;
5086- let uniquePart = '' ;
5087- if ( typeof imageSrcSet === 'string' && imageSrcSet !== '' ) {
5088- uniquePart += '[' + imageSrcSet + ']' ;
5089- if ( typeof imageSizes === 'string' ) {
5090- uniquePart += '[' + imageSizes + ']' ;
5091- }
5092- } else {
5093- uniquePart += '[][]' + href ;
5094- }
5095- key = getResourceKey ( as , uniquePart ) ;
5157+ key = getImagePreloadKey ( href , imageSrcSet , imageSizes ) ;
50965158 } else {
50975159 key = getResourceKey ( as , href ) ;
50985160 }
0 commit comments