GeezerVison is showing github.com — esm: remove specifier resolution flag · nodejs/node@f594cc8
File treeExpand file treeCollapse file tree Open diff view settings fixtures/es-module-specifiers
Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/api/cli.md -18Lines changed: 0 additions & 18 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -427,23 +427,6 @@ added: REPLACEME | 427 | 427 |
| 428 | 428 | Use this flag to enable [ShadowRealm][] support. | 429 | 429 |
| 430 | | -### `--experimental-specifier-resolution=mode` | 431 | | - | 432 | | -<!-- YAML | 433 | | -added: | 434 | | - - v13.4.0 | 435 | | - - v12.16.0 | 436 | | ---> | 437 | | - | 438 | | -Sets the resolution algorithm for resolving ES module specifiers. Valid options | 439 | | -are `explicit` and `node`. | 440 | | - | 441 | | -The default is `explicit`, which requires providing the full path to a | 442 | | -module. The `node` mode enables support for optional file extensions and | 443 | | -the ability to import a directory that has an index file. | 444 | | - | 445 | | -See [customizing ESM specifier resolution][] for example usage. | 446 | | - | 447 | 430 | ### `--experimental-vm-modules` | 448 | 431 |
| 449 | 432 | <!-- YAML | @@ -2312,7 +2295,6 @@ done | 2312 | 2295 | [`worker_threads.threadId`]: worker_threads.md#workerthreadid | 2313 | 2296 | [conditional exports]: packages.md#conditional-exports | 2314 | 2297 | [context-aware]: addons.md#context-aware-addons | 2315 | | -[customizing ESM specifier resolution]: esm.md#customizing-esm-specifier-resolution-algorithm | 2316 | 2298 | [debugger]: debugger.md | 2317 | 2299 | [debugging security implications]: https://nodejs.org/en/docs/guides/debugging-getting-started/#security-implications | 2318 | 2300 | [emit_warning]: process.md#processemitwarningwarning-options | |
Collapse file Copy file name to clipboardExpand all lines: doc/api/esm.md +4-25Lines changed: 4 additions & 25 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -1518,31 +1518,9 @@ _isImports_, _conditions_) | 1518 | 1518 |
| 1519 | 1519 | ### Customizing ESM specifier resolution algorithm | 1520 | 1520 |
| 1521 | | -> Stability: 1 - Experimental | 1522 | | -
| 1523 | | -> Do not rely on this flag. We plan to remove it once the | 1524 | | -> [Loaders API][] has advanced to the point that equivalent functionality can | 1525 | | -> be achieved via custom loaders. | 1526 | | -
| 1527 | | -The current specifier resolution does not support all default behavior of | 1528 | | -the CommonJS loader. One of the behavior differences is automatic resolution | 1529 | | -of file extensions and the ability to import directories that have an index | 1530 | | -file. | 1531 | | -
| 1532 | | -The `--experimental-specifier-resolution=[mode]` flag can be used to customize | 1533 | | -the extension resolution algorithm. The default mode is `explicit`, which | 1534 | | -requires the full path to a module be provided to the loader. To enable the | 1535 | | -automatic extension resolution and importing from directories that include an | 1536 | | -index file use the `node` mode. | 1537 | | -
| 1538 | | -```console | 1539 | | -$ node index.mjs | 1540 | | -success! | 1541 | | -$ node index # Failure! | 1542 | | -Error: Cannot find module | 1543 | | -$ node --experimental-specifier-resolution=node index | 1544 | | -success! | 1545 | | -``` | | 1521 | +The [Loaders API][] provides a mechanism for customizing the ESM specifier | | 1522 | +resolution algorithm. An example loader that provides CommonJS-style resolution | | 1523 | +for ESM specifiers is [commonjs-extension-resolution-loader][]. | 1546 | 1524 |
| 1547 | 1525 | <!-- Note: The cjs-module-lexer link should be kept in-sync with the deps version --> | 1548 | 1526 |
| @@ -1583,6 +1561,7 @@ success! | 1583 | 1561 | [`string`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String | 1584 | 1562 | [`util.TextDecoder`]: util.md#class-utiltextdecoder | 1585 | 1563 | [cjs-module-lexer]: https://github.com/nodejs/cjs-module-lexer/tree/1.2.2 | | 1564 | +[commonjs-extension-resolution-loader]: https://github.com/nodejs/loaders-test/tree/main/commonjs-extension-resolution-loader | 1586 | 1565 | [custom https loader]: #https-loader | 1587 | 1566 | [load hook]: #loadurl-context-nextload | 1588 | 1567 | [percent-encoded]: url.md#percent-encoding-in-urls | |
Collapse file Copy file name to clipboardExpand all lines: doc/node.1 -3Lines changed: 0 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -171,9 +171,6 @@ Disable exposition of the Web Crypto API on the global scope. | 171 | 171 | .It Fl -no-experimental-repl-await | 172 | 172 | Disable top-level await keyword support in REPL. | 173 | 173 | . | 174 | | -.It Fl -experimental-specifier-resolution | 175 | | -Select extension resolution algorithm for ES Modules; either 'explicit' (default) or 'node'. | 176 | | -. | 177 | 174 | .It Fl -experimental-vm-modules | 178 | 175 | Enable experimental ES module support in VM module. | 179 | 176 | . | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/formats.js +1-17Lines changed: 1 addition & 17 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 5 | 5 | } = primordials; | 6 | 6 | const { getOptionValue } = require('internal/options'); | 7 | 7 |
| 8 | | - | 9 | 8 | const experimentalWasmModules = getOptionValue('--experimental-wasm-modules'); | 10 | 9 |
| 11 | 10 | const extensionFormatMap = { | @@ -16,17 +15,8 @@ const extensionFormatMap = { | 16 | 15 | '.mjs': 'module', | 17 | 16 | }; | 18 | 17 |
| 19 | | -const legacyExtensionFormatMap = { | 20 | | - '__proto__': null, | 21 | | - '.cjs': 'commonjs', | 22 | | - '.js': 'commonjs', | 23 | | - '.json': 'commonjs', | 24 | | - '.mjs': 'module', | 25 | | - '.node': 'commonjs', | 26 | | -}; | 27 | | - | 28 | 18 | if (experimentalWasmModules) { | 29 | | - extensionFormatMap['.wasm'] = legacyExtensionFormatMap['.wasm'] = 'wasm'; | | 19 | + extensionFormatMap['.wasm'] = 'wasm'; | 30 | 20 | } | 31 | 21 |
| 32 | 22 | /** | @@ -45,13 +35,7 @@ function mimeToFormat(mime) { | 45 | 35 | return null; | 46 | 36 | } | 47 | 37 |
| 48 | | -function getLegacyExtensionFormat(ext) { | 49 | | - return legacyExtensionFormatMap[ext]; | 50 | | -} | 51 | | - | 52 | 38 | module.exports = { | 53 | 39 | extensionFormatMap, | 54 | | - getLegacyExtensionFormat, | 55 | | - legacyExtensionFormatMap, | 56 | 40 | mimeToFormat, | 57 | 41 | }; |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/get_format.js +14-21Lines changed: 14 additions & 21 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -11,14 +11,11 @@ const { getOptionValue } = require('internal/options'); | 11 | 11 | const { fetchModule } = require('internal/modules/esm/fetch_module'); | 12 | 12 | const { | 13 | 13 | extensionFormatMap, | 14 | | - getLegacyExtensionFormat, | 15 | 14 | mimeToFormat, | 16 | 15 | } = require('internal/modules/esm/formats'); | 17 | 16 |
| 18 | 17 | const experimentalNetworkImports = | 19 | 18 | getOptionValue('--experimental-network-imports'); | 20 | | -const experimentalSpecifierResolution = | 21 | | - getOptionValue('--experimental-specifier-resolution'); | 22 | 19 | const { getPackageType, getPackageScopeConfig } = require('internal/modules/esm/resolve'); | 23 | 20 | const { URL, fileURLToPath } = require('internal/url'); | 24 | 21 | const { ERR_UNKNOWN_FILE_EXTENSION } = require('internal/errors').codes; | @@ -61,25 +58,21 @@ function getFileProtocolModuleFormat(url, context, ignoreErrors) { | 61 | 58 | const format = extensionFormatMap[ext]; | 62 | 59 | if (format) return format; | 63 | 60 |
| 64 | | - if (experimentalSpecifierResolution !== 'node') { | 65 | | - // Explicit undefined return indicates load hook should rerun format check | 66 | | - if (ignoreErrors) return undefined; | 67 | | - let suggestion = ''; | 68 | | - if (getPackageType(url) === 'module' && ext === '') { | 69 | | - const config = getPackageScopeConfig(url); | 70 | | - const fileBasename = basename(filepath); | 71 | | - const relativePath = StringPrototypeSlice(relative(config.pjsonPath, filepath), 1); | 72 | | - suggestion = 'Loading extensionless files is not supported inside of ' + | 73 | | - '"type":"module" package.json contexts. The package.json file ' + | 74 | | - `${config.pjsonPath} caused this "type":"module" context. Try ` + | 75 | | - `changing ${filepath} to have a file extension. Note the "bin" ` + | 76 | | - 'field of package.json can point to a file with an extension, for example ' + | 77 | | - `{"type":"module","bin":{"${fileBasename}":"${relativePath}.js"}}`; | 78 | | - } | 79 | | - throw new ERR_UNKNOWN_FILE_EXTENSION(ext, filepath, suggestion); | | 61 | + // Explicit undefined return indicates load hook should rerun format check | | 62 | + if (ignoreErrors) { return undefined; } | | 63 | + let suggestion = ''; | | 64 | + if (getPackageType(url) === 'module' && ext === '') { | | 65 | + const config = getPackageScopeConfig(url); | | 66 | + const fileBasename = basename(filepath); | | 67 | + const relativePath = StringPrototypeSlice(relative(config.pjsonPath, filepath), 1); | | 68 | + suggestion = 'Loading extensionless files is not supported inside of ' + | | 69 | + '"type":"module" package.json contexts. The package.json file ' + | | 70 | + `${config.pjsonPath} caused this "type":"module" context. Try ` + | | 71 | + `changing ${filepath} to have a file extension. Note the "bin" ` + | | 72 | + 'field of package.json can point to a file with an extension, for example ' + | | 73 | + `{"type":"module","bin":{"${fileBasename}":"${relativePath}.js"}}`; | 80 | 74 | } | 81 | | - | 82 | | - return getLegacyExtensionFormat(ext) ?? null; | | 75 | + throw new ERR_UNKNOWN_FILE_EXTENSION(ext, filepath, suggestion); | 83 | 76 | } | 84 | 77 |
| 85 | 78 | /** | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/loader.js -12Lines changed: 0 additions & 12 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -89,8 +89,6 @@ const { getOptionValue } = require('internal/options'); | 89 | 89 |
| 90 | 90 | // [2] `validate...()`s throw the wrong error | 91 | 91 |
| 92 | | -let emittedSpecifierResolutionWarning = false; | 93 | | - | 94 | 92 | /** | 95 | 93 | * A utility function to iterate through a hook chain, track advancement in the | 96 | 94 | * chain, and generate and supply the `next<HookName>` argument to the custom | @@ -241,16 +239,6 @@ class ESMLoader { | 241 | 239 | if (getOptionValue('--experimental-network-imports')) { | 242 | 240 | emitExperimentalWarning('Network Imports'); | 243 | 241 | } | 244 | | - if ( | 245 | | - !emittedSpecifierResolutionWarning && | 246 | | - getOptionValue('--experimental-specifier-resolution') === 'node' | 247 | | - ) { | 248 | | - process.emitWarning( | 249 | | - 'The Node.js specifier resolution flag is experimental. It could change or be removed at any time.', | 250 | | - 'ExperimentalWarning' | 251 | | - ); | 252 | | - emittedSpecifierResolutionWarning = true; | 253 | | - } | 254 | 242 | } | 255 | 243 |
| 256 | 244 | /** | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/resolve.js +2-66Lines changed: 2 additions & 66 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 5 | 5 | ArrayPrototypeConcat, | 6 | 6 | ArrayPrototypeJoin, | 7 | 7 | ArrayPrototypeShift, | 8 | | - JSONParse, | 9 | 8 | JSONStringify, | 10 | 9 | ObjectFreeze, | 11 | 10 | ObjectGetOwnPropertyNames, | @@ -37,7 +36,7 @@ const { getOptionValue } = require('internal/options'); | 37 | 36 | const policy = getOptionValue('--experimental-policy') ? | 38 | 37 | require('internal/process/policy') : | 39 | 38 | null; | 40 | | -const { sep, relative, resolve } = require('path'); | | 39 | +const { sep, relative } = require('path'); | 41 | 40 | const preserveSymlinks = getOptionValue('--preserve-symlinks'); | 42 | 41 | const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main'); | 43 | 42 | const experimentalNetworkImports = | @@ -60,7 +59,6 @@ const { | 60 | 59 | } = require('internal/errors').codes; | 61 | 60 |
| 62 | 61 | const { Module: CJSModule } = require('internal/modules/cjs/loader'); | 63 | | -const packageJsonReader = require('internal/modules/package_json_reader'); | 64 | 62 | const { getPackageConfig, getPackageScopeConfig } = require('internal/modules/esm/package_config'); | 65 | 63 |
| 66 | 64 | /** | @@ -234,50 +232,6 @@ function legacyMainResolve(packageJSONUrl, packageConfig, base) { | 234 | 232 | fileURLToPath(new URL('.', packageJSONUrl)), fileURLToPath(base)); | 235 | 233 | } | 236 | 234 |
| 237 | | -/** | 238 | | - * @param {URL} search | 239 | | - * @returns {URL | undefined} | 240 | | - */ | 241 | | -function resolveExtensionsWithTryExactName(search) { | 242 | | - if (fileExists(search)) return search; | 243 | | - return resolveExtensions(search); | 244 | | -} | 245 | | - | 246 | | -const extensions = ['.js', '.json', '.node', '.mjs']; | 247 | | - | 248 | | -/** | 249 | | - * @param {URL} search | 250 | | - * @returns {URL | undefined} | 251 | | - */ | 252 | | -function resolveExtensions(search) { | 253 | | - for (let i = 0; i < extensions.length; i++) { | 254 | | - const extension = extensions[i]; | 255 | | - const guess = new URL(`${search.pathname}${extension}`, search); | 256 | | - if (fileExists(guess)) return guess; | 257 | | - } | 258 | | - return undefined; | 259 | | -} | 260 | | - | 261 | | -/** | 262 | | - * @param {URL} search | 263 | | - * @returns {URL | undefined} | 264 | | - */ | 265 | | -function resolveDirectoryEntry(search) { | 266 | | - const dirPath = fileURLToPath(search); | 267 | | - const pkgJsonPath = resolve(dirPath, 'package.json'); | 268 | | - if (fileExists(pkgJsonPath)) { | 269 | | - const pkgJson = packageJsonReader.read(pkgJsonPath); | 270 | | - if (pkgJson.containsKeys) { | 271 | | - const { main } = JSONParse(pkgJson.string); | 272 | | - if (main != null) { | 273 | | - const mainUrl = pathToFileURL(resolve(dirPath, main)); | 274 | | - return resolveExtensionsWithTryExactName(mainUrl); | 275 | | - } | 276 | | - } | 277 | | - } | 278 | | - return resolveExtensions(new URL('index', search)); | 279 | | -} | 280 | | - | 281 | 235 | const encodedSepRegEx = /%2F|%5C/i; | 282 | 236 | /** | 283 | 237 | * @param {URL} resolved | @@ -291,25 +245,7 @@ function finalizeResolution(resolved, base, preserveSymlinks) { | 291 | 245 | resolved.pathname, 'must not include encoded "/" or "\\" characters', | 292 | 246 | fileURLToPath(base)); | 293 | 247 |
| 294 | | - let path = fileURLToPath(resolved); | 295 | | - if (getOptionValue('--experimental-specifier-resolution') === 'node') { | 296 | | - let file = resolveExtensionsWithTryExactName(resolved); | 297 | | - | 298 | | - // Directory | 299 | | - if (file === undefined) { | 300 | | - file = StringPrototypeEndsWith(path, '/') ? | 301 | | - (resolveDirectoryEntry(resolved) || resolved) : resolveDirectoryEntry(new URL(`${resolved}/`)); | 302 | | - | 303 | | - if (file === resolved) return file; | 304 | | - | 305 | | - if (file === undefined) { | 306 | | - throw new ERR_MODULE_NOT_FOUND( | 307 | | - resolved.pathname, fileURLToPath(base), 'module'); | 308 | | - } | 309 | | - } | 310 | | - | 311 | | - path = file; | 312 | | - } | | 248 | + const path = fileURLToPath(resolved); | 313 | 249 |
| 314 | 250 | const stats = tryStatSync(StringPrototypeEndsWith(path, '/') ? | 315 | 251 | StringPrototypeSlice(path, -1) : path); | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/run_main.js -4Lines changed: 0 additions & 4 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -40,10 +40,6 @@ function shouldUseESMLoader(mainPath) { | 40 | 40 | const userImports = getOptionValue('--import'); | 41 | 41 | if (userLoaders.length > 0 || userImports.length > 0) | 42 | 42 | return true; | 43 | | - const esModuleSpecifierResolution = | 44 | | - getOptionValue('--experimental-specifier-resolution'); | 45 | | - if (esModuleSpecifierResolution === 'node') | 46 | | - return true; | 47 | 43 | // Determine the module format of the main | 48 | 44 | if (mainPath && StringPrototypeEndsWith(mainPath, '.mjs')) | 49 | 45 | return true; | |
Collapse file Copy file name to clipboardExpand all lines: lib/repl.js +1-5Lines changed: 1 addition & 5 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -180,7 +180,6 @@ const { | 180 | 180 | const history = require('internal/repl/history'); | 181 | 181 | const { | 182 | 182 | extensionFormatMap, | 183 | | - legacyExtensionFormatMap, | 184 | 183 | } = require('internal/modules/esm/formats'); | 185 | 184 |
| 186 | 185 | let nextREPLResourceNumber = 1; | @@ -1377,10 +1376,7 @@ function complete(line, callback) { | 1377 | 1376 | if (this.allowBlockingCompletions) { | 1378 | 1377 | const subdir = match[2] || ''; | 1379 | 1378 | // File extensions that can be imported: | 1380 | | - const extensions = ObjectKeys( | 1381 | | - getOptionValue('--experimental-specifier-resolution') === 'node' ? | 1382 | | - legacyExtensionFormatMap : | 1383 | | - extensionFormatMap); | | 1379 | + const extensions = ObjectKeys(extensionFormatMap); | 1384 | 1380 |
| 1385 | 1381 | // Only used when loading bare module specifiers from `node_modules`: | 1386 | 1382 | const indexes = ArrayPrototypeMap(extensions, (ext) => `index${ext}`); | |
Collapse file Copy file name to clipboardExpand all lines: src/node_options.cc +2-13Lines changed: 2 additions & 13 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -113,14 +113,6 @@ void EnvironmentOptions::CheckOptions(std::vector<std::string>* errors) { | 113 | 113 | } | 114 | 114 | } | 115 | 115 |
| 116 | | - if (!experimental_specifier_resolution.empty()) { | 117 | | - if (experimental_specifier_resolution != "node" && | 118 | | - experimental_specifier_resolution != "explicit") { | 119 | | - errors->push_back( | 120 | | - "invalid value for --experimental-specifier-resolution"); | 121 | | - } | 122 | | - } | 123 | | - | 124 | 116 | if (syntax_check_only && has_eval_string) { | 125 | 117 | errors->push_back("either --check or --eval can be used, not both"); | 126 | 118 | } | @@ -444,11 +436,8 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { | 444 | 436 | "set module type for string input", | 445 | 437 | &EnvironmentOptions::module_type, | 446 | 438 | kAllowedInEnvironment); | 447 | | - AddOption("--experimental-specifier-resolution", | 448 | | - "Select extension resolution algorithm for es modules; " | 449 | | - "either 'explicit' (default) or 'node'", | 450 | | - &EnvironmentOptions::experimental_specifier_resolution, | 451 | | - kAllowedInEnvironment); | | 439 | + AddOption( | | 440 | + "--experimental-specifier-resolution", "", NoOp{}, kAllowedInEnvironment); | 452 | 441 | AddAlias("--es-module-specifier-resolution", | 453 | 442 | "--experimental-specifier-resolution"); | 454 | 443 | AddOption("--deprecation", | |
|