Файл package.json определяет способ подключения пакета,
доступные точки входа, типы модулей, совместимость с CommonJS и ESM, а
также экспорт TypeScript-типов. Для библиотек правильная настройка
main, module, exports и
types влияет на:
Ошибки в конфигурации приводят к дублированию бандлов, невозможности импорта, конфликтам ESM/CJS и проблемам типов.
mainПоле main определяет основной entry point пакета для
CommonJS.
{
"main": "./dist/index.js"
}
При использовании:
const lib = require('my-library');
Node.js ищет:
exportsmainЕсли exports отсутствует, используется
main.
Структура:
dist/
├─ index.js
└─ utils.js
Конфигурация:
{
"main": "./dist/index.js"
}
Импорт:
const lib = require('my-library');
Обычно main указывает на CJS-сборку:
{
"main": "./dist/index.cjs"
}
или:
{
"main": "./dist/index.js"
}
если проект использует CommonJS по умолчанию.
Старые версии:
используют main как основной источник пакета.
mainПоле не умеет:
Для современных библиотек одного main недостаточно.
moduleПоле module используется bundler’ами для указания
ESM-версии библиотеки.
{
"module": "./dist/index.esm.js"
}
CommonJS:
const lib = require('lib');
ESM:
import lib from 'lib';
Bundler предпочитает module, потому что ESM:
{
"main": "./dist/index.cjs",
"module": "./dist/index.esm.js"
}
moduleWebpack анализирует поля:
{
"main": "...",
"module": "..."
}
Приоритет обычно такой:
browsermodulemainЕсли существует ESM-версия, Webpack использует её.
ESM:
export function add() {}
export function sub() {}
Импорт:
import { add } from 'lib';
Webpack может удалить sub.
moduleПоле module не является официальным стандартом
Node.js.
Его поддерживают:
Но Node.js игнорирует module.
main и
moduleПроблемная конфигурация:
{
"main": "./dist/index.js",
"module": "./dist/index.js"
}
Если файл содержит CommonJS:
module.exports = {};
bundler ожидает ESM и возникают ошибки анализа.
typesПоле types указывает TypeScript declaration file.
{
"types": "./dist/index.d.ts"
}
TypeScript использует .d.ts для:
Файл:
// index.d.ts
export function sum(a: number, b: number): number;
Конфигурация:
{
"types": "./dist/index.d.ts"
}
Теперь IDE знает типы библиотеки.
typingsИсторически использовалось:
{
"typings": "./dist/index.d.ts"
}
Сегодня предпочтительно:
{
"types": "./dist/index.d.ts"
}
TypeScript:
{
"compilerOptions": {
"declaration": true
}
}
После сборки:
dist/
├─ index.js
└─ index.d.ts
Даже если библиотека написана на JavaScript, можно публиковать типы:
{
"main": "./dist/index.js",
"types": "./dist/index.d.ts"
}
exportsmain и module не решали:
Для этого появился exports.
exports{
"exports": "./dist/index.js"
}
Эквивалент:
{
"main": "./dist/index.js"
}
но с более строгим контролем.
{
"exports": {
".": "./dist/index.js"
}
}
Точка "." означает корневой импорт:
import lib from 'lib';
{
"exports": {
".": "./dist/index.js",
"./utils": "./dist/utils.js"
}
}
Теперь доступны:
import lib from 'lib';
import utils from 'lib/utils';
Без exports пользователи могут делать:
import x from 'lib/internal/private.js';
С exports это запрещено:
{
"exports": {
".": "./dist/index.js"
}
}
Теперь доступны только объявленные entry points.
Современная конфигурация:
{
"exports": {
".": {
"import": "./dist/index.esm.js",
"require": "./dist/index.cjs"
}
}
}
ESM:
import lib from 'lib';
использует:
./dist/index.esm.js
CommonJS:
const lib = require('lib');
использует:
./dist/index.cjs
typeКонфигурация:
{
"type": "module"
}
означает:
.js трактуется как ESM;.cjs — CommonJS.type: moduleПо умолчанию:
.js = CommonJS;.mjs = ESM.Часто используют:
{
"type": "module",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
}
}
{
"exports": {
".": {
"browser": "./dist/browser.js",
"node": "./dist/node.js",
"default": "./dist/browser.js"
}
}
}
Node.js версия:
import fs from 'fs';
Browser версия:
fetch('/api');
Bundler выбирает нужный файл автоматически.
defaultdefault используется как fallback.
{
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs",
"default": "./dist/index.mjs"
}
}
}
Современный вариант:
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
}
}
TypeScript получает типы напрямую из exports.
Это особенно важно при:
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./utils": {
"types": "./dist/utils.d.ts",
"import": "./dist/utils.js"
}
}
}
import { helper } from 'lib/utils';
TypeScript автоматически найдёт:
dist/utils.d.ts
Типичная структура:
dist/
├─ index.cjs
├─ index.js
├─ index.d.ts
└─ utils.js
Конфигурация:
{
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
}
}
Несмотря на exports, main часто сохраняют
для:
Многие библиотеки публикуются только как ESM.
Пример:
{
"type": "module",
"exports": {
".": "./dist/index.js"
}
}
CommonJS:
require('lib');
перестаёт работать.
Error [ERR_REQUIRE_ESM]
После включения exports файл package.json
становится недоступным.
Ошибка:
import pkg from 'lib/package.json';
{
"exports": {
".": "./dist/index.js",
"./package.json": "./package.json"
}
}
{
"exports": {
"./features/*": "./dist/features/*.js"
}
}
import x from 'lib/features/math';
Проблема:
{
"type": "module"
}
Файл:
module.exports = {};
Node.js интерпретирует файл как ESM.
{
"exports": {
".": {
"import": "./dist/index.js"
}
}
}
CommonJS:
require('lib');
ломается.
После добавления exports:
import x from 'lib/utils';
может перестать работать.
Нужно явно экспортировать subpath:
{
"exports": {
"./utils": "./dist/utils.js"
}
}
Webpack 5 обычно анализирует:
exportsbrowsermodulemainЛучший вариант для tree shaking:
{
"type": "module",
"exports": {
".": {
"import": "./dist/index.js"
}
}
}
Rollup и Vite предпочитают:
exports;module.CommonJS используется как fallback.
Современные режимы:
{
"compilerOptions": {
"moduleResolution": "NodeNext"
}
}
или:
{
"compilerOptions": {
"moduleResolution": "Bundler"
}
}
TypeScript начинает учитывать:
exports;{
"name": "my-library",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./utils": {
"types": "./dist/utils.d.ts",
"import": "./dist/utils.js",
"require": "./dist/utils.cjs"
},
"./package.json": "./package.json"
},
"sideEffects": false
}
{
"sideEffects": false
}
Сообщает bundler’у, что модули безопасно удалять при tree shaking.
| Поле | Назначение | Поддержка Node.js | Поддержка bundler |
|---|---|---|---|
main |
CommonJS entry | Да | Да |
module |
ESM для bundler | Нет | Да |
exports |
Современная система экспортов | Да | Да |
types |
TypeScript types | TypeScript | TypeScript |
{
"main": "./dist/index.js"
}
{
"main": "./dist/index.cjs",
"module": "./dist/index.js"
}
{
"type": "module",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
}
}
{
"type": "module",
"exports": {
".": "./dist/index.js"
}
}