diff --git a/doc/api/esm.md b/doc/api/esm.md index 64e9c68289d01d..e019b3458c1759 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -1102,7 +1102,7 @@ Note: This function is directly invoked by the CommonJS resolution algorithm. Note: This function is directly invoked by the CommonJS resolution algorithm. > 1. Assert: _specifier_ begins with _"#"_. -> 2. If _specifier_ is exactly equal to _"#"_ or starts with _"#/"_, then +> 2. If _specifier_ is exactly equal to _"#"_, then > 1. Throw an _Invalid Module Specifier_ error. > 3. Let _packageURL_ be the result of **LOOKUP\_PACKAGE\_SCOPE**(_parentURL_). > 4. If _packageURL_ is not **null**, then diff --git a/doc/api/packages.md b/doc/api/packages.md index a980d8d7aa3e59..6110d25be530a7 100644 --- a/doc/api/packages.md +++ b/doc/api/packages.md @@ -527,6 +527,10 @@ can be written: added: - v14.6.0 - v12.19.0 +changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/60864 + description: Allow subpath imports that start with `#/`. --> In addition to the [`"exports"`][] field, there is a package `"imports"` field diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js index c27ee4c6612c6a..a321744ab8263c 100644 --- a/lib/internal/modules/esm/resolve.js +++ b/lib/internal/modules/esm/resolve.js @@ -692,8 +692,7 @@ function patternKeyCompare(a, b) { * @returns {URL} The resolved import URL. */ function packageImportsResolve(name, base, conditions) { - if (name === '#' || StringPrototypeStartsWith(name, '#/') || - StringPrototypeEndsWith(name, '/')) { + if (name === '#' || StringPrototypeEndsWith(name, '/')) { const reason = 'is not a valid internal imports specifier name'; throw new ERR_INVALID_MODULE_SPECIFIER(name, reason, fileURLToPath(base)); } diff --git a/test/es-module/test-esm-imports.mjs b/test/es-module/test-esm-imports.mjs index 7d2f7a63773514..b9982e2d0a585b 100644 --- a/test/es-module/test-esm-imports.mjs +++ b/test/es-module/test-esm-imports.mjs @@ -15,6 +15,10 @@ const { requireImport, importImport } = importer; const internalImports = new Map([ // Base case ['#test', maybeWrapped({ default: 'test' })], + // Root wildcard import + ['#/foo', maybeWrapped({ default: 'foo' })], + // Explicit #/ mapping + ['#/initialslash', maybeWrapped({ default: 'test' })], // import / require conditions ['#branch', maybeWrapped({ default: isRequire ? 'requirebranch' : 'importbranch' })], // Subpath imports @@ -64,8 +68,6 @@ const { requireImport, importImport } = importer; ['#external/subpath/x%5Cy', 'must not include encoded "/" or "\\"'], // Target must have a name ['#', '#'], - // Initial slash target must have a leading name - ['#/initialslash', '#/initialslash'], // Percent-encoded target paths ['#encodedslash', 'must not include encoded "/" or "\\"'], ['#encodedbackslash', 'must not include encoded "/" or "\\"'], diff --git a/test/fixtures/es-modules/pkgimports/package.json b/test/fixtures/es-modules/pkgimports/package.json index dbbbcd1ab01ea1..82e57b11ce45cd 100644 --- a/test/fixtures/es-modules/pkgimports/package.json +++ b/test/fixtures/es-modules/pkgimports/package.json @@ -1,5 +1,6 @@ { "imports": { + "#/*": "./src/*.js", "#branch": { "import": "./importbranch.js", "require": "./requirebranch.js" diff --git a/test/fixtures/es-modules/pkgimports/src/foo.js b/test/fixtures/es-modules/pkgimports/src/foo.js new file mode 100644 index 00000000000000..2651774ae60543 --- /dev/null +++ b/test/fixtures/es-modules/pkgimports/src/foo.js @@ -0,0 +1 @@ +module.exports = 'foo';