Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/internal/modules/cjs/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -1751,6 +1751,7 @@ function wrapSafe(filename, content, cjsModuleInstance, format) {
* @returns {any}
*/
Module.prototype._compile = function(content, filename, format) {
const explicitCommonJS = format === 'commonjs' || format === 'commonjs-typescript';
if (format === 'commonjs-typescript' || format === 'module-typescript' || format === 'typescript') {
content = stripTypeScriptModuleTypes(content, filename);
switch (format) {
Expand All @@ -1777,6 +1778,11 @@ Module.prototype._compile = function(content, filename, format) {
const result = wrapSafe(filename, content, this, format);
compiledWrapper = result.function;
if (result.canParseAsESM) {
// Throw for ESM syntax in explicitly CommonJS main entry to avoid silent failure.
if (explicitCommonJS && this[kIsMainSymbol]) {
const pkg = packageJsonReader.getPackageScopeConfig(filename);
throw new ERR_REQUIRE_ESM(filename, true, null, pkg?.pjsonPath);
}
format = 'module';
}
}
Expand Down
38 changes: 38 additions & 0 deletions test/es-module/test-esm-syntax-in-cjs-main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Flags: --no-warnings
'use strict';

// Test that running a main entry point with ESM syntax in a "type": "commonjs"
// package throws an error instead of silently failing with exit code 0.
// Regression test for https://github.com/nodejs/node/issues/61104

const { spawnPromisified } = require('../common');
const fixtures = require('../common/fixtures.js');
const assert = require('node:assert');
const { execPath } = require('node:process');
const { describe, it } = require('node:test');

describe('ESM syntax in explicit CommonJS main entry point', { concurrency: !process.env.TEST_PARALLEL }, () => {
it('should throw ERR_REQUIRE_ESM when main module has ESM syntax in type:commonjs package', async () => {
const mainScript = fixtures.path('es-modules/package-type-commonjs-esm-syntax/esm-script.js');
const { code, signal, stderr } = await spawnPromisified(execPath, [mainScript]);

// Should exit with non-zero exit code
assert.strictEqual(code, 1, `Expected exit code 1, got ${code}`);
assert.strictEqual(signal, null);

// Should contain error about ESM in CommonJS context
assert.match(stderr, /ERR_REQUIRE_ESM/,
'Expected error message to contain ERR_REQUIRE_ESM');
assert.match(stderr, /esm-script\.js/,
'Expected error message to mention the script filename');
});

it('should include helpful message about package.json type field', async () => {
const mainScript = fixtures.path('es-modules/package-type-commonjs-esm-syntax/esm-script.js');
const { stderr } = await spawnPromisified(execPath, [mainScript]);

// Error message should mention the package.json path for helpful debugging
assert.match(stderr, /package\.json/,
'Expected error message to mention package.json');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// This file has ESM syntax but is in a "type": "commonjs" package
console.log('script STARTED');
import { version } from 'node:process';
console.log(version);
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "commonjs"
}