1
- import { DATA_FETCH_QUERY , MF_DATA_FETCH_STATUS } from '../../constant' ;
2
- import logger from '../../logger' ;
3
- import { getDataFetchMap } from '../../utils' ;
4
- import {
5
- fetchData ,
6
- initDataFetchMap ,
7
- loadDataFetchModule ,
8
- } from '../../utils/dataFetch' ;
9
- import { SEPARATOR , MANIFEST_EXT } from '@module-federation/sdk' ;
10
- import type {
11
- MiddlewareHandler ,
12
- ServerPlugin ,
13
- } from '@modern-js/server-runtime' ;
14
- import type { NoSSRRemoteInfo } from '../../interfaces/global' ;
1
+ import dataFetchMiddleWare from '@module-federation/react/data-fetch-server-middleware' ;
15
2
16
- function wrapSetTimeout (
17
- targetPromise : Promise < unknown > ,
18
- delay = 20000 ,
19
- id : string ,
20
- ) {
21
- if ( targetPromise && typeof targetPromise . then === 'function' ) {
22
- return new Promise ( ( resolve , reject ) => {
23
- const timeoutId = setTimeout ( ( ) => {
24
- logger . warn ( `Data fetch for ID ${ id } timed out after 20 seconds.` ) ;
25
- reject ( new Error ( `Data fetch for ID ${ id } timed out after 20 seconds` ) ) ;
26
- } , delay ) ;
27
-
28
- targetPromise
29
- . then ( ( value : any ) => {
30
- clearTimeout ( timeoutId ) ;
31
- resolve ( value ) ;
32
- } )
33
- . catch ( ( err : any ) => {
34
- clearTimeout ( timeoutId ) ;
35
- reject ( err ) ;
36
- } ) ;
37
- } ) ;
38
- }
39
- }
40
-
41
- function addProtocol ( url : string ) {
42
- if ( url . startsWith ( '//' ) ) {
43
- return 'https:' + url ;
44
- }
45
- return url ;
46
- }
47
-
48
- const getDecodeQuery = ( url : URL , name : string ) => {
49
- const res = url . searchParams . get ( name ) ;
50
- if ( ! res ) {
51
- return null ;
52
- }
53
- return decodeURIComponent ( res ) ;
54
- } ;
55
-
56
- const middleware : MiddlewareHandler = async ( ctx , next ) => {
57
- let url : URL ;
58
- let dataFetchId : string | null ;
59
- let params : Record < string , unknown > ;
60
- let remoteInfo : NoSSRRemoteInfo ;
61
- try {
62
- url = new URL ( ctx . req . url ) ;
63
- dataFetchId = getDecodeQuery ( url , DATA_FETCH_QUERY ) ;
64
- params = JSON . parse ( getDecodeQuery ( url , 'params' ) || '{}' ) ;
65
- const remoteInfoQuery = getDecodeQuery ( url , 'remoteInfo' ) ;
66
- remoteInfo = remoteInfoQuery ? JSON . parse ( remoteInfoQuery ) : null ;
67
- } catch ( e ) {
68
- logger . error ( 'fetch data from server, error: ' , e ) ;
69
- return next ( ) ;
70
- }
71
-
72
- if ( ! dataFetchId ) {
73
- return next ( ) ;
74
- }
75
- logger . log ( 'fetch data from server, dataFetchId: ' , dataFetchId ) ;
76
- logger . debug (
77
- 'fetch data from server, moduleInfo: ' ,
78
- globalThis . __FEDERATION__ ?. moduleInfo ,
79
- ) ;
80
- try {
81
- const dataFetchMap = getDataFetchMap ( ) ;
82
- if ( ! dataFetchMap ) {
83
- initDataFetchMap ( ) ;
84
- }
85
- const fetchDataPromise = dataFetchMap [ dataFetchId ] ?. [ 1 ] ;
86
- logger . debug (
87
- 'fetch data from server, fetchDataPromise: ' ,
88
- fetchDataPromise ,
89
- ) ;
90
- if (
91
- fetchDataPromise &&
92
- dataFetchMap [ dataFetchId ] ?. [ 2 ] !== MF_DATA_FETCH_STATUS . ERROR
93
- ) {
94
- const targetPromise = fetchDataPromise [ 0 ] ;
95
- // Ensure targetPromise is thenable
96
- const wrappedPromise = wrapSetTimeout ( targetPromise , 20000 , dataFetchId ) ;
97
- if ( wrappedPromise ) {
98
- const res = await wrappedPromise ;
99
- logger . log ( 'fetch data from server, fetchDataPromise res: ' , res ) ;
100
- return ctx . json ( res ) ;
101
- }
102
- logger . error (
103
- `Expected a Promise from fetchDataPromise[0] for dataFetchId ${ dataFetchId } , but received:` ,
104
- targetPromise ,
105
- 'Will try call new dataFetch again...' ,
106
- ) ;
107
- }
108
-
109
- if ( remoteInfo ) {
110
- try {
111
- const hostInstance = globalThis . __FEDERATION__ . __INSTANCES__ [ 0 ] ;
112
- const remoteEntry = `${ addProtocol ( remoteInfo . ssrPublicPath ) + remoteInfo . ssrRemoteEntry } ` ;
113
- if ( ! hostInstance ) {
114
- throw new Error ( 'host instance not found!' ) ;
115
- }
116
- const remote = hostInstance . options . remotes . find (
117
- ( remote ) => remote . name === remoteInfo . name ,
118
- ) ;
119
- logger . debug ( 'find remote: ' , JSON . stringify ( remote ) ) ;
120
- if ( ! remote ) {
121
- hostInstance . registerRemotes ( [
122
- {
123
- name : remoteInfo . name ,
124
- entry : remoteEntry ,
125
- entryGlobalName : remoteInfo . globalName ,
126
- } ,
127
- ] ) ;
128
- } else if (
129
- ! ( 'entry' in remote ) ||
130
- ! remote . entry . includes ( MANIFEST_EXT )
131
- ) {
132
- const { hostGlobalSnapshot, remoteSnapshot } =
133
- hostInstance . snapshotHandler . getGlobalRemoteInfo ( remoteInfo ) ;
134
- logger . debug (
135
- 'find hostGlobalSnapshot: ' ,
136
- JSON . stringify ( hostGlobalSnapshot ) ,
137
- ) ;
138
- logger . debug ( 'find remoteSnapshot: ' , JSON . stringify ( remoteSnapshot ) ) ;
139
-
140
- if ( ! hostGlobalSnapshot || ! remoteSnapshot ) {
141
- if ( 'version' in remote ) {
142
- // @ts -ignore
143
- delete remote . version ;
144
- }
145
- // @ts -ignore
146
- remote . entry = remoteEntry ;
147
- remote . entryGlobalName = remoteInfo . globalName ;
148
- }
149
- }
150
- } catch ( e ) {
151
- ctx . status ( 500 ) ;
152
- return ctx . text (
153
- `failed to fetch ${ remoteInfo . name } data, error:\n ${ e } ` ,
154
- ) ;
155
- }
156
- }
157
-
158
- const dataFetchItem = dataFetchMap [ dataFetchId ] ;
159
- logger . debug ( 'fetch data from server, dataFetchItem: ' , dataFetchItem ) ;
160
- if ( dataFetchItem ) {
161
- const callFetchDataPromise = fetchData ( dataFetchId , {
162
- ...params ,
163
- isDowngrade : ! remoteInfo ,
164
- } ) ;
165
- const wrappedPromise = wrapSetTimeout (
166
- callFetchDataPromise ,
167
- 20000 ,
168
- dataFetchId ,
169
- ) ;
170
- if ( wrappedPromise ) {
171
- const res = await wrappedPromise ;
172
- logger . log ( 'fetch data from server, dataFetchItem res: ' , res ) ;
173
- return ctx . json ( res ) ;
174
- }
175
- }
176
-
177
- const remoteId = dataFetchId . split ( SEPARATOR ) [ 0 ] ;
178
- const hostInstance = globalThis . __FEDERATION__ . __INSTANCES__ [ 0 ] ;
179
- if ( ! hostInstance ) {
180
- throw new Error ( 'host instance not found!' ) ;
181
- }
182
- const dataFetchFn = await loadDataFetchModule ( hostInstance , remoteId ) ;
183
- const data = await dataFetchFn ( { ...params , isDowngrade : ! remoteInfo } ) ;
184
- logger . log ( 'fetch data from server, loadDataFetchModule res: ' , data ) ;
185
- return ctx . json ( data ) ;
186
- } catch ( e ) {
187
- logger . error ( 'server plugin data fetch error: ' , e ) ;
188
- ctx . status ( 500 ) ;
189
- return ctx . text ( `failed to fetch ${ remoteInfo . name } data, error:\n ${ e } ` ) ;
190
- }
191
- } ;
3
+ import type { ServerPlugin } from '@modern-js/server-runtime' ;
192
4
193
5
const dataFetchServePlugin = ( ) : ServerPlugin => ( {
194
6
name : 'mf-data-fetch-server-plugin' ,
@@ -198,7 +10,7 @@ const dataFetchServePlugin = (): ServerPlugin => ({
198
10
middlewares . push ( {
199
11
name : 'module-federation-serve-manifest' ,
200
12
// @ts -ignore type error
201
- handler : middleware ,
13
+ handler : dataFetchMiddleWare ,
202
14
} ) ;
203
15
} ) ;
204
16
} ,
0 commit comments