Skip to content

Commit 4ee41c5

Browse files
GeoffreyBoothMylesBorins
authored andcommitted
module: drop support for extensionless main entry points in esm
Backport-PR-URL: #32280 PR-URL: #31415 Backport-PR-URL: #32280 Reviewed-By: Guy Bedford <[email protected]> Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: Bradley Farias <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent dca3d29 commit 4ee41c5

File tree

5 files changed

+57
-136
lines changed

5 files changed

+57
-136
lines changed

doc/api/esm.md

+20-27
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,8 @@ ES module code:
3636

3737
* Files ending in `.mjs`.
3838

39-
* Files ending in `.js`, or extensionless files, when the nearest parent
40-
`package.json` file contains a top-level field `"type"` with a value of
41-
`"module"`.
39+
* Files ending in `.js` when the nearest parent `package.json` file contains a
40+
top-level field `"type"` with a value of `"module"`.
4241

4342
* Strings passed in as an argument to `--eval` or `--print`, or piped to
4443
`node` via `STDIN`, with the flag `--input-type=module`.
@@ -53,18 +52,17 @@ or when referenced by `import` statements within ES module code:
5352

5453
* Files ending in `.cjs`.
5554

56-
* Files ending in `.js`, or extensionless files, when the nearest parent
57-
`package.json` file contains a top-level field `"type"` with a value of
58-
`"commonjs"`.
55+
* Files ending in `.js` when the nearest parent `package.json` file contains a
56+
top-level field `"type"` with a value of `"commonjs"`.
5957

6058
* Strings passed in as an argument to `--eval` or `--print`, or piped to
6159
`node` via `STDIN`, with the flag `--input-type=commonjs`.
6260

6361
### `package.json` `"type"` field
6462

65-
Files ending with `.js` or lacking any extension will be loaded as ES modules
66-
when the nearest parent `package.json` file contains a top-level field `"type"`
67-
with a value of `"module"`.
63+
Files ending with `.js` will be loaded as ES modules when the nearest parent
64+
`package.json` file contains a top-level field `"type"` with a value of
65+
`"module"`.
6866

6967
The nearest parent `package.json` is defined as the first `package.json` found
7068
when searching in the current folder, that folder’s parent, and so on up
@@ -84,14 +82,12 @@ node --experimental-modules my-app.js # Runs as ES module
8482
```
8583

8684
If the nearest parent `package.json` lacks a `"type"` field, or contains
87-
`"type": "commonjs"`, extensionless and `.js` files are treated as CommonJS.
88-
If the volume root is reached and no `package.json` is found,
89-
Node.js defers to the default, a `package.json` with no `"type"`
90-
field. "Extensionless" refers to file paths which do not contain
91-
an extension as opposed to optionally dropping a file extension in a specifier.
85+
`"type": "commonjs"`, `.js` files are treated as CommonJS. If the volume root is
86+
reached and no `package.json` is found, Node.js defers to the default, a
87+
`package.json` with no `"type"` field.
9288

93-
`import` statements of `.js` and extensionless files are treated as ES modules
94-
if the nearest parent `package.json` contains `"type": "module"`.
89+
`import` statements of `.js` files are treated as ES modules if the nearest
90+
parent `package.json` contains `"type": "module"`.
9591

9692
```js
9793
// my-app.js, part of the same example as above
@@ -109,14 +105,13 @@ as ES modules and `.cjs` files are always treated as CommonJS.
109105

110106
### Package Scope and File Extensions
111107

112-
A folder containing a `package.json` file, and all subfolders below that
113-
folder down until the next folder containing another `package.json`, is
114-
considered a _package scope_. The `"type"` field defines how `.js` and
115-
extensionless files should be treated within a particular `package.json` file’s
116-
package scope. Every package in a project’s `node_modules` folder contains its
117-
own `package.json` file, so each project’s dependencies have their own package
118-
scopes. A `package.json` lacking a `"type"` field is treated as if it contained
119-
`"type": "commonjs"`.
108+
A folder containing a `package.json` file, and all subfolders below that folder
109+
down until the next folder containing another `package.json`, is considered a
110+
_package scope_. The `"type"` field defines how `.js` files should be treated
111+
within a particular `package.json` file’s package scope. Every package in a
112+
project’s `node_modules` folder contains its own `package.json` file, so each
113+
project’s dependencies have their own package scopes. A `package.json` lacking a
114+
`"type"` field is treated as if it contained `"type": "commonjs"`.
120115

121116
The package scope applies not only to initial entry points (`node
122117
--experimental-modules my-app.js`) but also to files referenced by `import`
@@ -1610,12 +1605,10 @@ _defaultEnv_ is the conditional environment name priority array,
16101605
> 1. If _url_ ends in _".cjs"_, then
16111606
> 1. Return _"commonjs"_.
16121607
> 1. If _pjson?.type_ exists and is _"module"_, then
1613-
> 1. If _url_ ends in _".js"_ or lacks a file extension, then
1608+
> 1. If _url_ ends in _".js"_, then
16141609
> 1. Return _"module"_.
16151610
> 1. Throw an _Unsupported File Extension_ error.
16161611
> 1. Otherwise,
1617-
> 1. If _url_ lacks a file extension, then
1618-
> 1. Return _"commonjs"_.
16191612
> 1. Throw an _Unsupported File Extension_ error.
16201613
16211614
**READ_PACKAGE_SCOPE**(_url_)

lib/internal/modules/esm/get_format.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function defaultGetFormat(url, context, defaultGetFormat) {
5555
} else if (parsed.protocol === 'file:') {
5656
const ext = extname(parsed.pathname);
5757
let format;
58-
if (ext === '.js' || ext === '') {
58+
if (ext === '.js') {
5959
format = getPackageType(parsed.href) === TYPE_MODULE ?
6060
'module' : 'commonjs';
6161
} else {

test/es-module/test-esm-no-extension.js

-27
This file was deleted.

test/es-module/test-esm-unknown-main.js

-81
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const fixtures = require('../common/fixtures');
5+
const { spawn } = require('child_process');
6+
const assert = require('assert');
7+
8+
// In a "type": "module" package scope, files with unknown extensions or no
9+
// extensions should throw; both when used as a main entry point and also when
10+
// referenced via `import`.
11+
12+
[
13+
'/es-modules/package-type-module/noext-esm',
14+
'/es-modules/package-type-module/imports-noext.mjs',
15+
'/es-modules/package-type-module/extension.unknown',
16+
'/es-modules/package-type-module/imports-unknownext.mjs',
17+
].forEach((fixturePath) => {
18+
const entry = fixtures.path(fixturePath);
19+
const child = spawn(process.execPath, ['--experimental-modules', entry]);
20+
let stdout = '';
21+
let stderr = '';
22+
child.stderr.setEncoding('utf8');
23+
child.stdout.setEncoding('utf8');
24+
child.stdout.on('data', (data) => {
25+
stdout += data;
26+
});
27+
child.stderr.on('data', (data) => {
28+
stderr += data;
29+
});
30+
child.on('close', common.mustCall((code, signal) => {
31+
assert.strictEqual(code, 1);
32+
assert.strictEqual(signal, null);
33+
assert.strictEqual(stdout, '');
34+
assert.ok(stderr.indexOf('ERR_UNKNOWN_FILE_EXTENSION') !== -1);
35+
}));
36+
});

0 commit comments

Comments
 (0)