babel/babel

Node.js require(esm) and __esModule

nicolo-ribaudo opened this issue · 0 comments

This issue is meant to be a place for tools maintainers to discuss about interop with Node.js

Node.js has experimental support for require(esm), and on its result does not set __esModule: true. How does this affect transpilers?

Consider this example:

// main.cjs

// This file was authored as ESM and compiled to CJS
//
// import x from "dep";
// console.log(x);

function _interopRequireDefault(mod) { return mod?.__esModule ? mod.default : mod }
const _dep = _interopRequireDefault(require("dep"));

console.log(_dep.default);
// dep

// This file was authored as ESM and compiled to CJS
// export default 2;
// export const named = 3;

exports.__esModule = true;
exports.default = 2;
exports.named = 3;

It currently logs 2. If dep starts publishing as native ESM, the "_interopRequireDefault" logic will not detect it anymore as "was ESM" and thus it will log { default: 2, named: 3 }. The author of main.cjs will have to rewrite their code:

// main.cjs

// This file was authored as ESM and compiled to CJS
//
// import x from "dep";
// console.log(x.default);

function _interopRequireDefault(mod) { return mod?.__esModule ? mod.default : mod }
const _dep = _interopRequireDefault(require("dep"));

console.log(_dep.default.default);

There are two ways to make the "stop compiling to CJS" change non-breaking for its consumers (assuming that they are on a Node.js version where require(esm) works):

  1. either Node.js adds __esModule: true on the result of require(esm)
  2. or all tools update their "was ESM" detection to return mod?.__esModule || mod?.[Symbol.toStringTag] === "Module" ? mod.default : mod

I originally pushed for (1), which is implemented in an open PR in the Node.js repo (52166), but as you can see in that PR it comes with problems. Maybe we could do (2) instead?

I'm opening this issue in the Babel repo as a place to discuss outside of the Node.js repo, so that we (tools) can give a unified opinion on whether Node.js should or shouldn't do (1).