How to configure npm package exports for CommonJS and ES modules?

I’m trying to set up my npm package to work with both CommonJS and ES modules. I saw this example in some docs:

{
  "name": "cool-package",
  "type": "module",
  "files": ["build"],
  "main": "./build/cool-package.umd.cjs",
  "module": "./build/cool-package.js",
  "exports": {
    ".": {
      "import": "./build/cool-package.js",
      "require": "./build/cool-package.umd.cjs"
    }
  }
}

Does this mean if someone uses import it’ll use the .js file, but if they use require() it’ll use the .cjs file? Also, do I really need both main and module fields if I’m using exports? It looks like they’re doing the same thing. Can anyone explain how this all works together?

hey, i’ve been there too. the exports thing is pretty neat. it lets u use diff files for import and require. main and module are kinda old school but keep em for old tools.

One thing tho - make sure ur actual code works in both systems. i once messed up cause i forgot bout some ES6 stuff that didnt play nice with CommonJS. test it good!

I’ve dealt with this exact issue in a recent project. You’re on the right track with your understanding. The exports field is indeed the modern way to handle dual module support.

The exports configuration you’ve shown will route import statements to the ES module version and require() calls to the CommonJS version. It’s a clean solution for supporting both module systems.

As for main and module, they’re mainly there for backwards compatibility with older tools. Modern package managers and bundlers will prioritize the exports field, so you could potentially omit them. However, keeping them doesn’t hurt and ensures maximum compatibility across different environments.

One tip from my experience: make sure your build process outputs both .js (ES module) and .cjs (CommonJS) files. Tools like Rollup or webpack can help with this. Also, thoroughly test your package in both Node.js and browser environments to catch any potential issues.

Your package.json setup looks solid for dual module support. The ‘exports’ field is key here, directing ES6 imports to the .js file and CommonJS requires to the .cjs file. It’s a clever way to support both module systems seamlessly.

While ‘main’ and ‘module’ fields aren’t strictly necessary with ‘exports’, they’re good to keep for older tools that might not recognize the exports field. It’s a small overhead for better compatibility.

One thing to watch out for: ensure your code is truly compatible with both module systems. Sometimes, subtle differences can cause issues. I’d recommend extensive testing in various environments to catch any potential problems early on.

Also, consider adding a ‘types’ field if you’re using TypeScript. It helps IDEs and users with type information, enhancing the developer experience for your package.