@@ -46,7 +46,7 @@ const {
4646 getSourceMapsSupport,
4747} = require ( 'internal/source_map/source_map_cache' ) ;
4848const assert = require ( 'internal/assert' ) ;
49- const resolvedPromise = PromiseResolve ( ) ;
49+ let resolvedPromise ;
5050const {
5151 setHasStartedUserESMExecution,
5252 urlToFilename,
@@ -374,7 +374,7 @@ class ModuleJob extends ModuleJobBase {
374374 for ( const dependencyJob of jobsInGraph ) {
375375 // Calling `this.module.instantiate()` instantiates not only the
376376 // ModuleWrap in this module, but all modules in the graph.
377- dependencyJob . instantiated = resolvedPromise ;
377+ dependencyJob . instantiated = resolvedPromise ??= PromiseResolve ( ) ;
378378 }
379379 }
380380
@@ -445,12 +445,14 @@ class ModuleJob extends ModuleJobBase {
445445
446446/**
447447 * This is a fully synchronous job and does not spawn additional threads in any way.
448- * All the steps are ensured to be synchronous and it throws on instantiating
449- * an asynchronous graph. It also disallows CJS <-> ESM cycles.
448+ * Loading and linking are always synchronous. Evaluation via runSync() throws on an
449+ * asynchronous graph; evaluation via run() falls back to async for top-level await.
450+ * It also disallows CJS <-> ESM cycles.
450451 *
451- * This is used for ES modules loaded via require(esm). Modules loaded by require() in
452- * imported CJS are handled by ModuleJob with the isForRequireInImportedCJS set to true instead.
453- * The two currently have different caching behaviors.
452+ * Used for all ES module imports on the main thread, regardless of how the import was
453+ * triggered (entry point, import(), require(esm), --import, etc.).
454+ * Modules loaded by require() in imported CJS are handled by ModuleJob with the
455+ * isForRequireInImportedCJS set to true instead. The two currently have different caching behaviors.
454456 * TODO(joyeecheung): consolidate this with the isForRequireInImportedCJS variant of ModuleJob.
455457 */
456458class ModuleJobSync extends ModuleJobBase {
@@ -491,32 +493,49 @@ class ModuleJobSync extends ModuleJobBase {
491493 return PromiseResolve ( this . module ) ;
492494 }
493495
494- async run ( ) {
496+ async run ( isEntryPoint = false ) {
495497 assert ( this . phase === kEvaluationPhase ) ;
496- // This path is hit by a require'd module that is imported again.
497498 const status = this . module . getStatus ( ) ;
498499 debug ( 'ModuleJobSync.run()' , status , this . module ) ;
499500 // If the module was previously required and errored, reject from import() again.
500501 if ( status === kErrored ) {
501502 throw this . module . getError ( ) ;
502- } else if ( status > kInstantiated ) {
503+ }
504+ if ( status > kInstantiated ) {
505+ // Already evaluated (e.g. previously require()'d and now import()'d again).
503506 if ( this . evaluationPromise ) {
504507 await this . evaluationPromise ;
505508 }
506509 return { __proto__ : null , module : this . module } ;
507- } else if ( status === kInstantiated ) {
508- // The evaluation may have been canceled because instantiate() detected TLA first.
509- // But when it is imported again, it's fine to re-evaluate it asynchronously.
510+ }
511+ if ( status < kInstantiated ) {
512+ // Fresh module: instantiate it now (links were already resolved synchronously in constructor)
513+ this . module . instantiate ( ) ;
514+ }
515+ // `status === kInstantiated`: either just instantiated above, or previously instantiated
516+ // but evaluation was deferred (e.g. TLA detected by a prior `runSync()` call)
517+ if ( isEntryPoint ) {
518+ globalThis [ entry_point_module_private_symbol ] = this . module ;
519+ }
520+ setHasStartedUserESMExecution ( ) ;
521+ if ( this . module . hasAsyncGraph ) {
522+ // Has top-level `await`: fall back to async evaluation
510523 const timeout = - 1 ;
511524 const breakOnSigint = false ;
512525 this . evaluationPromise = this . module . evaluate ( timeout , breakOnSigint ) ;
513526 await this . evaluationPromise ;
514527 this . evaluationPromise = undefined ;
515528 return { __proto__ : null , module : this . module } ;
516529 }
517-
518- assert . fail ( 'Unexpected status of a module that is imported again after being required. ' +
519- `Status = ${ status } ` ) ;
530+ // No top-level `await`: evaluate synchronously
531+ const filename = urlToFilename ( this . url ) ;
532+ try {
533+ this . module . evaluateSync ( filename , undefined ) ;
534+ } catch ( evaluateError ) {
535+ explainCommonJSGlobalLikeNotDefinedError ( evaluateError , this . module . url , this . module . hasTopLevelAwait ) ;
536+ throw evaluateError ;
537+ }
538+ return { __proto__ : null , module : this . module } ;
520539 }
521540
522541 runSync ( parent ) {
0 commit comments