Skip to content

Recommend node/default conditions instead of require/import as a solution to the dual package hazard #52174

Open
@nicolo-ribaudo

Description

@nicolo-ribaudo

Affected URL(s)

https://nodejs.org/api/packages.html#dual-package-hazard

Description of the problem

Publishing packages with dual CommonJS and ESM sources, while has the benefits of supporting both CJS consumers and ESM-only platforms, is known to cause problems because Node.js might load both versions. Example:

package.json foo.cjs foo.mjs
{
  "name": "foo",
  "exports": {
    "require": "./foo.cjs",
    "import": "./foo.mjs"
  }
}
exports.object = {};     
export const object = {};     
package.json bar.js
{
  "name": "bar",
  "main": "./bar.js"
}
const foo = require("foo");
exports.object = foo.object;     
// my app

import { object as fooObj } from "foo";
import { object as barObj } from "bar";

console.log(fooObj === barObj); // false?????

The two suggested solutions boil down to "even when you have an ESM entrypoint, still use only CJS internallly". This solves the dual package hazard, but completely defeats the cross-platform benefits of dual modules.

If foo instead used these export conditions:

{
  "name": "foo",
  "exports": {
    "node": "./foo.cjs",
    "default": "./foo.mjs"
  }
}

Then:

  • there would be no dual-package hazard in Node.js, because it only ever loads the CommonJS version
  • there would be no dual-package hazard in bundlers, because they would only ever load either the node version (if they are configured to target Node.js) or the default version (if they are configured to target other platforms).
  • the package solves the dual-package hazard while still providing an ESM-only version

We have been using this node/default pattern in @babel/runtime for a couple years, because we wanted to provide an ESM-only version for browsers while still avoiding the dual-package hazard (@babel/runtime is mostly stateless, but @babel/runtime/helpers/temporalUndefined relies on object identity of an object defined in a separate file).

Metadata

Metadata

Assignees

No one assigned

    Labels

    docIssues and PRs related to the documentations.good first issueIssues that are suitable for first-time contributors.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions