diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js index 730c815b8435f0..9a2e081b201065 100644 --- a/lib/internal/modules/esm/resolve.js +++ b/lib/internal/modules/esm/resolve.js @@ -587,12 +587,35 @@ function shouldBeTreatedAsRelativeOrAbsolutePath(specifier) { return false; } +function resolveRelativePackage(specifier /* string */, base /* URL */) { + const resolved = new URL(specifier, base); + const stat = tryStatSync(resolved); + if (stat.isDirectory()) { + let packageJSONPath; + if (!StringPrototypeEndsWith(resolved.pathname, '/')) { + packageJSONPath = `${resolved.pathname}/package.json`; + } else { + packageJSONPath = `${resolved.pathname}package.json`; + } + const packageJSONUrl = new URL(packageJSONPath, base); + packageJSONPath = fileURLToPath(packageJSONUrl); + const packageConfig = getPackageConfig(packageJSONPath, base); + return packageMainResolve(packageJSONUrl, packageConfig, base); + } + return resolved; +} + function moduleResolve(specifier /* string */, base /* URL */) { /* -> URL */ // Order swapped from spec for minor perf gain. // Ok since relative URLs cannot parse as URLs. let resolved; if (shouldBeTreatedAsRelativeOrAbsolutePath(specifier)) { - resolved = new URL(specifier, base); + if (getOptionValue('--experimental-specifier-resolution') === 'node') { + // experimental-specifier-resolution also allows importing from directory + resolved = resolveRelativePackage(specifier, base); + } else { + resolved = new URL(specifier, base); + } } else { try { resolved = new URL(specifier); diff --git a/test/es-module/test-esm-specifiers.mjs b/test/es-module/test-esm-specifiers.mjs index 5e436f21b74cc6..da3b3a2432488c 100644 --- a/test/es-module/test-esm-specifiers.mjs +++ b/test/es-module/test-esm-specifiers.mjs @@ -9,12 +9,18 @@ import { fileURLToPath } from 'url'; import commonjs from '../fixtures/es-module-specifiers/package-type-commonjs'; // esm index.js import module from '../fixtures/es-module-specifiers/package-type-module'; +// esm main.js +import pkg_main from '../fixtures/es-module-specifiers/package-with-main'; +// esm exports_main.js +import pkg_export from '../fixtures/es-module-specifiers/package-with-exports'; // Notice the trailing slash import success, { explicit, implicit, implicitModule, getImplicitCommonjs } from '../fixtures/es-module-specifiers/'; assert.strictEqual(commonjs, 'commonjs'); assert.strictEqual(module, 'module'); +assert.strictEqual(pkg_main, 'package-with-main'); +assert.strictEqual(pkg_export, 'package-with-exports'); assert.strictEqual(success, 'success'); assert.strictEqual(explicit, 'esm'); assert.strictEqual(implicit, 'cjs'); diff --git a/test/fixtures/es-module-specifiers/package-with-exports/exports_main.js b/test/fixtures/es-module-specifiers/package-with-exports/exports_main.js new file mode 100644 index 00000000000000..196c5b4c7f1ef9 --- /dev/null +++ b/test/fixtures/es-module-specifiers/package-with-exports/exports_main.js @@ -0,0 +1,2 @@ +const identifier = 'package-with-exports'; +module.exports = identifier; diff --git a/test/fixtures/es-module-specifiers/package-with-exports/package.json b/test/fixtures/es-module-specifiers/package-with-exports/package.json new file mode 100644 index 00000000000000..41b46dbac95c48 --- /dev/null +++ b/test/fixtures/es-module-specifiers/package-with-exports/package.json @@ -0,0 +1,6 @@ +{ + "main": "main.js", + "exports": { + ".": "./exports_main.js" + } +} diff --git a/test/fixtures/es-module-specifiers/package-with-main/main.js b/test/fixtures/es-module-specifiers/package-with-main/main.js new file mode 100644 index 00000000000000..c335f303944136 --- /dev/null +++ b/test/fixtures/es-module-specifiers/package-with-main/main.js @@ -0,0 +1,2 @@ +const identifier = 'package-with-main'; +module.exports = identifier; diff --git a/test/fixtures/es-module-specifiers/package-with-main/package.json b/test/fixtures/es-module-specifiers/package-with-main/package.json new file mode 100644 index 00000000000000..c13b8cf6acfd33 --- /dev/null +++ b/test/fixtures/es-module-specifiers/package-with-main/package.json @@ -0,0 +1,3 @@ +{ + "main": "main.js" +}