Skip to content

--disallow-code-generation-from-strings breaks named import of re-exported CommonJS export #61078

@Mysak0CZ

Description

@Mysak0CZ

Version

v25.2.1

Platform

Linux [...] 6.12.62-1-MANJARO #1 SMP PREEMPT_DYNAMIC Fri, 12 Dec 2025 19:00:00 +0000 x86_64 GNU/Linux

Subsystem

No response

What steps will reproduce the bug?

Minimal example with three files, with a.mjs being entrypoint:

a.mjs:

import { Hello } from "./b.cjs";

Hello();

b.cjs:

module.exports = require("./c.cjs");

c.cjs:

function Hello() {
    console.log("Hello, World!");
}

module.exports = { Hello };

How often does it reproduce? Is there a required condition?

With default flags works without issues, but if using --disallow-code-generation-from-strings the application breaks.

What is the expected behavior? Why is that the expected behavior?

The application should work as intended, as it doesn't use eval nor new Function:

$ node a.mjs
Hello, World!

What do you see instead?

The application fails to run:

$ node --disallow-code-generation-from-strings a.mjs
file://[...]/a.mjs:1
import { Hello } from "./b.cjs";
         ^^^^^
SyntaxError: Named export 'Hello' not found. The requested module './b.cjs' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from './b.cjs';
const { Hello } = pkg;

    at #asyncInstantiate (node:internal/modules/esm/module_job:302:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:405:5)
    at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:654:26)
    at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:101:5)

Node.js v25.2.1

Additional information

Note, that the above example is a simplification of real-world usecase, where a.mjs comes from an ESM application that imports b.cjs CommonJS library, which uses re-exports internally.
Most notable case I stumbled upon of this happening is while using express.

There is a workaround for the application:
a_workaround.mjs:

import b from "./b.cjs";
const { Hello } = b;

Hello();

But this workaround is not always applicable (e.g. if using an ESM dependency outside our control that imports such CommonJS dependency itself) and from the meaning --disallow-code-generation-from-strings it is not clear why it should be needed. Even if the workaround is possible, the bug hampers usage of this hardening flag, even if the application nor the libraries make use of eval-like features.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions