diff --git a/doc/api/esm.md b/doc/api/esm.md index c4bed1c690df2d..40b753c4d5a27b 100644 --- a/doc/api/esm.md +++ b/doc/api/esm.md @@ -1456,6 +1456,10 @@ _internal_, _conditions_) > Stability: 1 - Experimental +> **Note: Do not rely on this flag. We plan to remove it once the +> [Loaders API][] has advanced to the point that equivalent functionality can +> be achieved via custom loaders.** + The current specifier resolution does not support all default behavior of the CommonJS loader. One of the behavior differences is automatic resolution of file extensions and the ability to import directories that have an index @@ -1488,6 +1492,7 @@ success! [Import Assertions]: #import-assertions [Import Assertions proposal]: https://github.com/tc39/proposal-import-assertions [JSON modules]: #json-modules +[Loaders API]: #loaders [Node.js Module Resolution Algorithm]: #resolver-algorithm-specification [Terminology]: #terminology [URL]: https://url.spec.whatwg.org/ diff --git a/lib/internal/modules/esm/formats.js b/lib/internal/modules/esm/formats.js index 8fbe0f38446862..a00f6640fa4287 100644 --- a/lib/internal/modules/esm/formats.js +++ b/lib/internal/modules/esm/formats.js @@ -43,17 +43,7 @@ function mimeToFormat(mime) { return null; } -let experimentalSpecifierResolutionWarned = false; function getLegacyExtensionFormat(ext) { - if ( - experimentalSpecifierResolution === 'node' && - !experimentalSpecifierResolutionWarned - ) { - process.emitWarning( - 'The Node.js specifier resolution in ESM is experimental.', - 'ExperimentalWarning'); - experimentalSpecifierResolutionWarned = true; - } return legacyExtensionFormatMap[ext]; } diff --git a/lib/internal/modules/esm/resolve.js b/lib/internal/modules/esm/resolve.js index 9d002a9a5c2e88..7751fa756e198b 100644 --- a/lib/internal/modules/esm/resolve.js +++ b/lib/internal/modules/esm/resolve.js @@ -362,6 +362,7 @@ function resolveDirectoryEntry(search) { } const encodedSepRegEx = /%2F|%5C/i; +let experimentalSpecifierResolutionWarned = false; /** * @param {URL} resolved * @param {string | URL | undefined} base @@ -376,6 +377,13 @@ function finalizeResolution(resolved, base, preserveSymlinks) { let path = fileURLToPath(resolved); if (getOptionValue('--experimental-specifier-resolution') === 'node') { + if (!experimentalSpecifierResolutionWarned) { + process.emitWarning( + 'The Node.js specifier resolution flag is experimental. It could change or be removed at any time.', + 'ExperimentalWarning'); + experimentalSpecifierResolutionWarned = true; + } + let file = resolveExtensionsWithTryExactName(resolved); // Directory diff --git a/test/es-module/test-esm-specifiers-legacy-flag-warning.mjs b/test/es-module/test-esm-specifiers-legacy-flag-warning.mjs new file mode 100644 index 00000000000000..244499a3e02093 --- /dev/null +++ b/test/es-module/test-esm-specifiers-legacy-flag-warning.mjs @@ -0,0 +1,24 @@ +import { mustCall } from '../common/index.mjs'; +import { fileURL } from '../common/fixtures.mjs'; +import { match, strictEqual } from 'assert'; +import { spawn } from 'child_process'; +import { execPath } from 'process'; + +// Verify experimental warning is printed +const child = spawn(execPath, [ + '--experimental-specifier-resolution=node', + '--input-type=module', + '--eval', + `import ${JSON.stringify(fileURL('es-module-specifiers', 'package-type-module'))}`, +]); + +let stderr = ''; +child.stderr.setEncoding('utf8'); +child.stderr.on('data', (data) => { + stderr += data; +}); +child.on('close', mustCall((code, signal) => { + strictEqual(code, 0); + strictEqual(signal, null); + match(stderr, /ExperimentalWarning: The Node\.js specifier resolution flag is experimental/); +}));