intermediate

ESM vs CommonJS

Сравнивайте static ESM bindings с CommonJS require, package type, interop edges, loading timing и migration paths.

CommonJS (`require`/`module.exports`) грузится синхронно с динамическим разрешением; ESM (`import`/`export`) статически анализируется, асинхронен на верхнем уровне и стандарт для нового кода Node.

| Аспект | CommonJS | ESM | |--------|----------|-----| | Загрузка | Синхронный `require` | Асинхронный граф модулей | | Экспорт | Мутабельный `module.exports` | Live bindings | | Top-level await | Нет | Да | | `__dirname` | Встроен | Через `import.meta.url` |

`"type": "module"` в `package.json` делает `.js` ESM; иначе `.js` — CommonJS, `.mjs` — ESM. Interop: `import cjs from 'pkg'` часто оборачивает default; `createRequire` — CJS из ESM.

					import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const legacy = require('./legacy.cjs');
				

На интервью: live bindings; боль dual packages; миграция (`type`, `.cjs`, `.mjs`, tooling).

Типовые ошибки: `require` в ESM без createRequire; ожидание hoisting как у bundler; различия циклических зависимостей.

Компромисс — между простотой, производительностью, безопасностью и эксплуатацией: назовите, что оптимизировали и какую цену приняли.

Чеклист:

  • Явно задайте `type` в пакете.
  • Знайте мосты interop.
  • Согласуйте test runner и module в tsconfig.
  • Не публикуйте CJS+ESM без exports map.