Skip to content

Commit eb4f443

Browse files
MylesBorinstargos
authored andcommitted
esm: make specifier flag clearly experimental
`--es-module-specifier-resolution` is the only flagged portion of the ESM implementation that does not have the word experimental in the flag name. This commit changes the flag to: `--experimental-specifier-resolution` `--es-module-specifier-resolution` remains as an alias for backwards compatibility but it is no longer documented. PR-URL: #30678 Reviewed-By: Jan Krems <[email protected]> Reviewed-By: Guy Bedford <[email protected]>
1 parent 7b9400c commit eb4f443

11 files changed

+89
-31
lines changed

doc/api/cli.md

+15-15
Original file line numberDiff line numberDiff line change
@@ -156,20 +156,6 @@ Enable experimental Source Map V3 support for stack traces.
156156
Currently, overriding `Error.prepareStackTrace` is ignored when the
157157
`--enable-source-maps` flag is set.
158158

159-
### `--es-module-specifier-resolution=mode`
160-
<!-- YAML
161-
added: v12.0.0
162-
-->
163-
164-
Sets the resolution algorithm for resolving ES module specifiers. Valid options
165-
are `explicit` and `node`.
166-
167-
The default is `explicit`, which requires providing the full path to a
168-
module. The `node` mode will enable support for optional file extensions and
169-
the ability to import a directory that has an index file.
170-
171-
Please see [customizing ESM specifier resolution][] for example usage.
172-
173159
### `--experimental-conditional-exports`
174160
<!-- YAML
175161
added: v13.2.0
@@ -223,6 +209,20 @@ added: v13.1.0
223209
Enable experimental support for a package using `require` or `import` to load
224210
itself.
225211

212+
### `--experimental-specifier-resolution=mode`
213+
<!-- YAML
214+
added: REPLACEME
215+
-->
216+
217+
Sets the resolution algorithm for resolving ES module specifiers. Valid options
218+
are `explicit` and `node`.
219+
220+
The default is `explicit`, which requires providing the full path to a
221+
module. The `node` mode will enable support for optional file extensions and
222+
the ability to import a directory that has an index file.
223+
224+
Please see [customizing ESM specifier resolution][] for example usage.
225+
226226
### `--experimental-vm-modules`
227227
<!-- YAML
228228
added: v9.6.0
@@ -1045,7 +1045,6 @@ Node.js options that are allowed are:
10451045
<!-- node-options-node start -->
10461046
* `--enable-fips`
10471047
* `--enable-source-maps`
1048-
* `--es-module-specifier-resolution`
10491048
* `--experimental-conditional-exports`
10501049
* `--experimental-json-modules`
10511050
* `--experimental-loader`
@@ -1054,6 +1053,7 @@ Node.js options that are allowed are:
10541053
* `--experimental-repl-await`
10551054
* `--experimental-report`
10561055
* `--experimental-resolve-self`
1056+
* `--experimental-specifier-resolution`
10571057
* `--experimental-vm-modules`
10581058
* `--experimental-wasi-unstable-preview0`
10591059
* `--experimental-wasm-modules`

doc/api/esm.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1372,7 +1372,7 @@ the CommonJS loader. One of the behavior differences is automatic resolution
13721372
of file extensions and the ability to import directories that have an index
13731373
file.
13741374
1375-
The `--es-module-specifier-resolution=[mode]` flag can be used to customize
1375+
The `--experimental-specifier-resolution=[mode]` flag can be used to customize
13761376
the extension resolution algorithm. The default mode is `explicit`, which
13771377
requires the full path to a module be provided to the loader. To enable the
13781378
automatic extension resolution and importing from directories that include an
@@ -1383,7 +1383,7 @@ $ node index.mjs
13831383
success!
13841384
$ node index # Failure!
13851385
Error: Cannot find module
1386-
$ node --es-module-specifier-resolution=node index
1386+
$ node --experimental-specifier-resolution=node index
13871387
success!
13881388
```
13891389

doc/node.1

+3-3
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,6 @@ Enable FIPS-compliant crypto at startup.
110110
Requires Node.js to be built with
111111
.Sy ./configure --openssl-fips .
112112
.
113-
.It Fl -es-module-specifier-resolution
114-
Select extension resolution algorithm for ES Modules; either 'explicit' (default) or 'node'
115-
.
116113
.It Fl -experimental-conditional-exports
117114
Enable experimental support for "require" and "node" conditional export targets.
118115
.
@@ -130,6 +127,9 @@ Enable experimental top-level
130127
.Sy await
131128
keyword support in REPL.
132129
.
130+
.It Fl -experimental-specifier-resolution
131+
Select extension resolution algorithm for ES Modules; either 'explicit' (default) or 'node'
132+
.
133133
.It Fl -experimental-report
134134
Enable experimental
135135
.Sy diagnostic report

lib/internal/modules/esm/default_resolve.js

+8-4
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ const { getOptionValue } = require('internal/options');
1313
const preserveSymlinks = getOptionValue('--preserve-symlinks');
1414
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
1515
const experimentalJsonModules = getOptionValue('--experimental-json-modules');
16-
const esModuleSpecifierResolution =
17-
getOptionValue('--es-module-specifier-resolution');
16+
const experimentalSpeciferResolution =
17+
getOptionValue('--experimental-specifier-resolution');
1818
const typeFlag = getOptionValue('--input-type');
1919
const experimentalWasmModules = getOptionValue('--experimental-wasm-modules');
2020
const { resolve: moduleWrapResolve,
@@ -110,10 +110,14 @@ function resolve(specifier, parentURL) {
110110
if (ext === '.js' || (!format && isMain))
111111
format = getPackageType(url.href) === TYPE_MODULE ? 'module' : 'commonjs';
112112
if (!format) {
113-
if (esModuleSpecifierResolution === 'node')
113+
if (experimentalSpeciferResolution === 'node') {
114+
process.emitWarning(
115+
'The Node.js specifier resolution in ESM is experimental.',
116+
'ExperimentalWarning');
114117
format = legacyExtensionFormatMap[ext];
115-
else
118+
} else {
116119
throw new ERR_UNKNOWN_FILE_EXTENSION(fileURLToPath(url));
120+
}
117121
}
118122
return { url: `${url}`, format };
119123
}

src/module_wrap.cc

+2-2
Original file line numberDiff line numberDiff line change
@@ -789,7 +789,7 @@ inline Maybe<URL> ResolveIndex(const URL& search) {
789789
Maybe<URL> FinalizeResolution(Environment* env,
790790
const URL& resolved,
791791
const URL& base) {
792-
if (env->options()->es_module_specifier_resolution == "node") {
792+
if (env->options()->experimental_specifier_resolution == "node") {
793793
Maybe<URL> file = ResolveExtensions<TRY_EXACT_NAME>(resolved);
794794
if (!file.IsNothing()) {
795795
return file;
@@ -1053,7 +1053,7 @@ Maybe<URL> PackageMainResolve(Environment* env,
10531053
return Just(resolved);
10541054
}
10551055
}
1056-
if (env->options()->es_module_specifier_resolution == "node") {
1056+
if (env->options()->experimental_specifier_resolution == "node") {
10571057
if (pcfg.has_main == HasMain::Yes) {
10581058
return FinalizeResolution(env, URL(pcfg.main, pjson_url), base);
10591059
} else {

src/node_options.cc

+22-4
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,23 @@ void EnvironmentOptions::CheckOptions(std::vector<std::string>* errors) {
128128
}
129129

130130
if (!es_module_specifier_resolution.empty()) {
131-
if (es_module_specifier_resolution != "node" &&
132-
es_module_specifier_resolution != "explicit") {
133-
errors->push_back("invalid value for --es-module-specifier-resolution");
131+
if (!experimental_specifier_resolution.empty()) {
132+
errors->push_back(
133+
"bad option: cannot use --es-module-specifier-resolution"
134+
" and --experimental-specifier-resolution at the same time");
135+
} else {
136+
experimental_specifier_resolution = es_module_specifier_resolution;
137+
if (experimental_specifier_resolution != "node" &&
138+
experimental_specifier_resolution != "explicit") {
139+
errors->push_back(
140+
"invalid value for --es-module-specifier-resolution");
141+
}
142+
}
143+
} else if (!experimental_specifier_resolution.empty()) {
144+
if (experimental_specifier_resolution != "node" &&
145+
experimental_specifier_resolution != "explicit") {
146+
errors->push_back(
147+
"invalid value for --experimental-specifier-resolution");
134148
}
135149
}
136150

@@ -365,9 +379,13 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
365379
"set module type for string input",
366380
&EnvironmentOptions::module_type,
367381
kAllowedInEnvironment);
368-
AddOption("--es-module-specifier-resolution",
382+
AddOption("--experimental-specifier-resolution",
369383
"Select extension resolution algorithm for es modules; "
370384
"either 'explicit' (default) or 'node'",
385+
&EnvironmentOptions::experimental_specifier_resolution,
386+
kAllowedInEnvironment);
387+
AddOption("--es-module-specifier-resolution",
388+
"",
371389
&EnvironmentOptions::es_module_specifier_resolution,
372390
kAllowedInEnvironment);
373391
AddOption("--no-deprecation",

src/node_options.h

+1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ class EnvironmentOptions : public Options {
104104
bool experimental_conditional_exports = false;
105105
bool experimental_json_modules = false;
106106
bool experimental_resolve_self = false;
107+
std::string experimental_specifier_resolution;
107108
std::string es_module_specifier_resolution;
108109
bool experimental_wasm_modules = false;
109110
std::string module_type;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { mustCall } from '../common/index.mjs';
2+
import { exec } from 'child_process';
3+
import assert from 'assert';
4+
5+
const expectedError =
6+
'cannot use --es-module-specifier-resolution ' +
7+
'and --experimental-specifier-resolution at the same time';
8+
9+
const flags = '--es-module-specifier-resolution=node ' +
10+
'--experimental-specifier-resolution=node';
11+
12+
exec(`${process.execPath} ${flags}`, {
13+
timeout: 300
14+
}, mustCall((error) => {
15+
assert(error.message.includes(expectedError));
16+
}));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Flags: --es-module-specifier-resolution=node
2+
import '../common/index.mjs';
3+
import assert from 'assert';
4+
5+
// commonJS index.js
6+
import commonjs from '../fixtures/es-module-specifiers/package-type-commonjs';
7+
// esm index.js
8+
import module from '../fixtures/es-module-specifiers/package-type-module';
9+
// Notice the trailing slash
10+
import success, { explicit, implicit, implicitModule }
11+
from '../fixtures/es-module-specifiers/';
12+
13+
assert.strictEqual(commonjs, 'commonjs');
14+
assert.strictEqual(module, 'module');
15+
assert.strictEqual(success, 'success');
16+
assert.strictEqual(explicit, 'esm');
17+
assert.strictEqual(implicit, 'cjs');
18+
assert.strictEqual(implicitModule, 'cjs');

test/es-module/test-esm-specifiers.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Flags: --es-module-specifier-resolution=node
1+
// Flags: --experimental-specifier-resolution=node
22
import { mustNotCall } from '../common/index.mjs';
33
import assert from 'assert';
44
import path from 'path';

test/parallel/test-process-env-allowed-flags-are-documented.js

+1
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ const undocumented = difference(process.allowedNodeEnvironmentFlags,
8585
documented);
8686
// Remove intentionally undocumented options.
8787
assert(undocumented.delete('--debug-arraybuffer-allocations'));
88+
assert(undocumented.delete('--es-module-specifier-resolution'));
8889
assert(undocumented.delete('--experimental-worker'));
8990
assert(undocumented.delete('--no-node-snapshot'));
9091
assert(undocumented.delete('--loader'));

0 commit comments

Comments
 (0)