22
33const {
44 ArrayIsArray,
5+ ArrayPrototypeJoin,
6+ ArrayPrototypeShift,
57 JSONParse,
68 JSONStringify,
79 ObjectFreeze,
810 ObjectGetOwnPropertyNames,
911 ObjectPrototypeHasOwnProperty,
12+ RegExp,
1013 SafeMap,
1114 SafeSet,
1215 StringPrototypeEndsWith,
1316 StringPrototypeIncludes,
1417 StringPrototypeIndexOf,
18+ StringPrototypeReplace,
1519 StringPrototypeSlice,
20+ StringPrototypeSplit,
1621 StringPrototypeStartsWith,
1722 StringPrototypeSubstr,
1823} = primordials ;
@@ -29,8 +34,8 @@ const {
2934 Stats,
3035} = require ( 'fs' ) ;
3136const { getOptionValue } = require ( 'internal/options' ) ;
32- const { sep } = require ( 'path' ) ;
33-
37+ const { sep, relative } = require ( 'path' ) ;
38+ const { Module : CJSModule } = require ( 'internal/modules/cjs/loader' ) ;
3439const preserveSymlinks = getOptionValue ( '--preserve-symlinks' ) ;
3540const preserveSymlinksMain = getOptionValue ( '--preserve-symlinks-main' ) ;
3641const typeFlag = getOptionValue ( '--input-type' ) ;
@@ -611,9 +616,11 @@ function packageResolve(specifier, base, conditions) {
611616 throw new ERR_MODULE_NOT_FOUND ( packageName , fileURLToPath ( base ) ) ;
612617}
613618
614- function shouldBeTreatedAsRelativeOrAbsolutePath ( specifier ) {
615- if ( specifier === '' ) return false ;
616- if ( specifier [ 0 ] === '/' ) return true ;
619+ function isBareSpecifier ( specifier ) {
620+ return specifier [ 0 ] && specifier [ 0 ] !== '/' && specifier [ 0 ] !== '.' ;
621+ }
622+
623+ function isRelativeSpecifier ( specifier ) {
617624 if ( specifier [ 0 ] === '.' ) {
618625 if ( specifier . length === 1 || specifier [ 1 ] === '/' ) return true ;
619626 if ( specifier [ 1 ] === '.' ) {
@@ -623,6 +630,12 @@ function shouldBeTreatedAsRelativeOrAbsolutePath(specifier) {
623630 return false ;
624631}
625632
633+ function shouldBeTreatedAsRelativeOrAbsolutePath ( specifier ) {
634+ if ( specifier === '' ) return false ;
635+ if ( specifier [ 0 ] === '/' ) return true ;
636+ return isRelativeSpecifier ( specifier ) ;
637+ }
638+
626639/**
627640 * @param {string } specifier
628641 * @param {URL } base
@@ -645,6 +658,51 @@ function moduleResolve(specifier, base, conditions) {
645658 return finalizeResolution ( resolved , base ) ;
646659}
647660
661+ /**
662+ * Try to resolve an import as a CommonJS module
663+ * @param {string } specifier
664+ * @param {string } parentURL
665+ * @returns {boolean|string }
666+ */
667+ function resolveAsCommonJS ( specifier , parentURL ) {
668+ try {
669+ const parent = fileURLToPath ( parentURL ) ;
670+ const tmpModule = new CJSModule ( parent , null ) ;
671+ tmpModule . paths = CJSModule . _nodeModulePaths ( parent ) ;
672+
673+ let found = CJSModule . _resolveFilename ( specifier , tmpModule , false ) ;
674+
675+ // If it is a relative specifier return the relative path
676+ // to the parent
677+ if ( isRelativeSpecifier ( specifier ) ) {
678+ found = relative ( parent , found ) ;
679+ // Add '.separator if the path does not start with '..separator'
680+ // This should be a safe assumption because when loading
681+ // esm modules there should be always a file specified so
682+ // there should not be a specifier like '..' or '.'
683+ if ( ! StringPrototypeStartsWith ( found , `..${ sep } ` ) ) {
684+ found = `.${ sep } ${ found } ` ;
685+ }
686+ } else if ( isBareSpecifier ( specifier ) ) {
687+ // If it is a bare specifier return the relative path within the
688+ // module
689+ const pkg = StringPrototypeSplit ( specifier , '/' ) [ 0 ] ;
690+ const index = StringPrototypeIndexOf ( found , pkg ) ;
691+ if ( index !== - 1 ) {
692+ found = StringPrototypeSlice ( found , index ) ;
693+ }
694+ }
695+ // Normalize the path separator to give a valid suggestion
696+ // on Windows
697+ if ( process . platform === 'win32' ) {
698+ found = StringPrototypeReplace ( found , new RegExp ( `\\${ sep } ` , 'g' ) , '/' ) ;
699+ }
700+ return found ;
701+ } catch {
702+ return false ;
703+ }
704+ }
705+
648706function defaultResolve ( specifier , context = { } , defaultResolveUnused ) {
649707 let { parentURL, conditions } = context ;
650708 let parsed ;
@@ -685,7 +743,27 @@ function defaultResolve(specifier, context = {}, defaultResolveUnused) {
685743 }
686744
687745 conditions = getConditionsSet ( conditions ) ;
688- let url = moduleResolve ( specifier , parentURL , conditions ) ;
746+ let url ;
747+ try {
748+ url = moduleResolve ( specifier , parentURL , conditions ) ;
749+ } catch ( error ) {
750+ // Try to give the user a hint of what would have been the
751+ // resolved CommonJS module
752+ if ( error . code === 'ERR_MODULE_NOT_FOUND' ) {
753+ const found = resolveAsCommonJS ( specifier , parentURL ) ;
754+ if ( found ) {
755+ // Modify the stack and message string to include the hint
756+ const lines = StringPrototypeSplit ( error . stack , '\n' ) ;
757+ const hint = `Did you mean to import ${ found } ?` ;
758+ error . stack =
759+ ArrayPrototypeShift ( lines ) + '\n' +
760+ hint + '\n' +
761+ ArrayPrototypeJoin ( lines , '\n' ) ;
762+ error . message += `\n${ hint } ` ;
763+ }
764+ }
765+ throw error ;
766+ }
689767
690768 if ( isMain ? ! preserveSymlinksMain : ! preserveSymlinks ) {
691769 const urlPath = fileURLToPath ( url ) ;
0 commit comments