Процесс подготовки npm‑пакета

Я часто делаю npm-пакеты. Во-первых, многие куски проектов Breadhead становятся общедоступным решением. Во-вторых, у меня есть небольшой проект Solid Soda, это набор библиотек, помогающих писать более простые и надежные Node.js приложения.

Это не моя основная работа, потому хочется тратить минимум времени и сил. После публикации пакет нужно поддерживать, хочется минимизировать и эти затраты.

Итак, мои требования к процессу разработки и публикации npm-пакетов:

  • инициализация проекта должна занимать минимум времени;
  • весь код должен быть единообразным, чтобы максимально просто включаться в контекст;
  • код должен анализироваться автоматизировано на предмет ошибок;
  • любые проверки должны выполняться постоянно (на каждый коммит, на каждый пулл-реквест);
  • публикация новой версии должна происходить с минимальным участием человека.

Инициализация

Почти все мои библиотеки написаны на TypeScript, и я использую для них плюс-минус один и тот же tsconfig-файл.

{
	"compilerOptions": {
    	"lib": ["es2017"],
        "module": "commonjs",
        "declaration": true,
        "noImplicitAny": false,
        "removeComments": true,
        "noLib": false,
        "allowSyntheticDefaultImports": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "target": "es5",
        "sourceMap": true,
        "outDir": "./dist",
        "baseUrl": "./lib"
	},
    "include": ["lib/**/*"],
    "exclude": ["node_modules", "**/*.spec.ts"]
}

Однажды это привело к неприятным последствиям. Я воспользовался этой конфигурацией в библиотеке для фронтенда. Ничего не работало, потому что по умолчанию я использовал цель компиляции es6 (не многие браузеры могут выполнить его), теперь там es5.

В каждом проекте первой командой всегда ставлю дев-зависимости. Дальше станет понятно зачем каждая из них.

yarn add --dev @solid-soda/scripts jest ts-jest typescript
yarn soda init

Автоматизация

Все что может быть автоматизировано, должно быть автоматизировано. Все проверки, линтеры и форматтеры спрятаны в пакете @solid-soda/scripts. Это удобно, все изменения и новые возможности проверить код раскатываются из одного места. Рассмотрим что внутри.

Prettier — друг разработчика. Он разгружает голову и помогает не думать о форматировании кода. Просто пишешь как-нибудь, а он разбирается.

yarn s pretty

Статический анализ

TypeScript уже анализирует типы на предмет глупых ошибок, но хочется больше уверенности. Потому я использую ESLint. Для линтинга CSS используется Stylelint.

yarn s lint

Тесты

Тестировать код здорово. Не всегда есть время на полное покрытие, но стараюсь тестировать критические места. Использую Jest — jest.config.js

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
}

Линтинг коммитов

Я не хочу писать CHANGELOG, потому использую Convential Commits, из которого автоматически генерирую историю изменений и изменяю версии пакетов (он позволяет вести семантическое версионирование не думая о нем).

yarn s cz

Это команда позволяет просто генерировать сообщения для коммитов.

Автоматический запуск всех проверок

Я забываю и ленюсь запускать ESLint, Stylelintи Prettier перед коммитом, потому @solid-soda/scripts встраивает в проект гит-хуки. На pre-commit висит утилита lint-staged. Она запускает скрипты только измененном коде (много быстрее, чем на всем).

Peer Dependencies

Если у создаваемого пакеты есть peer dependencies (я не смог это перевести), всегда ставлю @team-griffin/install-self-peers. В таком случае несколько меняется скрипт подготовки пакета к публикации.

"scripts": {
  "prepare": "install-self-peers -- --ignore-scripts && yarn build"
 }

Скрипты

"scripts": {
  "build": "rimraf dist && tsc",
  "prepare": "yarn build",
  "ci": "yarn types && yarn test && yarn s lint",
  "test": "jest", 
  "types": "tsc --noEmit lib",
  "s": "yarn soda"
}

TravisCI

Остается две задачи: запускать проверки на пулл-реквестах и публиковать новые версии в npm. Для этого использую TravisCI. Он простой и бесплатный.

Сначала нужно включить репозиторий на сайте, после — создать базовый .travis.yml

language: node_js
node_js:
- '8'
- '10'
cache: yarn
script: yarn ci

Потом с помощью travic-cli настроить публикацию — travis setup npm, потребует ввести логин на npm и сгенерировать токен. После добавляем еще один параметр в конфиг файл.

language: node_js
node_js:
- '8'
- '10'
cache: yarn
script: yarn ci
deploy:
	provider: npm
    email: igor@kamyshev.me
    skip_cleanup: true
    api_key:
    	secure: ...
    on:
    	tags: true 
        repo: @solid-soda/console

skip_cleanup: true — запрещает делать reset перед публикацией (собранный код удалять не нужно).

Процесс публикации

После всех этих приготовлений процесс публикации новой версии выглядит очень просто.

yarn release
git push --follow-tags

В репозитории оказывается новый код, тэг для новой версии, а через некоторое время Travis публикует в npm собранный пакет.

Игнорировать

Полагаю, публиковать лишние файлы в npm не здорово, потому — .npmignore

node_modules/
lib/
tsconfig.json
.travis.yml
yarn.lock
tslint.json
jest.config.js
**/__tests__/*
jest.config.js

Ну и кое-что не попадает в гит — .gitignore

node_modules
*.log
dist

Лицензии всегда создаю через интерфейс GitHub.

Вместо заключения

Вполне возможно, этот способ производить пакеты не самый оптимальный. Если вы знаете, что в нем можно улучшить, пожалуйста, напишите в комментариях.