Skip to content

Commit 674c150

Browse files
committed
module: throw error for ESM syntax in explicit commonjs entry
When a main entry point contains ESM syntax but is in a package with "type": "commonjs" in package.json, the module would silently exit with code 0 without executing or showing any error. This happened because the CJS loader detected ESM syntax, attempted to defer to ESM loading, but the async execution never completed before the process exited. This change throws ERR_REQUIRE_ESM for main modules with ESM syntax in explicitly CommonJS-typed packages, providing a clear error message instead of silent failure. Fixes: #61104
1 parent 13073a9 commit 674c150

File tree

4 files changed

+51
-0
lines changed

4 files changed

+51
-0
lines changed

lib/internal/modules/cjs/loader.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1751,6 +1751,7 @@ function wrapSafe(filename, content, cjsModuleInstance, format) {
17511751
* @returns {any}
17521752
*/
17531753
Module.prototype._compile = function(content, filename, format) {
1754+
const explicitCommonJS = format === 'commonjs' || format === 'commonjs-typescript';
17541755
if (format === 'commonjs-typescript' || format === 'module-typescript' || format === 'typescript') {
17551756
content = stripTypeScriptModuleTypes(content, filename);
17561757
switch (format) {
@@ -1777,6 +1778,11 @@ Module.prototype._compile = function(content, filename, format) {
17771778
const result = wrapSafe(filename, content, this, format);
17781779
compiledWrapper = result.function;
17791780
if (result.canParseAsESM) {
1781+
// Throw for ESM syntax in explicitly CommonJS main entry to avoid silent failure.
1782+
if (explicitCommonJS && this[kIsMainSymbol]) {
1783+
const pkg = packageJsonReader.getPackageScopeConfig(filename);
1784+
throw new ERR_REQUIRE_ESM(filename, true, null, pkg?.pjsonPath);
1785+
}
17801786
format = 'module';
17811787
}
17821788
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Flags: --no-warnings
2+
'use strict';
3+
4+
// Test that running a main entry point with ESM syntax in a "type": "commonjs"
5+
// package throws an error instead of silently failing with exit code 0.
6+
// Regression test for https://github.com/nodejs/node/issues/61104
7+
8+
const { spawnPromisified } = require('../common');
9+
const fixtures = require('../common/fixtures.js');
10+
const assert = require('node:assert');
11+
const { execPath } = require('node:process');
12+
const { describe, it } = require('node:test');
13+
14+
describe('ESM syntax in explicit CommonJS main entry point', { concurrency: !process.env.TEST_PARALLEL }, () => {
15+
it('should throw ERR_REQUIRE_ESM when main module has ESM syntax in type:commonjs package', async () => {
16+
const mainScript = fixtures.path('es-modules/package-type-commonjs-esm-syntax/esm-script.js');
17+
const { code, signal, stderr } = await spawnPromisified(execPath, [mainScript]);
18+
19+
// Should exit with non-zero exit code
20+
assert.strictEqual(code, 1, `Expected exit code 1, got ${code}`);
21+
assert.strictEqual(signal, null);
22+
23+
// Should contain error about ESM in CommonJS context
24+
assert.match(stderr, /ERR_REQUIRE_ESM/,
25+
'Expected error message to contain ERR_REQUIRE_ESM');
26+
assert.match(stderr, /esm-script\.js/,
27+
'Expected error message to mention the script filename');
28+
});
29+
30+
it('should include helpful message about package.json type field', async () => {
31+
const mainScript = fixtures.path('es-modules/package-type-commonjs-esm-syntax/esm-script.js');
32+
const { stderr } = await spawnPromisified(execPath, [mainScript]);
33+
34+
// Error message should mention the package.json path for helpful debugging
35+
assert.match(stderr, /package\.json/,
36+
'Expected error message to mention package.json');
37+
});
38+
});
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// This file has ESM syntax but is in a "type": "commonjs" package
2+
console.log('script STARTED');
3+
import { version } from 'node:process';
4+
console.log(version);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"type": "commonjs"
3+
}

0 commit comments

Comments
 (0)