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

Igor Kamyshev
Jan 18, 2019 · 3 min read
Image for post
Image for post
Photo by frank mckenna on Unsplash

Обновлено 8 мая 2019 года.

Я часто делаю 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.

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

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

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store