@@ -18,7 +18,6 @@ const {
18
18
kIsExecuting,
19
19
kRequiredModuleSymbol,
20
20
} = require ( 'internal/modules/cjs/loader' ) ;
21
-
22
21
const { imported_cjs_symbol } = internalBinding ( 'symbols' ) ;
23
22
24
23
const assert = require ( 'internal/assert' ) ;
@@ -38,7 +37,13 @@ const {
38
37
forceDefaultLoader,
39
38
} = require ( 'internal/modules/esm/utils' ) ;
40
39
const { kImplicitTypeAttribute } = require ( 'internal/modules/esm/assert' ) ;
41
- const { ModuleWrap, kEvaluating, kEvaluated } = internalBinding ( 'module_wrap' ) ;
40
+ const {
41
+ ModuleWrap,
42
+ kEvaluating,
43
+ kEvaluated,
44
+ kInstantiated,
45
+ throwIfPromiseRejected,
46
+ } = internalBinding ( 'module_wrap' ) ;
42
47
const {
43
48
urlToFilename,
44
49
} = require ( 'internal/modules/helpers' ) ;
@@ -53,6 +58,10 @@ let defaultResolve, defaultLoad, defaultLoadSync, importMetaInitializer;
53
58
const { tracingChannel } = require ( 'diagnostics_channel' ) ;
54
59
const onImport = tracingChannel ( 'module.import' ) ;
55
60
61
+ let debug = require ( 'internal/util/debuglog' ) . debuglog ( 'esm' , ( fn ) => {
62
+ debug = fn ;
63
+ } ) ;
64
+
56
65
/**
57
66
* @typedef {import('./hooks.js').HooksProxy } HooksProxy
58
67
* @typedef {import('./module_job.js').ModuleJobBase } ModuleJobBase
@@ -340,35 +349,58 @@ class ModuleLoader {
340
349
// evaluated at this point.
341
350
// TODO(joyeecheung): add something similar to CJS loader's requireStack to help
342
351
// debugging the the problematic links in the graph for import.
352
+ debug ( 'importSyncForRequire' , parent ?. filename , '->' , filename , job ) ;
343
353
if ( job !== undefined ) {
344
354
mod [ kRequiredModuleSymbol ] = job . module ;
345
355
const parentFilename = urlToFilename ( parent ?. filename ) ;
346
356
// TODO(node:55782): this race may stop to happen when the ESM resolution and loading become synchronous.
357
+ let raceMessage = `Cannot require() ES Module ${ filename } because it is not yet fully loaded. ` ;
358
+ raceMessage += 'This may be caused by a race condition if the module is simultaneously dynamically ' ;
359
+ raceMessage += 'import()-ed via Promise.all(). Try await-ing the import() sequentially in a loop instead.' ;
360
+ if ( parentFilename ) {
361
+ raceMessage += ` (from ${ parentFilename } )` ;
362
+ }
347
363
if ( ! job . module ) {
348
- let message = `Cannot require() ES Module ${ filename } because it is not yet fully loaded. ` ;
349
- message += 'This may be caused by a race condition if the module is simultaneously dynamically ' ;
350
- message += 'import()-ed via Promise.all(). Try await-ing the import() sequentially in a loop instead.' ;
351
- if ( parentFilename ) {
352
- message += ` (from ${ parentFilename } )` ;
353
- }
354
- assert ( job . module , message ) ;
364
+ assert ( job . module , raceMessage ) ;
355
365
}
356
366
if ( job . module . async ) {
357
367
throw new ERR_REQUIRE_ASYNC_MODULE ( filename , parentFilename ) ;
358
368
}
359
- // job.module may be undefined if it's asynchronously loaded. Which means
360
- // there is likely a cycle.
361
- if ( job . module . getStatus ( ) !== kEvaluated ) {
362
- let message = `Cannot require() ES Module ${ filename } in a cycle.` ;
363
- if ( parentFilename ) {
364
- message += ` (from ${ parentFilename } )` ;
365
- }
366
- message += 'A cycle involving require(esm) is disallowed to maintain ' ;
367
- message += 'invariants madated by the ECMAScript specification' ;
368
- message += 'Try making at least part of the dependency in the graph lazily loaded.' ;
369
- throw new ERR_REQUIRE_CYCLE_MODULE ( message ) ;
369
+ const status = job . module . getStatus ( ) ;
370
+ debug ( 'Module status' , filename , status ) ;
371
+ if ( status === kEvaluated ) {
372
+ return { wrap : job . module , namespace : job . module . getNamespaceSync ( filename , parentFilename ) } ;
373
+ } else if ( status === kInstantiated ) {
374
+ // When it's an async job cached by another import request,
375
+ // which has finished linking but has not started its
376
+ // evaluation because the async run() task would be later
377
+ // in line. Then start the evaluation now with runSync(), which
378
+ // is guaranteed to finish by the time the other run() get to it,
379
+ // and the other task would just get the cached evaluation results,
380
+ // similar to what would happen when both are async.
381
+ mod [ kRequiredModuleSymbol ] = job . module ;
382
+ const { namespace } = job . runSync ( parent ) ;
383
+ return { wrap : job . module , namespace : namespace || job . module . getNamespace ( ) } ;
384
+ }
385
+ // When the cached async job have already encountered a linking
386
+ // error that gets wrapped into a rejection, but is still later
387
+ // in line to throw on it, just unwrap and throw the linking error
388
+ // from require().
389
+ if ( job . instantiated ) {
390
+ throwIfPromiseRejected ( job . instantiated ) ;
370
391
}
371
- return { wrap : job . module , namespace : job . module . getNamespaceSync ( filename , parentFilename ) } ;
392
+ if ( status !== kEvaluating ) {
393
+ assert . fail ( `Unexpected module status ${ status } . ` + raceMessage ) ;
394
+ }
395
+ let message = `Cannot require() ES Module ${ filename } in a cycle.` ;
396
+ if ( parentFilename ) {
397
+ message += ` (from ${ parentFilename } )` ;
398
+ }
399
+ message += 'A cycle involving require(esm) is disallowed to maintain ' ;
400
+ message += 'invariants madated by the ECMAScript specification' ;
401
+ message += 'Try making at least part of the dependency in the graph lazily loaded.' ;
402
+ throw new ERR_REQUIRE_CYCLE_MODULE ( message ) ;
403
+
372
404
}
373
405
// TODO(joyeecheung): refactor this so that we pre-parse in C++ and hit the
374
406
// cache here, or use a carrier object to carry the compiled module script
0 commit comments