Skip to content

Commit 24a0212

Browse files
bfarias-godaddyBridgeAR
authored andcommitted
esm: empty ext from pkg type/main doesnt affect format
This ensures files with unknown extensions like foo.bar are not loaded as CJS/ESM when imported as a main entry point and makes sure that those files would maintain the same format even if loaded after the main entrypoint. PR-URL: #31021 Reviewed-By: Guy Bedford <[email protected]>
1 parent 85d152f commit 24a0212

File tree

6 files changed

+95
-10
lines changed

6 files changed

+95
-10
lines changed

doc/api/esm.md

+6-8
Original file line numberDiff line numberDiff line change
@@ -1154,15 +1154,13 @@ updates.
11541154
In the following algorithms, all subroutine errors are propagated as errors
11551155
of these top-level routines unless stated otherwise.
11561156
1157-
_isMain_ is **true** when resolving the Node.js application entry point.
1158-
11591157
_defaultEnv_ is the conditional environment name priority array,
11601158
`["node", "import"]`.
11611159
11621160
<details>
11631161
<summary>Resolver algorithm specification</summary>
11641162
1165-
**ESM_RESOLVE**(_specifier_, _parentURL_, _isMain_)
1163+
**ESM_RESOLVE**(_specifier_, _parentURL_)
11661164
11671165
> 1. Let _resolvedURL_ be **undefined**.
11681166
> 1. If _specifier_ is a valid URL, then
@@ -1183,7 +1181,7 @@ _defaultEnv_ is the conditional environment name priority array,
11831181
> 1. If the file at _resolvedURL_ does not exist, then
11841182
> 1. Throw a _Module Not Found_ error.
11851183
> 1. Set _resolvedURL_ to the real path of _resolvedURL_.
1186-
> 1. Let _format_ be the result of **ESM_FORMAT**(_resolvedURL_, _isMain_).
1184+
> 1. Let _format_ be the result of **ESM_FORMAT**(_resolvedURL_).
11871185
> 1. Load _resolvedURL_ as module format, _format_.
11881186
11891187
**PACKAGE_RESOLVE**(_packageSpecifier_, _parentURL_)
@@ -1334,20 +1332,20 @@ _defaultEnv_ is the conditional environment name priority array,
13341332
> 1. Return _resolved_.
13351333
> 1. Throw a _Module Not Found_ error.
13361334
1337-
**ESM_FORMAT**(_url_, _isMain_)
1335+
**ESM_FORMAT**(_url_)
13381336
1339-
> 1. Assert: _url_ corresponds to an existing file.
1337+
> 1. Assert: _url_ corresponds to an existing file pathname.
13401338
> 1. Let _pjson_ be the result of **READ_PACKAGE_SCOPE**(_url_).
13411339
> 1. If _url_ ends in _".mjs"_, then
13421340
> 1. Return _"module"_.
13431341
> 1. If _url_ ends in _".cjs"_, then
13441342
> 1. Return _"commonjs"_.
13451343
> 1. If _pjson?.type_ exists and is _"module"_, then
1346-
> 1. If _isMain_ is **true** or _url_ ends in _".js"_, then
1344+
> 1. If _url_ ends in _".js"_ or lacks a file extension, then
13471345
> 1. Return _"module"_.
13481346
> 1. Throw an _Unsupported File Extension_ error.
13491347
> 1. Otherwise,
1350-
> 1. If _isMain_ is **true**, then
1348+
> 1. If _url_ lacks a file extension, then
13511349
> 1. Return _"commonjs"_.
13521350
> 1. Throw an _Unsupported File Extension_ error.
13531351

lib/internal/modules/esm/default_resolve.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,12 @@ function resolve(specifier, parentURL) {
106106
}
107107

108108
const ext = extname(url.pathname);
109-
let format = extensionFormatMap[ext];
110-
if (ext === '.js' || (!format && isMain))
109+
let format;
110+
if (ext === '.js' || ext === '') {
111111
format = getPackageType(url.href) === TYPE_MODULE ? 'module' : 'commonjs';
112+
} else {
113+
format = extensionFormatMap[ext];
114+
}
112115
if (!format) {
113116
if (experimentalSpeciferResolution === 'node') {
114117
process.emitWarning(
+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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+
{
9+
const entry = fixtures.path(
10+
'/es-modules/package-type-module/extension.unknown'
11+
);
12+
const child = spawn(process.execPath, [entry]);
13+
let stdout = '';
14+
let stderr = '';
15+
child.stderr.setEncoding('utf8');
16+
child.stdout.setEncoding('utf8');
17+
child.stdout.on('data', (data) => {
18+
stdout += data;
19+
});
20+
child.stderr.on('data', (data) => {
21+
stderr += data;
22+
});
23+
child.on('close', common.mustCall((code, signal) => {
24+
assert.strictEqual(code, 1);
25+
assert.strictEqual(signal, null);
26+
assert.strictEqual(stdout, '');
27+
assert.ok(stderr.indexOf('ERR_UNKNOWN_FILE_EXTENSION') !== -1);
28+
}));
29+
}
30+
{
31+
const entry = fixtures.path(
32+
'/es-modules/package-type-module/imports-unknownext.mjs'
33+
);
34+
const child = spawn(process.execPath, [entry]);
35+
let stdout = '';
36+
let stderr = '';
37+
child.stderr.setEncoding('utf8');
38+
child.stdout.setEncoding('utf8');
39+
child.stdout.on('data', (data) => {
40+
stdout += data;
41+
});
42+
child.stderr.on('data', (data) => {
43+
stderr += data;
44+
});
45+
child.on('close', common.mustCall((code, signal) => {
46+
assert.strictEqual(code, 1);
47+
assert.strictEqual(signal, null);
48+
assert.strictEqual(stdout, '');
49+
assert.ok(stderr.indexOf('ERR_UNKNOWN_FILE_EXTENSION') !== -1);
50+
}));
51+
}
52+
{
53+
const entry = fixtures.path('/es-modules/package-type-module/noext-esm');
54+
const child = spawn(process.execPath, [entry]);
55+
let stdout = '';
56+
child.stdout.setEncoding('utf8');
57+
child.stdout.on('data', (data) => {
58+
stdout += data;
59+
});
60+
child.on('close', common.mustCall((code, signal) => {
61+
assert.strictEqual(code, 0);
62+
assert.strictEqual(signal, null);
63+
assert.strictEqual(stdout, 'executed\n');
64+
}));
65+
}
66+
{
67+
const entry = fixtures.path(
68+
'/es-modules/package-type-module/imports-noext.mjs'
69+
);
70+
const child = spawn(process.execPath, [entry]);
71+
let stdout = '';
72+
child.stdout.setEncoding('utf8');
73+
child.stdout.on('data', (data) => {
74+
stdout += data;
75+
});
76+
child.on('close', common.mustCall((code, signal) => {
77+
assert.strictEqual(code, 0);
78+
assert.strictEqual(signal, null);
79+
assert.strictEqual(stdout, 'executed\n');
80+
}));
81+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
throw new Error('NO, NEVER');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import './noext-esm';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import './extension.unknown';

0 commit comments

Comments
 (0)