7
7
* @flow
8
8
*/
9
9
10
+ import type { Thenable } from 'shared/ReactTypes' ;
11
+
10
12
export type WebpackSSRMap = {
11
13
[ clientId : string ] : {
12
14
[ clientExportName : string ] : ModuleMetaData ,
@@ -19,6 +21,7 @@ export opaque type ModuleMetaData = {
19
21
id : string ,
20
22
chunks : Array < string > ,
21
23
name : string ,
24
+ async : boolean ,
22
25
} ;
23
26
24
27
// eslint-disable-next-line no-unused-vars
@@ -29,7 +32,17 @@ export function resolveModuleReference<T>(
29
32
moduleData : ModuleMetaData ,
30
33
) : ModuleReference < T > {
31
34
if ( bundlerConfig ) {
32
- return bundlerConfig [ moduleData . id ] [ moduleData . name ] ;
35
+ const resolvedModuleData = bundlerConfig [ moduleData . id ] [ moduleData . name ] ;
36
+ if ( moduleData . async ) {
37
+ return {
38
+ id : resolvedModuleData . id ,
39
+ chunks : resolvedModuleData . chunks ,
40
+ name : resolvedModuleData . name ,
41
+ async : true ,
42
+ } ;
43
+ } else {
44
+ return resolvedModuleData ;
45
+ }
33
46
}
34
47
return moduleData ;
35
48
}
@@ -39,39 +52,72 @@ export function resolveModuleReference<T>(
39
52
// in Webpack but unfortunately it's not exposed so we have to
40
53
// replicate it in user space. null means that it has already loaded.
41
54
const chunkCache : Map < string , null | Promise < any > | Error> = new Map ( ) ;
55
+ const asyncModuleCache : Map < string , Thenable < any > > = new Map ( ) ;
42
56
43
57
// Start preloading the modules since we might need them soon.
44
58
// This function doesn't suspend.
45
59
export function preloadModule < T > ( moduleData : ModuleReference < T > ) : void {
46
60
const chunks = moduleData . chunks ;
61
+ const promises = [ ] ;
47
62
for ( let i = 0 ; i < chunks . length ; i ++ ) {
48
63
const chunkId = chunks [ i ] ;
49
64
const entry = chunkCache . get ( chunkId ) ;
50
65
if ( entry === undefined ) {
51
66
const thenable = __webpack_chunk_load__ ( chunkId ) ;
67
+ promises . push ( thenable ) ;
52
68
const resolve = chunkCache . set . bind ( chunkCache , chunkId , null ) ;
53
69
const reject = chunkCache . set . bind ( chunkCache , chunkId ) ;
54
70
thenable . then ( resolve , reject ) ;
55
71
chunkCache . set ( chunkId , thenable ) ;
56
72
}
57
73
}
74
+ if ( moduleData . async ) {
75
+ const modulePromise : any = Promise . all ( promises ) . then ( ( ) => {
76
+ return __webpack_require__ ( moduleData . id ) ;
77
+ } ) ;
78
+ modulePromise . then (
79
+ value => {
80
+ modulePromise . status = 'fulfilled' ;
81
+ modulePromise . value = value ;
82
+ } ,
83
+ reason => {
84
+ modulePromise . status = 'rejected' ;
85
+ modulePromise . reason = reason ;
86
+ } ,
87
+ ) ;
88
+ asyncModuleCache . set ( moduleData . id , modulePromise ) ;
89
+ }
58
90
}
59
91
60
92
// Actually require the module or suspend if it's not yet ready.
61
93
// Increase priority if necessary.
62
94
export function requireModule < T > (moduleData: ModuleReference< T > ): T {
63
- const chunks = moduleData . chunks ;
64
- for ( let i = 0 ; i < chunks . length ; i ++ ) {
65
- const chunkId = chunks [ i ] ;
66
- const entry = chunkCache . get ( chunkId ) ;
67
- if ( entry !== null ) {
68
- // We assume that preloadModule has been called before.
69
- // So we don't expect to see entry being undefined here, that's an error.
70
- // Let's throw either an error or the Promise.
71
- throw entry ;
95
+ let moduleExports ;
96
+ if ( moduleData . async ) {
97
+ // We assume that preloadModule has been called before, which
98
+ // should have added something to the module cache.
99
+ const promise : any = asyncModuleCache . get ( moduleData . id ) ;
100
+ if ( promise . status === 'fulfilled' ) {
101
+ moduleExports = promise . value ;
102
+ } else if (promise.status === 'rejected') {
103
+ throw promise . reason ;
104
+ } else {
105
+ throw promise ;
106
+ }
107
+ } else {
108
+ const chunks = moduleData . chunks ;
109
+ for ( let i = 0 ; i < chunks . length ; i ++ ) {
110
+ const chunkId = chunks [ i ] ;
111
+ const entry = chunkCache . get ( chunkId ) ;
112
+ if ( entry !== null ) {
113
+ // We assume that preloadModule has been called before.
114
+ // So we don't expect to see entry being undefined here, that's an error.
115
+ // Let's throw either an error or the Promise.
116
+ throw entry ;
117
+ }
72
118
}
119
+ moduleExports = __webpack_require__ ( moduleData . id ) ;
73
120
}
74
- const moduleExports = __webpack_require__ ( moduleData . id ) ;
75
121
if (moduleData.name === '*') {
76
122
// This is a placeholder value that represents that the caller imported this
77
123
// as a CommonJS module as is.
0 commit comments