package.json — структура и настройка
Зачем нужно
package.json -- манифест Node.js-проекта. Он описывает имя, версию, зависимости, скрипты и точку входа. npm, yarn, pnpm и бандлеры (Webpack, Vite) читают этот файл для установки пакетов, запуска скриптов и сборки. Без package.json проект не может быть npm-пакетом и не может использовать npm install.
Где используется
- Каждый Node.js-проект (бэкенд, фронтенд, CLI, библиотека)
- npm-пакеты (публикация в registry)
- Monorepo (workspaces)
- CI/CD (npm ci, npm test, npm run build)
Предпосылки
- npm basics — npm init, npm install
- Что такое Node.js — модульная система (CJS vs ESM)
Создание
# Интерактивно
npm init
# Быстро с дефолтами
npm init -y
# С scope (для организации)
npm init --scope=@myorg -y
Основные поля
name и version (обязательные для публикации)
{
"name": "my-awesome-app",
"version": "1.0.0"
}
Правила для name:
- Строчные буквы, без пробелов
- Допустимы: дефисы, подчёркивания, точки
- Максимум 214 символов
- Уникальное в npm registry (для публикации)
- Scoped: "@myorg/my-package"
version: semver формат — major.minor.patch
см. [[Версионирование]]
description, keywords, author, license
{
"description": "A fast HTTP framework for Node.js",
"keywords": ["http", "framework", "express", "server"],
"author": "Anna Petrova <anna@example.com>",
"license": "MIT"
}
Популярные лицензии:
"MIT" — самая свободная, минимум ограничений
"Apache-2.0" — как MIT + патентная защита
"GPL-3.0" — копилефт (производные тоже GPL)
"ISC" — упрощённая MIT (default npm init)
"UNLICENSED" — проприетарный код
Точки входа
main (CommonJS)
{
"main": "dist/index.js"
}
// Когда кто-то делает:
const myPkg = require('my-package');
// Node.js ищет файл, указанный в "main"
module (ES Modules — для бандлеров)
{
"main": "dist/index.cjs",
"module": "dist/index.mjs"
}
"module" — неофициальное поле, но используется Webpack/Rollup/Vite
для импорта ESM-версии вместо CJS
exports (современный способ, Node.js 12+)
{
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
},
"./utils": {
"import": "./dist/utils.mjs",
"require": "./dist/utils.cjs"
}
}
}
// Пользователи могут импортировать:
import pkg from 'my-package'; // → dist/index.mjs
import utils from 'my-package/utils'; // → dist/utils.mjs
const pkg = require('my-package'); // → dist/index.cjs
// ❌ Прямой доступ к файлам заблокирован:
import x from 'my-package/dist/internal.js'; // Error!
// exports работает как allowlist
type — модульная система
{
"type": "module"
}
"type": "module" — все .js файлы = ES Modules (import/export)
"type": "commonjs" — все .js файлы = CommonJS (require) [default]
Переопределение через расширение:
.mjs — всегда ESM
.cjs — всегда CommonJS
scripts — команды проекта
{
"scripts": {
"start": "node dist/server.js",
"dev": "nodemon src/server.js",
"build": "tsc",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"lint": "eslint src/",
"lint:fix": "eslint src/ --fix",
"format": "prettier --write src/",
"prepare": "husky install",
"precommit": "npm run lint && npm test"
}
}
Запуск скриптов
# Специальные имена (без run):
npm start # → node dist/server.js
npm test # → jest
npm restart # → npm stop && npm start
# Все остальные через run:
npm run dev # → nodemon src/server.js
npm run build # → tsc
npm run lint # → eslint src/
npm run lint:fix # → eslint src/ --fix
# Передача аргументов
npm test -- --coverage
# jest --coverage
# Запуск нескольких скриптов
npm run lint && npm test # последовательно
Lifecycle-скрипты
{
"scripts": {
"prebuild": "rm -rf dist",
"build": "tsc",
"postbuild": "echo 'Build complete!'",
"pretest": "npm run lint",
"test": "jest",
"prepare": "husky install",
"prepublishOnly": "npm test && npm run build"
}
}
npm run build выполнит:
1. prebuild → rm -rf dist
2. build → tsc
3. postbuild → echo 'Build complete!'
Специальные lifecycle:
prepare — после npm install (для git hooks)
prepublishOnly — перед npm publish
preinstall — перед npm install
postinstall — после npm install
Зависимости
dependencies vs devDependencies vs peerDependencies
{
"dependencies": {
"express": "^4.18.2",
"mongoose": "^8.0.0",
"dotenv": "^16.3.1"
},
"devDependencies": {
"jest": "^29.7.0",
"eslint": "^8.55.0",
"typescript": "^5.3.3",
"nodemon": "^3.0.2",
"@types/node": "^22.0.0",
"@types/express": "^4.17.21"
},
"peerDependencies": {
"react": "^18.0.0"
}
}
dependencies:
- Нужны в production (runtime)
- Устанавливаются всегда
- Примеры: express, mongoose, lodash
devDependencies:
- Только при разработке
- НЕ устанавливаются с npm install --production
- Примеры: jest, eslint, typescript, @types/*
peerDependencies:
- "Мой пакет работает с этой версией хост-пакета"
- Используется плагинами: "я работаю с React 18"
- npm 7+: устанавливает автоматически
optionalDependencies:
- Не ломают install если не удалось поставить
- Пример: fsevents (только macOS)
Другие важные поля
engines — требования к версиям
{
"engines": {
"node": ">=18.0.0",
"npm": ">=9.0.0"
}
}
# Если версия Node.js не совпадает — предупреждение
# Для строгой проверки:
# echo "engine-strict=true" >> .npmrc
files — что включить в пакет
{
"files": [
"dist/",
"README.md",
"LICENSE"
]
}
"files" — whitelist файлов для npm publish
По умолчанию включается всё, кроме .gitignore
Всегда включаются: package.json, README, LICENSE, CHANGELOG
Всегда исключаются: node_modules/, .git/, .npmrc
bin — CLI-команды
{
"name": "my-cli",
"bin": {
"my-cli": "./bin/cli.js"
}
}
# После npm install -g my-cli:
my-cli --help
# Запускает ./bin/cli.js
// bin/cli.js
#!/usr/bin/env node // ← shebang обязателен!
console.log('Hello from my CLI!');
repository, homepage, bugs
{
"repository": {
"type": "git",
"url": "https://github.com/user/repo.git"
},
"homepage": "https://my-project.com",
"bugs": {
"url": "https://github.com/user/repo/issues"
}
}
Полный пример
{
"name": "@myorg/api-server",
"version": "2.1.0",
"description": "REST API server for my application",
"type": "module",
"main": "dist/index.js",
"exports": {
".": "./dist/index.js"
},
"bin": {
"api-server": "./bin/server.js"
},
"scripts": {
"start": "node dist/index.js",
"dev": "nodemon --exec tsx src/index.ts",
"build": "tsc",
"test": "jest",
"lint": "eslint src/",
"prepare": "husky install"
},
"dependencies": {
"express": "^4.18.2",
"cors": "^2.8.5",
"dotenv": "^16.3.1"
},
"devDependencies": {
"typescript": "^5.3.3",
"jest": "^29.7.0",
"eslint": "^8.55.0",
"nodemon": "^3.0.2",
"@types/node": "^22.0.0",
"@types/express": "^4.17.21"
},
"engines": {
"node": ">=20.0.0"
},
"files": [
"dist/",
"bin/"
],
"keywords": ["api", "express", "rest"],
"author": "Anna Petrova",
"license": "MIT"
}
Частые ошибки
- Путают dependencies и devDependencies —
typescriptв dependencies увеличивает production node_modules - Не указывают
"type": "module"— пишутimport/export, но Node.js ожидаетrequire - Забывают shebang в bin-файле —
#!/usr/bin/env nodeобязателен для CLI-инструментов - Не фиксируют engines — проект работает на Node 22, но у коллеги Node 16 и всё падает
- Включают лишнее в publish — без
"files"публикуются тесты, конфиги, src/ (увеличивает размер) - Ручное редактирование package-lock.json() — этот файл генерируется автоматически
Практика
- Создать package.json с
npm init, настроить scripts для start, dev, test, lint - Добавить
"type": "module"и переписать код на import/export - Настроить engines для минимальной версии Node.js
- Создать CLI-инструмент: поле
bin, файл с shebang,npm link - Использовать
exportsдля указания разных точек входа (CJS и ESM)
Связанные темы
- npm basics — установка и управление зависимостями
- Версионирование — semver, ^, ~, lock-файлы
- npx — запуск скриптов из package.json
- process — process.env и NODE_ENV
Ресурсы
🎓 Источник: Настройка среды Node.js, npm, git, eslint
- 📅 2018-10-01 · YouTube
- Тезисы:
npm init— интерактивное создание package.json,npm init -y— defaults- Секция
scripts— главное удобство npm; запуск черезnpm run X - devDependencies — не идут в production (
npm install --production) "main"— точка входа CJS; в новых пакетах используется"exports"для разделения CJS/ESM"engines": {"node": ">=18"}— npm предупредит при несовместимой версии
🎓 Источник: Node.js модули ECMA, Common.js, Module API
- 📅 2021-09-14 · YouTube
- Тезисы: поле
"type": "module"переключает все.jsфайлы пакета в ESM; conditional exports для dual-package