diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index 0b98fb9d4f0568..c24f1de99321b3 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -879,7 +879,7 @@ function getExportsForCircularRequire(module) { // 2. If the module is native: call // `NativeModule.prototype.compileForPublicLoader()` and return the exports. // 3. Otherwise, create a new module for the file and save it to the cache. -// Then have it load the file contents before returning its exports +// Then have it load the file contents before returning its exports // object. Module._load = function(request, parent, isMain) { let relResolveCacheIdentifier; diff --git a/lib/internal/modules/esm/default_resolve.js b/lib/internal/modules/esm/default_resolve.js index 169c6f35694f24..f0491ca149e5c8 100644 --- a/lib/internal/modules/esm/default_resolve.js +++ b/lib/internal/modules/esm/default_resolve.js @@ -9,6 +9,10 @@ const { NativeModule } = require('internal/bootstrap/loaders'); const { extname } = require('path'); const { realpathSync } = require('fs'); const { getOptionValue } = require('internal/options'); +const { + Module: CJSModule +} = require('internal/modules/cjs/loader'); + const preserveSymlinks = getOptionValue('--preserve-symlinks'); const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main'); @@ -93,7 +97,32 @@ function resolve(specifier, parentURL) { throw new ERR_INPUT_TYPE_NOT_ALLOWED(); } - let url = moduleWrapResolve(specifier, parentURL); + let url; + try { + url = moduleWrapResolve(specifier, parentURL); + } catch (error) { + try { + // By default ES Modules does not support extensionless files. + // The idea is make suggestions when file ext is not provided + // Example: import pkg from './meow' + // Ref: https://github.com/nodejs/node/issues/30603 + + // Create a fake CJS Module using parentURL. + const currentModule = new CJSModule(parentURL); + // Resolve our especifier e.g: 'file' + // using as module parent our current module. + // So with our fake parent, paths to search in CJS will be provided. + const cjsResolved = CJSModule._resolveFilename(specifier, + currentModule, + false); + if (cjsResolved) { + error.message += '\nCJS would resolved: ' + cjsResolved; + } + } catch (cjsError) { // eslint-disable-line no-unused-vars + // ignore + } + throw error; + } if (isMain ? !preserveSymlinksMain : !preserveSymlinks) { const real = realpathSync(fileURLToPath(url), { diff --git a/test/es-module/test-esm-cjs-loader-suggestions.mjs b/test/es-module/test-esm-cjs-loader-suggestions.mjs new file mode 100644 index 00000000000000..faf28d435e7499 --- /dev/null +++ b/test/es-module/test-esm-cjs-loader-suggestions.mjs @@ -0,0 +1,4 @@ +import '../common/index.mjs'; + +import('../fixtures/es-modules/cjs') + .catch(err => console.error(err)) diff --git a/test/message/esm_loader_not_found.out b/test/message/esm_loader_not_found.out index b03b7641af072b..d090c223fdd9e6 100644 --- a/test/message/esm_loader_not_found.out +++ b/test/message/esm_loader_not_found.out @@ -1,8 +1,8 @@ (node:*) ExperimentalWarning: The ESM module loader is experimental. (node:*) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time internal/modules/esm/default_resolve.js:* - let url = moduleWrapResolve(specifier, parentURL); - ^ + url = moduleWrapResolve(specifier, parentURL); + ^ Error: Cannot find package 'i-dont-exist' imported from * at Loader.resolve [as _resolve] (internal/modules/esm/default_resolve.js:*:*)