@@ -124,7 +124,19 @@ const { defaultLoadSync, throwUnknownModuleFormat } = require('internal/modules/
124124 */
125125
126126/**
127- * @typedef {{ format: ModuleFormat, source: ModuleSource, translatorKey: string } } TranslateContext
127+ * @typedef {{format: string, url: string, isResolvedBySyncHooks: boolean} } ResolveResult
128+ */
129+
130+ /**
131+ * @typedef {{
132+ * url: string,
133+ * format: ModuleFormat,
134+ * source: ModuleSource,
135+ * responseURL?: string,
136+ * translatorKey: string,
137+ * isResolvedBySyncHooks: boolean,
138+ * isSourceLoadedSynchronously: boolean,
139+ * }} TranslateContext
128140 */
129141
130142/**
@@ -385,19 +397,25 @@ class ModuleLoader {
385397 /**
386398 * Load a module and translate it into a ModuleWrap for require(esm).
387399 * This is run synchronously, and the translator always return a ModuleWrap synchronously.
388- * @param {string } url URL of the module to be translated .
400+ * @param {ResolveResult } resolveResult Result from the resolve step .
389401 * @param {object } loadContext See {@link load}
390402 * @param {string|undefined } parentURL URL of the parent module. Undefined if it's the entry point.
391403 * @param {ModuleRequest } request Module request.
392404 * @returns {ModuleWrap }
393405 */
394- loadAndTranslateForImportInRequiredESM ( url , loadContext , parentURL , request ) {
406+ loadAndTranslateForImportInRequiredESM ( resolveResult , loadContext , parentURL , request ) {
407+ const { url } = resolveResult ;
395408 const loadResult = this . #loadSync( url , loadContext ) ;
396409 // Use the synchronous commonjs translator which can deal with cycles.
397410 const formatFromLoad = loadResult . format ;
398411 const translatorKey = ( formatFromLoad === 'commonjs' || formatFromLoad === 'commonjs-typescript' ) ?
399412 'commonjs-sync' : formatFromLoad ;
400- const translateContext = { ...loadResult , translatorKey, __proto__ : null } ;
413+ const translateContext = {
414+ ...resolveResult ,
415+ ...loadResult ,
416+ translatorKey,
417+ __proto__ : null ,
418+ } ;
401419 const wrap = this . #translate( url , translateContext , parentURL ) ;
402420 assert ( wrap instanceof ModuleWrap , `Translator used for require(${ url } ) should not be async` ) ;
403421
@@ -446,12 +464,13 @@ class ModuleLoader {
446464 /**
447465 * Load a module and translate it into a ModuleWrap for require() in imported CJS.
448466 * This is run synchronously, and the translator always return a ModuleWrap synchronously.
449- * @param {string } url URL of the module to be translated .
467+ * @param {ResolveResult } resolveResult Result from the resolve step .
450468 * @param {object } loadContext See {@link load}
451469 * @param {string|undefined } parentURL URL of the parent module. Undefined if it's the entry point.
452470 * @returns {ModuleWrap }
453471 */
454- loadAndTranslateForRequireInImportedCJS ( url , loadContext , parentURL ) {
472+ loadAndTranslateForRequireInImportedCJS ( resolveResult , loadContext , parentURL ) {
473+ const { url } = resolveResult ;
455474 const loadResult = this . #loadSync( url , loadContext ) ;
456475 const formatFromLoad = loadResult . format ;
457476
@@ -473,7 +492,12 @@ class ModuleLoader {
473492 translatorKey = 'require-commonjs-typescript' ;
474493 }
475494
476- const translateContext = { ...loadResult , translatorKey, __proto__ : null } ;
495+ const translateContext = {
496+ ...resolveResult ,
497+ ...loadResult ,
498+ translatorKey,
499+ __proto__ : null ,
500+ } ;
477501 const wrap = this . #translate( url , translateContext , parentURL ) ;
478502 assert ( wrap instanceof ModuleWrap , `Translator used for require(${ url } ) should not be async` ) ;
479503 return wrap ;
@@ -482,15 +506,21 @@ class ModuleLoader {
482506 /**
483507 * Load a module and translate it into a ModuleWrap for ordinary imported ESM.
484508 * This may be run asynchronously if there are asynchronous module loader hooks registered.
485- * @param {string } url URL of the module to be translated .
509+ * @param {ResolveResult } resolveResult Result from the resolve step .
486510 * @param {object } loadContext See {@link load}
487511 * @param {string|undefined } parentURL URL of the parent module. Undefined if it's the entry point.
488512 * @returns {Promise<ModuleWrap>|ModuleWrap }
489513 */
490- loadAndTranslate ( url , loadContext , parentURL ) {
514+ loadAndTranslate ( resolveResult , loadContext , parentURL ) {
515+ const { url } = resolveResult ;
491516 const maybePromise = this . load ( url , loadContext ) ;
492517 const afterLoad = ( loadResult ) => {
493- const translateContext = { ...loadResult , translatorKey : loadResult . format , __proto__ : null } ;
518+ const translateContext = {
519+ ...resolveResult ,
520+ ...loadResult ,
521+ translatorKey : loadResult . format ,
522+ __proto__ : null ,
523+ } ;
494524 return this . #translate( url , translateContext , parentURL ) ;
495525 } ;
496526 if ( isPromise ( maybePromise ) ) {
@@ -506,7 +536,7 @@ class ModuleLoader {
506536 * the module should be linked by the time this returns. Otherwise it may still have
507537 * pending module requests.
508538 * @param {string } parentURL See {@link getOrCreateModuleJob}
509- * @param {{format: string, url: string} } resolveResult
539+ * @param {ResolveResult } resolveResult
510540 * @param {ModuleRequest } request Module request.
511541 * @param {ModuleRequestType } requestType Type of the module request.
512542 * @returns {ModuleJobBase } The (possibly pending) module job
@@ -535,11 +565,11 @@ class ModuleLoader {
535565
536566 let moduleOrModulePromise ;
537567 if ( requestType === kRequireInImportedCJS ) {
538- moduleOrModulePromise = this . loadAndTranslateForRequireInImportedCJS ( url , context , parentURL ) ;
568+ moduleOrModulePromise = this . loadAndTranslateForRequireInImportedCJS ( resolveResult , context , parentURL ) ;
539569 } else if ( requestType === kImportInRequiredESM ) {
540- moduleOrModulePromise = this . loadAndTranslateForImportInRequiredESM ( url , context , parentURL , request ) ;
570+ moduleOrModulePromise = this . loadAndTranslateForImportInRequiredESM ( resolveResult , context , parentURL , request ) ;
541571 } else {
542- moduleOrModulePromise = this . loadAndTranslate ( url , context , parentURL ) ;
572+ moduleOrModulePromise = this . loadAndTranslate ( resolveResult , context , parentURL ) ;
543573 }
544574
545575 if ( requestType === kImportInRequiredESM || requestType === kRequireInImportedCJS ||
@@ -653,7 +683,7 @@ class ModuleLoader {
653683 * @param {string } [parentURL] The URL of the module where the module request is initiated.
654684 * It's undefined if it's from the root module.
655685 * @param {ModuleRequest } request Module request.
656- * @returns {Promise<{format: string, url: string}>|{format: string, url: string} }
686+ * @returns {Promise<ResolveResult>|ResolveResult }
657687 */
658688 #resolve( parentURL , request ) {
659689 if ( this . isForAsyncLoaderHookWorker ) {
@@ -689,15 +719,18 @@ class ModuleLoader {
689719 /**
690720 * This is the default resolve step for module.registerHooks(), which incorporates asynchronous hooks
691721 * from module.register() which are run in a blocking fashion for it to be synchronous.
722+ * @param {{isResolvedByDefaultResolve: boolean} } out Output object to track whether the default resolve was used
723+ * without polluting the user-visible resolve result.
692724 * @param {string|URL } specifier See {@link resolveSync}.
693725 * @param {{ parentURL?: string, importAttributes: ImportAttributes, conditions?: string[]} } context
694726 * See {@link resolveSync}.
695727 * @returns {{ format: string, url: string } }
696728 */
697- #resolveAndMaybeBlockOnLoaderThread( specifier , context ) {
729+ #resolveAndMaybeBlockOnLoaderThread( out , specifier , context ) {
698730 if ( this . #asyncLoaderHooks?. resolveSync ) {
699731 return this . #asyncLoaderHooks. resolveSync ( specifier , context . parentURL , context . importAttributes ) ;
700732 }
733+ out . isResolvedByDefaultResolve = true ;
701734 return this . #cachedDefaultResolve( specifier , context ) ;
702735 }
703736
@@ -712,31 +745,45 @@ class ModuleLoader {
712745 * @param {boolean } [shouldSkipSyncHooks] Whether to skip the synchronous hooks registered by module.registerHooks().
713746 * This is used to maintain compatibility for the re-invented require.resolve (in imported CJS customized
714747 * by module.register()`) which invokes the CJS resolution separately from the hook chain.
715- * @returns {{ format: string, url: string } }
748+ * @returns {ResolveResult }
716749 */
717750 resolveSync ( parentURL , request , shouldSkipSyncHooks = false ) {
718751 const specifier = `${ request . specifier } ` ;
719752 const importAttributes = request . attributes ?? kEmptyObject ;
753+ // Use an output parameter to track the state and avoid polluting the user-visible resolve results.
754+ const out = { isResolvedByDefaultResolve : false , __proto__ : null } ;
720755
756+ let result ;
757+ let isResolvedBySyncHooks = false ;
721758 if ( ! shouldSkipSyncHooks && syncResolveHooks . length ) {
722759 // Has module.registerHooks() hooks, chain the asynchronous hooks in the default step.
723- return resolveWithSyncHooks ( specifier , parentURL , importAttributes , this . #defaultConditions,
724- this . #resolveAndMaybeBlockOnLoaderThread. bind ( this ) ) ;
760+ result = resolveWithSyncHooks ( specifier , parentURL , importAttributes , this . #defaultConditions,
761+ this . #resolveAndMaybeBlockOnLoaderThread. bind ( this , out ) ) ;
762+ // If the default step ran, sync hooks did not short-circuit the resolution.
763+ isResolvedBySyncHooks = ! out . isResolvedByDefaultResolve ;
764+ } else {
765+ const context = {
766+ ...request ,
767+ conditions : this . #defaultConditions,
768+ parentURL,
769+ importAttributes,
770+ __proto__ : null ,
771+ } ;
772+ result = this . #resolveAndMaybeBlockOnLoaderThread( out , specifier , context ) ;
725773 }
726- const context = {
727- ...request ,
728- conditions : this . #defaultConditions,
729- parentURL,
730- importAttributes,
731- __proto__ : null ,
732- } ;
733- return this . #resolveAndMaybeBlockOnLoaderThread( specifier , context ) ;
774+ result . isResolvedBySyncHooks = isResolvedBySyncHooks ;
775+ return result ;
734776 }
735777
736778 /**
737779 * Provide source that is understood by one of Node's translators. Handles customization hooks,
738780 * if any.
739- * @typedef { {format: ModuleFormat, source: ModuleSource } } LoadResult
781+ * @typedef {{
782+ * format: ModuleFormat,
783+ * source: ModuleSource,
784+ * responseURL?: string,
785+ * isSourceLoadedSynchronously: boolean,
786+ * }} LoadResult
740787 * @param {string } url The URL of the module to be loaded.
741788 * @param {object } context Metadata about the module
742789 * @returns {Promise<LoadResult> | LoadResult }}
@@ -752,14 +799,19 @@ class ModuleLoader {
752799 /**
753800 * This is the default load step for module.registerHooks(), which incorporates asynchronous hooks
754801 * from module.register() which are run in a blocking fashion for it to be synchronous.
802+ * @param {{isSourceLoadedSynchronously: boolean} } out
803+ * Output object to track whether the source was loaded synchronously without polluting
804+ * the user-visible load result.
755805 * @param {string } url See {@link load}
756806 * @param {object } context See {@link load}
757807 * @returns {{ format: ModuleFormat, source: ModuleSource } }
758808 */
759- #loadAndMaybeBlockOnLoaderThread( url , context ) {
809+ #loadAndMaybeBlockOnLoaderThread( out , url , context ) {
760810 if ( this . #asyncLoaderHooks?. loadSync ) {
811+ out . isSourceLoadedSynchronously = false ;
761812 return this . #asyncLoaderHooks. loadSync ( url , context ) ;
762813 }
814+ out . isSourceLoadedSynchronously = true ;
763815 return defaultLoadSync ( url , context ) ;
764816 }
765817
@@ -770,17 +822,32 @@ class ModuleLoader {
770822 * This is here to support `require()` in imported CJS and `module.registerHooks()` hooks.
771823 * @param {string } url See {@link load}
772824 * @param {object } [context] See {@link load}
773- * @returns {{ format: ModuleFormat, source: ModuleSource } }
825+ * @returns {LoadResult }
774826 */
775827 #loadSync( url , context ) {
828+ // Use an output parameter to track the state and avoid polluting the user-visible resolve results.
829+ const out = {
830+ isSourceLoadedSynchronously : true ,
831+ __proto__ : null ,
832+ } ;
833+ let result ;
776834 if ( syncLoadHooks . length ) {
777835 // Has module.registerHooks() hooks, chain the asynchronous hooks in the default step.
778836 // TODO(joyeecheung): construct the ModuleLoadContext in the loaders directly instead
779837 // of converting them from plain objects in the hooks.
780- return loadWithSyncHooks ( url , context . format , context . importAttributes , this . #defaultConditions,
781- this . #loadAndMaybeBlockOnLoaderThread. bind ( this ) , validateLoadSloppy ) ;
838+ result = loadWithSyncHooks (
839+ url ,
840+ context . format ,
841+ context . importAttributes ,
842+ this . #defaultConditions,
843+ this . #loadAndMaybeBlockOnLoaderThread. bind ( this , out ) ,
844+ validateLoadSloppy ,
845+ ) ;
846+ } else {
847+ result = this . #loadAndMaybeBlockOnLoaderThread( out , url , context ) ;
782848 }
783- return this . #loadAndMaybeBlockOnLoaderThread( url , context ) ;
849+ result . isSourceLoadedSynchronously = out . isSourceLoadedSynchronously ;
850+ return result ;
784851 }
785852
786853 validateLoadResult ( url , format ) {
0 commit comments