From 19e940d17c578ed30ea988bf6f8d2941cdd30214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heath=20Dutton=F0=9F=95=B4=EF=B8=8F?= Date: Wed, 24 Dec 2025 23:09:20 -0500 Subject: [PATCH] module: fix silent failure 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 fix ensures that when the file is in a package with explicit "type": "commonjs", shouldDetectModule is set to false in wrapSafe, so the native compiler throws the proper SyntaxError with source line information rather than detecting it as a module and deferring to ESM loading (which caused the silent failure). Fixes: https://github.com/nodejs/node/issues/61104 --- lib/internal/modules/cjs/loader.js | 21 ++++++++++++------- test/es-module/test-esm-syntax-in-cjs-main.js | 19 +++++++++++++++++ .../esm-script.js | 4 ++++ .../package.json | 3 +++ 4 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 test/es-module/test-esm-syntax-in-cjs-main.js create mode 100644 test/fixtures/es-modules/package-type-commonjs-esm-syntax/esm-script.js create mode 100644 test/fixtures/es-modules/package-type-commonjs-esm-syntax/package.json diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index d9fc92bbc813e8..e319ff99f1fa3e 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -1722,13 +1722,20 @@ function wrapSafe(filename, content, cjsModuleInstance, format) { let shouldDetectModule = false; if (format !== 'commonjs') { - if (cjsModuleInstance?.[kIsMainSymbol]) { - // For entry points, format detection is used unless explicitly disabled. - shouldDetectModule = getOptionValue('--experimental-detect-module'); - } else { - // For modules being loaded by `require()`, if require(esm) is disabled, - // don't try to reparse to detect format and just throw for ESM syntax. - shouldDetectModule = getOptionValue('--require-module'); + // Also check if we're in an explicit commonjs package, even if format wasn't + // set (e.g., for extensionless files). In explicit commonjs packages, we should + // not detect module syntax and instead let the syntax error propagate. + const pkg = packageJsonReader.getNearestParentPackageJSON(filename); + const explicitCommonJS = pkg?.data.type === 'commonjs'; + if (!explicitCommonJS) { + if (cjsModuleInstance?.[kIsMainSymbol]) { + // For entry points, format detection is used unless explicitly disabled. + shouldDetectModule = getOptionValue('--experimental-detect-module'); + } else { + // For modules being loaded by `require()`, if require(esm) is disabled, + // don't try to reparse to detect format and just throw for ESM syntax. + shouldDetectModule = getOptionValue('--require-module'); + } } } const result = compileFunctionForCJSLoader(content, filename, false /* is_sea_main */, shouldDetectModule); diff --git a/test/es-module/test-esm-syntax-in-cjs-main.js b/test/es-module/test-esm-syntax-in-cjs-main.js new file mode 100644 index 00000000000000..2c3987308a597b --- /dev/null +++ b/test/es-module/test-esm-syntax-in-cjs-main.js @@ -0,0 +1,19 @@ +// 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 + +require('../common'); +const { spawnSyncAndAssert } = require('../common/child_process'); +const fixtures = require('../common/fixtures.js'); +const { execPath } = require('node:process'); + +const mainScript = fixtures.path('es-modules/package-type-commonjs-esm-syntax/esm-script.js'); + +spawnSyncAndAssert(execPath, [mainScript], { + status: 1, + signal: null, + stderr: /import { version } from 'node:process'/, +}); diff --git a/test/fixtures/es-modules/package-type-commonjs-esm-syntax/esm-script.js b/test/fixtures/es-modules/package-type-commonjs-esm-syntax/esm-script.js new file mode 100644 index 00000000000000..4cabb2a94dbeae --- /dev/null +++ b/test/fixtures/es-modules/package-type-commonjs-esm-syntax/esm-script.js @@ -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); diff --git a/test/fixtures/es-modules/package-type-commonjs-esm-syntax/package.json b/test/fixtures/es-modules/package-type-commonjs-esm-syntax/package.json new file mode 100644 index 00000000000000..5bbefffbabee39 --- /dev/null +++ b/test/fixtures/es-modules/package-type-commonjs-esm-syntax/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +}