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 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"
}

Частые ошибки

  1. Путают dependencies и devDependenciestypescript в dependencies увеличивает production node_modules
  2. Не указывают "type": "module" — пишут import/export, но Node.js ожидает require
  3. Забывают shebang в bin-файле#!/usr/bin/env node обязателен для CLI-инструментов
  4. Не фиксируют engines — проект работает на Node 22, но у коллеги Node 16 и всё падает
  5. Включают лишнее в publish — без "files" публикуются тесты, конфиги, src/ (увеличивает размер)
  6. Ручное редактирование package-lock.json() — этот файл генерируется автоматически

Практика

  1. Создать package.json с npm init, настроить scripts для start, dev, test, lint
  2. Добавить "type": "module" и переписать код на import/export
  3. Настроить engines для минимальной версии Node.js
  4. Создать CLI-инструмент: поле bin, файл с shebang, npm link
  5. Использовать exports для указания разных точек входа (CJS и ESM)

Связанные темы

Ресурсы


🎓 Источник: Настройка среды 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