Skip to content

Imported CJS function-constructor loses its own .name (decimal.js clone() shape): constructor.name is empty, Node says "Decimal" #10702

Description

@proggeramlug

Found while validating the removal of the native decimal.js binding (#10684) — with the binding
gone, import Decimal from "decimal.js" now compiles the real npm package from source (via #10699).
Every value checked matches Node 26.5.1 exactly (1/3, 10/4, the large multiplication that used to
abort the process, instanceof, sqrt, pow, toFixed, toPrecision, cmp, chained arithmetic) —
except constructor.name, which is empty instead of "Decimal".

Reproduction

{ "dependencies": { "decimal.js": "^10.6.0" }, "type": "module" }
import Decimal from "decimal.js";
const d = new Decimal(5);
console.log(d instanceof Decimal);       // true on both (fixed by #10477)
console.log(d.constructor.name);         // Node: "Decimal"   Perry: ""
console.log((Decimal as any).name);      // Node: "Decimal"   Perry: ""

Root cause (as far as I traced it)

decimal.js's real source has no class Decimal; it is an ES5-style function constructor built by a
clone() factory and exported CommonJS-style (module.exports = Decimal;, decimal.js:4935) — the
same shape #10477 already tracked for instanceof (now fixed).

Isolated repros:

  • A function constructor built by a same-file factory (function clone() { function Decimal(v) {...} return Decimal; }) — .name and .constructor.name are both correct.
  • The same shape with the prototype wholesale-replaced (Decimal.prototype = { constructor: Decimal, ... }, matching decimal.js exactly) in the same file — still correct.
  • The same shape moved to a separate module and exported via module.exports = Decimal (CJS),
    then imported with import Decimal from "./lib.js"Decimal.name itself is already empty
    before .constructor even enters the picture.

So the loss happens specifically in the CJS-import interop path for a plain (non-class) function
value: whatever rebinds the CJS export at the import site is not preserving the underlying function
object's own .name. instanceof (#10477) and ordinary calls/construction work fine through the same
path; only the .name property is lost.

Impact

Any npm package (CJS or UMD, decimal.js/bignumber.js among them) that exports an ES5-style function
constructor and whose callers inspect .name / .constructor.name (error reporting, assert-style
type tags, some validation/serialization libraries) will see an empty string where Node reports the
real name.

Not fixed by, and not caused by, #10684's binding removal — decimal.js's own arithmetic, instanceof,
and everything else checked in that PR's acceptance test match Node exactly.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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