@@ -296,6 +296,8 @@ export function createResponseState(
296
296
const integrity =
297
297
typeof scriptConfig === 'string' ? undefined : scriptConfig . integrity ;
298
298
299
+ preloadBootstrapModule ( resources , src ) ;
300
+
299
301
bootstrapChunks . push (
300
302
startModuleSrc ,
301
303
stringToChunk ( escapeTextForBrowser ( src ) ) ,
@@ -1886,7 +1888,7 @@ function pushLink(
1886
1888
}
1887
1889
}
1888
1890
pushLinkImpl ( resource . chunks , resource . props ) ;
1889
- resources. usedStylesheets . add ( resource ) ;
1891
+ resources. usedStylesheets . set ( key , resource ) ;
1890
1892
return pushLinkImpl ( target , props ) ;
1891
1893
} else {
1892
1894
// This stylesheet refers to a Resource and we create a new one if necessary
@@ -4158,8 +4160,7 @@ export function writePreamble(
4158
4160
// Flush unblocked stylesheets by precedence
4159
4161
resources . precedences . forEach ( flushAllStylesInPreamble , destination ) ;
4160
4162
4161
- resources . usedStylesheets . forEach ( resource => {
4162
- const key = getResourceKey ( resource . props . as , resource . props . href ) ;
4163
+ resources . usedStylesheets . forEach ( ( resource , key ) => {
4163
4164
if ( resources . stylesMap . has ( key ) ) {
4164
4165
// The underlying stylesheet is represented both as a used stylesheet
4165
4166
// (a regular component we will attempt to preload) and as a StylesheetResource.
@@ -4254,8 +4255,7 @@ export function writeHoistables(
4254
4255
// but we want to kick off preloading as soon as possible
4255
4256
resources . precedences . forEach ( preloadLateStyles , destination ) ;
4256
4257
4257
- resources . usedStylesheets . forEach ( resource => {
4258
- const key = getResourceKey ( resource . props . as , resource . props . href ) ;
4258
+ resources . usedStylesheets . forEach ( ( resource , key ) => {
4259
4259
if ( resources . stylesMap . has ( key ) ) {
4260
4260
// The underlying stylesheet is represented both as a used stylesheet
4261
4261
// (a regular component we will attempt to preload) and as a StylesheetResource.
@@ -4770,12 +4770,18 @@ type PreconnectProps = {
4770
4770
} ;
4771
4771
type PreconnectResource = TResource < 'preconnect' , null > ;
4772
4772
4773
- type PreloadProps = {
4773
+ type PreloadAsProps = {
4774
4774
rel : 'preload' ,
4775
4775
as : string ,
4776
4776
href : string ,
4777
4777
[ string ] : mixed ,
4778
4778
} ;
4779
+ type PreloadModuleProps = {
4780
+ rel : 'modulepreload' ,
4781
+ href : string ,
4782
+ [ string ] : mixed ,
4783
+ } ;
4784
+ type PreloadProps = PreloadAsProps | PreloadModuleProps ;
4779
4785
type PreloadResource = TResource < 'preload' , PreloadProps > ;
4780
4786
4781
4787
type StylesheetProps = {
@@ -4820,7 +4826,7 @@ export type Resources = {
4820
4826
// usedImagePreloads: Set<PreloadResource>,
4821
4827
precedences : Map < string , Set < StyleResource> > ,
4822
4828
stylePrecedences : Map < string , StyleTagResource> ,
4823
- usedStylesheets : Set < PreloadResource > ,
4829
+ usedStylesheets : Map < string , PreloadResource> ,
4824
4830
scripts : Set < ScriptResource > ,
4825
4831
usedScripts : Set < PreloadResource > ,
4826
4832
explicitStylesheetPreloads : Set < PreloadResource > ,
@@ -4848,7 +4854,7 @@ export function createResources(): Resources {
4848
4854
// usedImagePreloads: new Set(),
4849
4855
precedences : new Map ( ) ,
4850
4856
stylePrecedences : new Map ( ) ,
4851
- usedStylesheets : new Set ( ) ,
4857
+ usedStylesheets : new Map ( ) ,
4852
4858
scripts : new Set ( ) ,
4853
4859
usedScripts : new Set ( ) ,
4854
4860
explicitStylesheetPreloads : new Set ( ) ,
@@ -5414,6 +5420,39 @@ function preloadBootstrapScript(resources: Resources, src: string): void {
5414
5420
pushLinkImpl ( resource . chunks , props ) ;
5415
5421
}
5416
5422
5423
+ // This function is only safe to call at Request start time since it assumes
5424
+ // that each module has not already been preloaded. If we find a need to preload
5425
+ // scripts at any other point in time we will need to check whether the preload
5426
+ // already exists and not assume it
5427
+ function preloadBootstrapModule ( resources : Resources , src : string ) : void {
5428
+ const key = getResourceKey ( 'script' , src ) ;
5429
+ if ( __DEV__ ) {
5430
+ if ( resources . preloadsMap . has ( key ) ) {
5431
+ // This is coded as a React error because it should be impossible for a userspace preload to preempt this call
5432
+ // If a userspace preload can preempt it then this assumption is broken and we need to reconsider this strategy
5433
+ // rather than instruct the user to not preload their bootstrap scripts themselves
5434
+ console . error (
5435
+ 'Internal React Error: React expected bootstrap module with src "%s" to not have been preloaded already. please file an issue' ,
5436
+ src ,
5437
+ ) ;
5438
+ }
5439
+ }
5440
+ const props : PreloadModuleProps = {
5441
+ rel : 'modulepreload ',
5442
+ href : src ,
5443
+ } ;
5444
+ const resource : PreloadResource = {
5445
+ type : 'preload' ,
5446
+ chunks : [ ] ,
5447
+ state : NoState ,
5448
+ props,
5449
+ } ;
5450
+ resources . preloadsMap . set ( key , resource ) ;
5451
+ resources . explicitScriptPreloads . add ( resource ) ;
5452
+ pushLinkImpl ( resource . chunks , props ) ;
5453
+ return ;
5454
+ }
5455
+
5417
5456
function internalPreinitScript (
5418
5457
resources : Resources ,
5419
5458
src : string ,
0 commit comments