@@ -61,6 +61,15 @@ export class ModuleGraph {
61
61
fileToModulesMap = new Map < string , Set < ModuleNode > > ( )
62
62
safeModulesPath = new Set < string > ( )
63
63
64
+ /**
65
+ * @internal
66
+ */
67
+ _unresolvedUrlToModuleMap = new Map < string , ModuleNode > ( )
68
+ /**
69
+ * @internal
70
+ */
71
+ _ssrUnresolvedUrlToModuleMap = new Map < string , ModuleNode > ( )
72
+
64
73
constructor (
65
74
private resolveId : (
66
75
url : string ,
@@ -72,7 +81,14 @@ export class ModuleGraph {
72
81
rawUrl : string ,
73
82
ssr ?: boolean ,
74
83
) : Promise < ModuleNode | undefined > {
75
- const [ url ] = await this . resolveUrl ( rawUrl , ssr )
84
+ // Quick path, if we already have a module for this rawUrl (even without extension)
85
+ rawUrl = removeImportQuery ( removeTimestampQuery ( rawUrl ) )
86
+ const mod = this . _getUnresolvedUrlToModule ( rawUrl , ssr )
87
+ if ( mod ) {
88
+ return mod
89
+ }
90
+
91
+ const [ url ] = await this . _resolveUrl ( rawUrl , ssr )
76
92
return this . urlToModuleMap . get ( url )
77
93
}
78
94
@@ -220,8 +236,15 @@ export class ModuleGraph {
220
236
ssr ?: boolean ,
221
237
setIsSelfAccepting = true ,
222
238
) : Promise < ModuleNode > {
223
- const [ url , resolvedId , meta ] = await this . resolveUrl ( rawUrl , ssr )
224
- let mod = this . idToModuleMap . get ( resolvedId )
239
+ // Quick path, if we already have a module for this rawUrl (even without extension)
240
+ rawUrl = removeImportQuery ( removeTimestampQuery ( rawUrl ) )
241
+ let mod = this . _getUnresolvedUrlToModule ( rawUrl , ssr )
242
+ if ( mod ) {
243
+ return mod
244
+ }
245
+
246
+ const [ url , resolvedId , meta ] = await this . _resolveUrl ( rawUrl , ssr )
247
+ mod = this . idToModuleMap . get ( resolvedId )
225
248
if ( ! mod ) {
226
249
mod = new ModuleNode ( url , setIsSelfAccepting )
227
250
if ( meta ) mod . meta = meta
@@ -241,6 +264,11 @@ export class ModuleGraph {
241
264
else if ( ! this . urlToModuleMap . has ( url ) ) {
242
265
this . urlToModuleMap . set ( url , mod )
243
266
}
267
+
268
+ // Also register the clean url to the module, so that we can short-circuit
269
+ // resolving the same url twice
270
+ this . _setUnresolvedUrlToModule ( rawUrl , mod , ssr )
271
+
244
272
return mod
245
273
}
246
274
@@ -275,6 +303,38 @@ export class ModuleGraph {
275
303
// the same module
276
304
async resolveUrl ( url : string , ssr ?: boolean ) : Promise < ResolvedUrl > {
277
305
url = removeImportQuery ( removeTimestampQuery ( url ) )
306
+ const mod = this . _getUnresolvedUrlToModule ( url , ssr )
307
+ if ( mod ?. id ) {
308
+ return [ mod . url , mod . id , mod . meta ]
309
+ }
310
+ return this . _resolveUrl ( url , ssr )
311
+ }
312
+
313
+ /**
314
+ * @internal
315
+ */
316
+ _getUnresolvedUrlToModule (
317
+ url : string ,
318
+ ssr ?: boolean ,
319
+ ) : ModuleNode | undefined {
320
+ return (
321
+ ssr ? this . _ssrUnresolvedUrlToModuleMap : this . _unresolvedUrlToModuleMap
322
+ ) . get ( url )
323
+ }
324
+ /**
325
+ * @internal
326
+ */
327
+ _setUnresolvedUrlToModule ( url : string , mod : ModuleNode , ssr ?: boolean ) : void {
328
+ ; ( ssr
329
+ ? this . _ssrUnresolvedUrlToModuleMap
330
+ : this . _unresolvedUrlToModuleMap
331
+ ) . set ( url , mod )
332
+ }
333
+
334
+ /**
335
+ * @internal
336
+ */
337
+ async _resolveUrl ( url : string , ssr ?: boolean ) : Promise < ResolvedUrl > {
278
338
const resolved = await this . resolveId ( url , ! ! ssr )
279
339
const resolvedId = resolved ?. id || url
280
340
if (
0 commit comments