Как создать и опубликовать npm-пакет
Создание и публикация npm-пакета с использованием ESM, TypeScript и npm Trusted Publishing на основе GitHub Actions OIDC для автоматических релизов без токенов.
Вы установили сотни пакетов с помощью npm install. Теперь вы хотите опубликовать свой собственный. Процесс кажется простым, пока вы не обнаружите устаревшие руководства, рекомендующие Node 12, настройки только для CommonJS и долгоживущие токены, хранящиеся в секретах CI.
Это руководство проведет вас через создание и публикацию npm-пакета с использованием современных безопасных практик, которые будут актуальны и в 2025 году. Мы создадим простую утилиту, правильно настроим её и опубликуем с помощью npm Trusted Publishing.
Ключевые выводы
- Используйте ESM с правильным полем
exportsвместо устаревшего поляmainдля современного разрешения пакетов - Компилируйте TypeScript в JavaScript с файлами деклараций вместо поставки исходного TypeScript
- Включите двухфакторную аутентификацию и используйте npm Trusted Publishing через GitHub Actions OIDC для безопасных автоматизированных релизов
- Всегда проверяйте содержимое пакета с помощью
npm pack --dry-runперед публикацией
Предварительные требования
Перед началом убедитесь, что у вас есть:
- Установленный Node.js версии 18 или выше
- Аккаунт на GitHub
- Базовое знакомство с npm как пользователя
Инициализация настройки npm-пакета с приоритетом ESM
Создайте новую директорию и инициализируйте проект:
mkdir use-toggle && cd use-toggle
npm init -y
git init
Мы создадим простой React-хук под названием useToggle. Этот пример демонстрирует публикацию TypeScript npm-пакета с правильными точками входа.
Настройка package.json для современной публикации
Замените ваш package.json на правильно настроенную версию:
{
"name": "@yourusername/use-toggle",
"version": "0.1.0",
"description": "A simple React hook for boolean toggle state",
"keywords": ["react", "hook", "toggle", "state"],
"license": "MIT",
"author": "Your Name",
"repository": {
"type": "git",
"url": "git+https://github.com/yourusername/use-toggle.git"
},
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
},
"files": ["dist"],
"engines": {
"node": ">=18"
},
"peerDependencies": {
"react": ">=18"
},
"devDependencies": {
"typescript": "^5.4.0",
"react": "^18.0.0",
"@types/react": "^18.0.0"
},
"scripts": {
"build": "tsc",
"prepublishOnly": "npm run build"
}
}
Ключевые моменты для настройки npm-пакета с приоритетом ESM:
type: "module"объявляет ESM по умолчаниюexportsзаменяет устаревшее полеmain— именно так современный Node разрешает точки входаfilesконтролирует, что будет опубликовано (толькоdist/)enginesуказывает Node 18+, избегая проблем совместимости с устаревшими версиями
Настройка компиляции TypeScript
Создайте tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"declaration": true,
"outDir": "dist",
"rootDir": "src",
"strict": true,
"skipLibCheck": true
},
"include": ["src"]
}
Создайте src/index.ts:
import { useState, useCallback } from 'react';
export function useToggle(initial = false): [boolean, () => void] {
const [value, setValue] = useState(initial);
const toggle = useCallback(() => setValue(v => !v), []);
return [value, toggle];
}
Запустите npm run build для компиляции. Вы получите dist/index.js и dist/index.d.ts — JavaScript плюс декларации типов, а не исходный TypeScript.
Безопасная публикация npm-пакета в 2025 году
Создайте аккаунт npm с двухфакторной аутентификацией
- Зарегистрируйтесь на npmjs.com
- Немедленно включите двухфакторную аутентификацию (предпочтительно WebAuthn/passkeys)
- Для всех новых пакетов теперь по умолчанию включено требование 2FA
- С новой моделью входа на основе сессий публикация любого пакета — нового или существующего — требует 2FA во время сессии
Ваша первая ручная публикация
npm login теперь создает токен сессии на два часа. Эти токены:
- Не отображаются в интерфейсе npm
- Автоматически истекают через два часа
- Не могут быть повторно использованы в CI/CD
- Всегда требуют 2FA перед публикацией
Старые инструменты, которые аутентифицируются через устаревшую конечную точку CouchDB, также получают токены сессии на два часа в переходный период.
Для вашего первого релиза:
npm login
npm publish --access public
Флаг --access public обязателен для пакетов с областью видимости (scoped packages) на бесплатных аккаунтах.
Проверьте ваш пакет по адресу https://www.npmjs.com/package/@yourusername/use-toggle.
Discover how at OpenReplay.com.
npm Trusted Publishing с CI
Старые руководства советуют создать NPM_TOKEN и сохранить его как секрет репозитория. Этот подход теперь устарел: классические токены были окончательно отозваны, а долгоживущие токены для CI больше не поддерживаются.
Современная альтернатива — npm Trusted Publishing через GitHub Actions OIDC. GitHub подтверждает свою идентичность npm, и npm автоматически выдает краткосрочные учетные данные — без необходимости хранить токен.
Настройка Trusted Publishing
- На npmjs.com перейдите к вашему пакету → Settings → Publishing access
- Добавьте нового “Trusted Publisher” с данными вашего GitHub-репозитория
Создание workflow
Добавьте .github/workflows/publish.yml:
name: Publish
on:
release:
types: [published]
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm publish --provenance --access public
Флаг --provenance добавляет криптографическое подтверждение, связывающее ваш пакет с его исходным репозиторием — это функция безопасности цепочки поставок, которую npm представил в 2023 году.
Если OIDC не может быть использован (например, для самостоятельно размещенных раннеров), создайте краткосрочный гранулярный токен доступа с помощью:
npm token create
Гранулярные токены могут включить опцию Bypass 2FA для публикации в CI и должны истекать в течение 90 дней.
Что вы увидите в старых руководствах
Избегайте этих устаревших паттернов:
"main": "index.js"безexports— используйтеexportsдля правильного разрешения ESM- Таргетинг на Node 10/12/14 — Node 18+ поддерживается, но новые пакеты должны ориентироваться на современные LTS-версии (Node 20 или 22)
- Пакеты только для CommonJS — ESM является стандартом; поставляйте пакеты с приоритетом ESM
- Постоянный
NPM_TOKENв каждом workflow — классические токены полностью отозваны; используйте Trusted Publishing без токенов
Проверка перед публикацией
Всегда тестируйте пакет локально перед публикацией:
npm pack --dry-run
Это покажет точно, какие файлы будут включены. Если вы видите src/ или node_modules/, ваше поле files требует корректировки.
Заключение
Чтобы создать и опубликовать npm-пакет в 2025 году: используйте ESM с правильным exports, компилируйте TypeScript в JavaScript с декларациями, включите 2FA и автоматизируйте релизы с помощью npm Trusted Publishing. Откажитесь от устаревших паттернов — они создадут проблемы для вас и ваших пользователей.
Часто задаваемые вопросы
Должен ли я публиковать исходные файлы TypeScript или скомпилированный JavaScript?
Публикуйте скомпилированный JavaScript с файлами деклараций. Поставляйте папку dist, содержащую скомпилированный JS и сгенерированные .d.ts файлы. Это обеспечивает совместимость с различными версиями TypeScript и инструментами сборки. Исходный TypeScript заставляет потребителей компилировать ваш код, что может вызвать конфликты версий и замедлить сборку.
В чем разница между dependencies и peerDependencies для npm-пакетов?
Используйте dependencies для пакетов, которые ваш код объединяет и поставляет. Используйте peerDependencies для пакетов, которые должны предоставить потребители, например React для React-хука. Это предотвращает дублирование копий больших библиотек в финальном бандле. Ваш пакет ожидает версию потребителя, а не включает свою собственную.
Почему мне нужен флаг --access public при публикации пакетов с областью видимости?
Пакеты с областью видимости, такие как @username/package-name, по умолчанию являются приватными на npm. Бесплатные аккаунты npm не могут публиковать приватные пакеты, поэтому вы должны явно указать --access public. Этот флаг сообщает npm опубликовать пакет публично. Он нужен только для первой публикации; последующие версии наследуют уровень доступа.
Как мне отменить публикацию или пометить как устаревший пакет, который я больше не поддерживаю?
Используйте npm deprecate @scope/package, чтобы предупредить пользователей без удаления пакета. Полная отмена публикации с помощью npm unpublish ограничена пакетами, опубликованными в течение 72 часов, или теми, у которых минимальное количество загрузок. Для заброшенных пакетов предпочтительнее deprecation, так как это предупреждает пользователей, сохраняя существующие установки.