From 7376edca6da3d236d58971d4162d88975fcc65df Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 8 Sep 2021 10:36:52 -0700 Subject: [PATCH] module: deprecate trailing slash pattern mappings PR-URL: https://github.com/nodejs/node/pull/40039 Reviewed-By: Antoine du Hamel Reviewed-By: Geoffrey Booth --- doc/api/deprecations.md | 14 +++++++++++++ doc/api/esm.md | 2 ++ lib/internal/modules/esm/resolve.js | 20 +++++++++++++++++++ .../test-esm-exports-deprecations.mjs | 4 ++++ test/es-module/test-esm-exports.mjs | 6 ++++++ .../es-module/test-esm-local-deprecations.mjs | 7 +++++++ .../es-modules/pattern-trailing-slash.mjs | 1 + .../node_modules/pkgexports/package.json | 3 ++- .../trailing-pattern-slash/index.js | 1 + 9 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/es-modules/pattern-trailing-slash.mjs create mode 100644 test/fixtures/node_modules/pkgexports/trailing-pattern-slash/index.js diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index 1e9f6a91516215..2bb534e2eb658d 100644 --- a/doc/api/deprecations.md +++ b/doc/api/deprecations.md @@ -2812,6 +2812,20 @@ Type: Documentation-only (supports [`--pending-deprecation`][]) The `'hash'` and `'mgf1Hash'` options are replaced with `'hashAlgorithm'` and `'mgf1HashAlgorithm'`. +### DEP0155: Trailing slashes in pattern specifier resolutions + + +Type: Documentation-only (supports [`--pending-deprecation`][]) + +The remapping of specifiers ending in `"/"` like `import 'pkg/x/'` is deprecated +for package `"exports"` and `"imports"` pattern resolutions. + [Legacy URL API]: url.md#url_legacy_url_api [NIST SP 800-38D]: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf [RFC 6066]: https://tools.ietf.org/html/rfc6066#section-3 diff --git a/doc/api/esm.md b/doc/api/esm.md index eb9811ada8b1a5..54892bf85ca7d0 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -1190,6 +1190,8 @@ _isImports_, _conditions_) > _expansionKey_ up to but excluding the first _"*"_ character. > 1. If _patternBase_ is not **null** and _matchKey_ starts with but is not > equal to _patternBase_, then +> 1. If _matchKey_ ends with _"/"_, throw an _Invalid Module Specifier_ +> error. > 1. Let _patternTrailer_ be the substring of _expansionKey_ from the > index after the first _"*"_ character. > 1. If _patternTrailer_ has zero length, or if _matchKey_ ends with diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js index c0f33f38e6810f..a8ce281af6eb96 100644 --- a/lib/internal/modules/esm/resolve.js +++ b/lib/internal/modules/esm/resolve.js @@ -40,6 +40,7 @@ const { sep, relative, resolve } = require('path'); const preserveSymlinks = getOptionValue('--preserve-symlinks'); const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main'); const typeFlag = getOptionValue('--input-type'); +const pendingDeprecation = getOptionValue('--pending-deprecation'); const { URL, pathToFileURL, fileURLToPath } = require('internal/url'); const { ERR_INPUT_TYPE_NOT_ALLOWED, @@ -106,6 +107,22 @@ function emitFolderMapDeprecation(match, pjsonUrl, isExports, base) { ); } +function emitTrailingSlashPatternDeprecation(match, pjsonUrl, isExports, base) { + if (!pendingDeprecation) return; + const pjsonPath = fileURLToPath(pjsonUrl); + if (emittedPackageWarnings.has(pjsonPath + '|' + match)) + return; + emittedPackageWarnings.add(pjsonPath + '|' + match); + process.emitWarning( + `Use of deprecated trailing slash pattern mapping "${match}" in the ${ + isExports ? '"exports"' : '"imports"'} field module resolution of the ` + + `package at ${pjsonPath}${base ? ` imported from ${fileURLToPath(base)}` : + ''}. Mapping specifiers ending in "/" is no longer supported.`, + 'DeprecationWarning', + 'DEP0155' + ); +} + /** * @param {URL} url * @param {URL} packageJSONUrl @@ -639,6 +656,9 @@ function packageExportsResolve( if (patternIndex !== -1 && StringPrototypeStartsWith(packageSubpath, StringPrototypeSlice(key, 0, patternIndex))) { + if (StringPrototypeEndsWith(packageSubpath, '/')) + emitTrailingSlashPatternDeprecation(packageSubpath, packageJSONUrl, + true, base); const patternTrailer = StringPrototypeSlice(key, patternIndex + 1); if (packageSubpath.length >= key.length && StringPrototypeEndsWith(packageSubpath, patternTrailer) && diff --git a/test/es-module/test-esm-exports-deprecations.mjs b/test/es-module/test-esm-exports-deprecations.mjs index 2dd2756e2ee844..8c7a07b0204b9a 100644 --- a/test/es-module/test-esm-exports-deprecations.mjs +++ b/test/es-module/test-esm-exports-deprecations.mjs @@ -1,3 +1,4 @@ +// Flags: --pending-deprecation import { mustCall } from '../common/index.mjs'; import assert from 'assert'; @@ -5,7 +6,10 @@ let curWarning = 0; const expectedWarnings = [ '"./sub/"', '"./fallbackdir/"', + '"./trailing-pattern-slash/"', '"./subpath/"', + '"./subpath/dir1/"', + '"./subpath/dir2/"', 'no_exports', 'default_index', ]; diff --git a/test/es-module/test-esm-exports.mjs b/test/es-module/test-esm-exports.mjs index 0bf361d4863fff..11323351b6512f 100644 --- a/test/es-module/test-esm-exports.mjs +++ b/test/es-module/test-esm-exports.mjs @@ -41,13 +41,19 @@ import fromInside from '../fixtures/node_modules/pkgexports/lib/hole.js'; ['pkgexports/dir2/dir2/trailer', { default: 'index' }], ['pkgexports/a/dir1/dir1', { default: 'main' }], ['pkgexports/a/b/dir1/dir1', { default: 'main' }], + + // Deprecated: + ['pkgexports/trailing-pattern-slash/', + { default: 'trailing-pattern-slash' }], ]); if (isRequire) { validSpecifiers.set('pkgexports/subpath/file', { default: 'file' }); validSpecifiers.set('pkgexports/subpath/dir1', { default: 'main' }); + // Deprecated: validSpecifiers.set('pkgexports/subpath/dir1/', { default: 'main' }); validSpecifiers.set('pkgexports/subpath/dir2', { default: 'index' }); + // Deprecated: validSpecifiers.set('pkgexports/subpath/dir2/', { default: 'index' }); } else { // No exports or main field diff --git a/test/es-module/test-esm-local-deprecations.mjs b/test/es-module/test-esm-local-deprecations.mjs index a1945f66f3422f..8d946b6650ed3b 100644 --- a/test/es-module/test-esm-local-deprecations.mjs +++ b/test/es-module/test-esm-local-deprecations.mjs @@ -1,3 +1,5 @@ +// Flags: --pending-deprecation + import '../common/index.mjs'; import assert from 'assert'; import fixtures from '../common/fixtures.js'; @@ -9,10 +11,14 @@ const selfDeprecatedFolders = const deprecatedFoldersIgnore = fixtures.path('/es-modules/deprecated-folders-ignore/main.js'); +const deprecatedTrailingSlashPattern = + fixtures.path('/es-modules/pattern-trailing-slash.mjs'); + const expectedWarnings = [ '"./" in the "exports" field', '"#self/" in the "imports" field', '"./folder/" in the "exports" field', + '"./trailing-pattern-slash/" in the "exports" field', ]; process.addListener('warning', (warning) => { @@ -28,5 +34,6 @@ process.on('exit', () => { (async () => { await import(pathToFileURL(selfDeprecatedFolders)); await import(pathToFileURL(deprecatedFoldersIgnore)); + await import(pathToFileURL(deprecatedTrailingSlashPattern)); })() .catch((err) => console.error(err)); diff --git a/test/fixtures/es-modules/pattern-trailing-slash.mjs b/test/fixtures/es-modules/pattern-trailing-slash.mjs new file mode 100644 index 00000000000000..e289305ee026b9 --- /dev/null +++ b/test/fixtures/es-modules/pattern-trailing-slash.mjs @@ -0,0 +1 @@ +import 'pkgexports/trailing-pattern-slash/'; diff --git a/test/fixtures/node_modules/pkgexports/package.json b/test/fixtures/node_modules/pkgexports/package.json index 7f8f994ac398bd..fe46111f793314 100644 --- a/test/fixtures/node_modules/pkgexports/package.json +++ b/test/fixtures/node_modules/pkgexports/package.json @@ -61,6 +61,7 @@ "./subpath/": "./subpath/", "./subpath/sub-*": "./subpath/dir1/*.js", "./subpath/sub-*.js": "./subpath/dir1/*.js", - "./features/*": "./subpath/*/*.js" + "./features/*": "./subpath/*/*.js", + "./trailing-pattern-slash*": "./trailing-pattern-slash*index.js" } } diff --git a/test/fixtures/node_modules/pkgexports/trailing-pattern-slash/index.js b/test/fixtures/node_modules/pkgexports/trailing-pattern-slash/index.js new file mode 100644 index 00000000000000..613bddbb6a44dd --- /dev/null +++ b/test/fixtures/node_modules/pkgexports/trailing-pattern-slash/index.js @@ -0,0 +1 @@ +module.exports = 'trailing-pattern-slash';