@@ -39,14 +39,13 @@ interface NodeImportResolveOptions
3939} 
4040
4141const  pendingModules  =  new  Map < string ,  Promise < SSRModule > > ( ) 
42- const  pendingImports  =  new  Map < string ,  string [ ] > ( ) 
42+ const  pendingModuleDependencyGraph  =  new  Map < string ,  Set < string > > ( ) 
4343const  importErrors  =  new  WeakMap < Error ,  {  importee : string  } > ( ) 
4444
4545export  async  function  ssrLoadModule ( 
4646  url : string , 
4747  server : ViteDevServer , 
4848  context : SSRContext  =  {  global } , 
49-   urlStack : string [ ]  =  [ ] , 
5049  fixStacktrace ?: boolean , 
5150) : Promise < SSRModule >  { 
5251  url  =  unwrapId ( url ) 
@@ -60,17 +59,11 @@ export async function ssrLoadModule(
6059    return  pending 
6160  } 
6261
63-   const  modulePromise  =  instantiateModule ( 
64-     url , 
65-     server , 
66-     context , 
67-     urlStack , 
68-     fixStacktrace , 
69-   ) 
62+   const  modulePromise  =  instantiateModule ( url ,  server ,  context ,  fixStacktrace ) 
7063  pendingModules . set ( url ,  modulePromise ) 
7164  modulePromise 
7265    . catch ( ( )  =>  { 
73-       pendingImports . delete ( url ) 
66+       /* prevent unhandled promise rejection error from bubbling up */ 
7467    } ) 
7568    . finally ( ( )  =>  { 
7669      pendingModules . delete ( url ) 
@@ -82,7 +75,6 @@ async function instantiateModule(
8275  url : string , 
8376  server : ViteDevServer , 
8477  context : SSRContext  =  {  global } , 
85-   urlStack : string [ ]  =  [ ] , 
8678  fixStacktrace ?: boolean , 
8779) : Promise < SSRModule >  { 
8880  const  {  moduleGraph }  =  server 
@@ -122,9 +114,6 @@ async function instantiateModule(
122114    url : pathToFileURL ( mod . file ! ) . toString ( ) , 
123115  } 
124116
125-   urlStack  =  urlStack . concat ( url ) 
126-   const  isCircular  =  ( url : string )  =>  urlStack . includes ( url ) 
127- 
128117  const  { 
129118    isProduction, 
130119    resolve : {  dedupe,  preserveSymlinks } , 
@@ -150,38 +139,37 @@ async function instantiateModule(
150139    packageCache : server . config . packageCache , 
151140  } 
152141
153-   // Since dynamic imports can happen in parallel, we need to 
154-   // account for multiple pending deps and duplicate imports. 
155-   const  pendingDeps : string [ ]  =  [ ] 
156- 
157142  const  ssrImport  =  async  ( dep : string ,  metadata ?: SSRImportBaseMetadata )  =>  { 
158143    try  { 
159144      if  ( dep [ 0 ]  !==  '.'  &&  dep [ 0 ]  !==  '/' )  { 
160145        return  await  nodeImport ( dep ,  mod . file ! ,  resolveOptions ,  metadata ) 
161146      } 
162147      // convert to rollup URL because `pendingImports`, `moduleGraph.urlToModuleMap` requires that 
163148      dep  =  unwrapId ( dep ) 
164-       if  ( ! isCircular ( dep )  &&  ! pendingImports . get ( dep ) ?. some ( isCircular ) )  { 
165-         pendingDeps . push ( dep ) 
166-         if  ( pendingDeps . length  ===  1 )  { 
167-           pendingImports . set ( url ,  pendingDeps ) 
168-         } 
169-         const  mod  =  await  ssrLoadModule ( 
170-           dep , 
171-           server , 
172-           context , 
173-           urlStack , 
174-           fixStacktrace , 
175-         ) 
176-         if  ( pendingDeps . length  ===  1 )  { 
177-           pendingImports . delete ( url ) 
178-         }  else  { 
179-           pendingDeps . splice ( pendingDeps . indexOf ( dep ) ,  1 ) 
149+ 
150+       // Handle any potential circular dependencies for static imports, preventing 
151+       // deadlock scenarios when two modules are indirectly waiting on one another 
152+       // to finish initializing. Dynamic imports are resolved at runtime, hence do 
153+       // not contribute to the static module dependency graph in the same way 
154+       if  ( ! metadata ?. isDynamicImport )  { 
155+         addPendingModuleDependency ( url ,  dep ) 
156+ 
157+         // If there's a circular dependency formed as a result of the dep import, 
158+         // return the current state of the dependent module being initialized, in 
159+         // order to avoid interlocking circular dependencies hanging indefinitely 
160+         if  ( checkModuleDependencyExists ( dep ,  url ) )  { 
161+           const  depSsrModule  =  moduleGraph . urlToModuleMap . get ( dep ) ?. ssrModule 
162+           if  ( ! depSsrModule )  { 
163+             // Technically, this should never happen under normal circumstances 
164+             throw  new  Error ( 
165+               '[vite] The dependency module is not yet fully initialized due to circular dependency. This is a bug in Vite SSR' , 
166+             ) 
167+           } 
168+           return  depSsrModule 
180169        } 
181-         // return local module to avoid race condition #5470 
182-         return  mod 
183170      } 
184-       return  moduleGraph . urlToModuleMap . get ( dep ) ?. ssrModule 
171+ 
172+       return  ssrLoadModule ( dep ,  server ,  context ,  fixStacktrace ) 
185173    }  catch  ( err )  { 
186174      // tell external error handler which mod was imported with error 
187175      importErrors . set ( err ,  {  importee : dep  } ) 
@@ -267,11 +255,52 @@ async function instantiateModule(
267255    ) 
268256
269257    throw  e 
258+   }  finally  { 
259+     pendingModuleDependencyGraph . delete ( url ) 
270260  } 
271261
272262  return  Object . freeze ( ssrModule ) 
273263} 
274264
265+ function  addPendingModuleDependency ( originUrl : string ,  depUrl : string ) : void { 
266+   if  ( pendingModuleDependencyGraph . has ( originUrl ) )  { 
267+     pendingModuleDependencyGraph . get ( originUrl ) ! . add ( depUrl ) 
268+   }  else  { 
269+     pendingModuleDependencyGraph . set ( originUrl ,  new  Set ( [ depUrl ] ) ) 
270+   } 
271+ } 
272+ 
273+ function  checkModuleDependencyExists ( 
274+   originUrl : string , 
275+   targetUrl : string , 
276+ ) : boolean  { 
277+   const  visited  =  new  Set ( ) 
278+   const  stack  =  [ originUrl ] 
279+ 
280+   while  ( stack . length )  { 
281+     const  currentUrl  =  stack . pop ( ) ! 
282+ 
283+     if  ( currentUrl  ===  targetUrl )  { 
284+       return  true 
285+     } 
286+ 
287+     if  ( ! visited . has ( currentUrl ) )  { 
288+       visited . add ( currentUrl ) 
289+ 
290+       const  dependencies  =  pendingModuleDependencyGraph . get ( currentUrl ) 
291+       if  ( dependencies )  { 
292+         for  ( const  depUrl  of  dependencies )  { 
293+           if  ( ! visited . has ( depUrl ) )  { 
294+             stack . push ( depUrl ) 
295+           } 
296+         } 
297+       } 
298+     } 
299+   } 
300+ 
301+   return  false 
302+ } 
303+ 
275304// In node@12+ we can use dynamic import to load CJS and ESM 
276305async  function  nodeImport ( 
277306  id : string , 
0 commit comments