If you just added "type": "module" to package.json and your test runner died with ERR_REQUIRE_ESM, this article gets you back to green in about 15 minutes. By the end you'll have a copy-pasteable decision map for .js / .cjs / .mjs, a working dual-format build, and the three config files (Jest, ESLint, a small CLI) that break most often — with the exact error strings so you can Ctrl+F your stack trace.

I migrated a 42-file internal tooling repo last month. The flag flip took 4 seconds; the fallout took 3 hours. Here's the part nobody writes down.

The one rule that explains every ERR_REQUIRE_ESM and ERR_MODULE_NOT_FOUND

Result first: in Node 20–24, the "type" field in the nearest package.json decides how .js files are parsed. "type": "module" → every .js is ESM. No field, or "type": "commonjs" → every .js is CJS. The extensions .cjs and .mjs ignore the field entirely and are always CJS and ESM respectively.

So 90% of migration pain is one of two mismatches: