Skip to content

Commit 6ed6c3f

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 a SyntaxError for main modules with ESM syntax in explicitly CommonJS-typed packages, matching the error Node.js throws when import syntax appears in other CommonJS contexts. Fixes: #61104
1 parent 13073a9 commit 6ed6c3f

File tree

4 files changed

+43
-0
lines changed

4 files changed

+43
-0
lines changed

lib/internal/modules/cjs/loader.js

Lines changed: 5 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,10 @@ 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+
throw new SyntaxError('Cannot use import statement outside a module');
1784+
}
17801785
format = 'module';
17811786
}
17821787
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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 SyntaxError 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 SyntaxError about import statement
24+
assert.match(stderr, /SyntaxError/,
25+
'Expected error to be a SyntaxError');
26+
assert.match(stderr, /Cannot use import statement outside a module/,
27+
'Expected error message about import statement');
28+
assert.match(stderr, /esm-script\.js/,
29+
'Expected error message to mention the script filename');
30+
});
31+
});
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)