Публикация ESM-пакета для потребителей CommonJS требует настройки экспорта модулей и выбора формата бандла. Основные шаги включают определение точки входа, настройку условий сборки для разных форматов (ESM/CJS) и проверку совместимости зависимостей.
Настройка package.json
В файле package.json необходимо явно указать поле "type": "module". Это сообщает инструментам сборки, что исходный код пакета использует синтаксис ESM (import/export). Без этого флага инструменты могут пытаться обрабатывать код как CommonJS или ES5, игнорируя ваши намерения.
Если ваш пакет должен поддерживать и ESM-потребителей, и CommonJS-потребителей, в поле "exports" нужно прописать условия. Это позволяет сборщику выбирать правильный формат на лету: для импортирующего модуля CommonJS будет сгенерирован код с require(), а для ESM — с import.
Пример структуры поля exports для совместимости:
Использование условных экспортов
Поле "exports" в package.json позволяет задать разные пути к файлам в зависимости от типа импортирующего проекта. Синтаксис условий работает следующим образом: если импортирующий проект имеет "type": "module", используется путь с .mjs или .js (в зависимости от настроек сборщика), а если нет — путь с .cjs.
Для реализации совместимости часто используют расширение .cjs для файлов, предназначенных для CommonJS. Это гарантирует, что даже внутри ESM-пакета файлы, которые должны быть потреблены CommonJS, будут корректно обработаны.
Пример конфигурации exports:
Настройка сборщика (Webpack/Vite/Rollup)
Инструменты сборки должны быть настроены на генерацию двух разных бандлов или одного с правильными условиями. В Webpack это делается через опцию output.format: 'esm' для основного входа и использование плагинов для условной загрузки, либо настройка entry points.
В Vite используется поле "main" и "module" в package.json. Поле "main" указывает на точку входа для CommonJS (обычно index.cjs), а "module" — для ESM (index.js). Сборщик автоматически выбирает нужный файл при импорте.
При использовании Rollup необходимо явно указать формат вывода через опцию output.format: ['cjs', 'esm'] и настроить условия экспорта через объект conditions.
Проверка зависимостей
Все зависимости вашего пакета должны быть совместимы с выбранными форматами. Если вы используете библиотеки, которые сами поддерживают только ESM, ваш пакет не сможет работать в среде CommonJS без дополнительных конвертеров.
Проверяйте версии зависимостей на наличие флагов "type": "module" в их package.json. Если зависимость требует ESM, а ваш потребитель CommonJS, потребуется использовать polyfills или альтернативные версии библиотек.
Тестирование совместимости
Перед публикацией протестируйте импорт вашего пакета в двух разных проектах: одном с "type": "module" и другом без этого флага. Убедитесь, что в обоих случаях код выполняется без ошибок.
Используйте инструменты вроде esm.sh или аналогичные онлайн-песочницы для проверки корректности экспорта модулей перед загрузкой на репозиторий.
Вопросы и ответы
Можно ли использовать только один файл index.js для ESM и CommonJS?
Нет, если потребитель CommonJS не настроен на поддержку ESM. Рекомендуется разделять файлы: index.js для ESM и index.cjs для CommonJS, указывая их в полях "module" и "main" соответственно.
Что делать, если зависимость требует строго ESM?
Используйте альтернативную версию зависимости, которая поддерживает CommonJS, или конвертируйте код через инструменты вроде esm-shim. Публикуйте пакет с явным указанием формата в exports.
По теме
{
"name": "my-esm-package",
"type": "module",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist-cjs/index.d.cts",
"default": "./dist-cjs/index.cjs"
}
}
},
"main": "./dist-cjs/index.cjs",
"module": "./dist/index.js"
}