Модуль 14. Сборка, публикация и CI/CD
Цель модуля: подготовить UI Kit к публикации как взрослую npm-библиотеку:
exports-карта,sideEffects,size-limit, changesets, CI/CD pipeline.
К концу модуля у вас:
package.jsonс subpath exports (./box,./button,./theme) — tree-shaking на уровне entry.size-limitна каждый компонент — CI падает при превышении.- Changesets — автоматический CHANGELOG и semver.
- CI pipeline: lint → typecheck → test → build → size-check → publish.
- Dry-run публикация на npm (без реального релиза).
Перед началом: Модули 1-13. Инструменты: tsup, changesets, GitHub Actions. Время: ~5 ч.
🔥 Инцидент.
npm publishс локальной машины. Версия не обновлена, CHANGELOG пустой. Пользователи UI Kit получают ошибку: «export Button not found». Нетexportsmap в package.json. Нетsize-limit— пакет весит 2 МБ. Ручная публикация не масштабируется.
📖 Теория
14.1 exports-карта (уже в boilerplate)
В boilerplate уже настроена multi-entry сборка. Проверим:
json// ui-kit/package.json (фрагмент) { "sideEffects": false, "exports": { ".": { "import": "./dist/index.js", "types": "./dist/index.d.ts" }, "./box": { "import": "./dist/components/Box/index.js", "types": "./dist/components/Box/index.d.ts" }, "./text": { "import": "./dist/components/Text/index.js", "types": "./dist/components/Text/index.d.ts" }, "./button": { "import": "./dist/components/Button/index.js", "types": "./dist/components/Button/index.d.ts" }, "./theme": { "import": "./dist/theme/index.js", "types": "./dist/theme/index.d.ts" }, "./styles/reset.css": "./dist/reset.css" } }
sideEffects: false
Сообщает bundler'ам (webpack, Vite, Rollup): «все файлы в этом пакете не имеют побочных эффектов, можно tree-shake». Это значит:
ts// Если импортировать только Button: import { Button } from '@myorg/ui-kit/button' // → в бандле окажется ТОЛЬКО Button, не весь UI Kit
CSS — исключение: если CSS-файлы импортируются как side-effect (
import './reset.css'), пометьте их:"sideEffects": ["*.css"]. Нашreset.cssпопадает вexportsкак явный путь — поэтомуfalseбезопасен.
14.2 size-limit — бюджет на размер
Установка:
bashnpm install -D size-limit @size-limit/file
Конфигурация в ui-kit/package.json:
json{ "size-limit": [ { "name": "Box", "path": "dist/components/Box/index.js", "limit": "2 KB" }, { "name": "Text", "path": "dist/components/Text/index.js", "limit": "2 KB" }, { "name": "Button", "path": "dist/components/Button/index.js", "limit": "3 KB" }, { "name": "Icon", "path": "dist/components/Icon/index.js", "limit": "3 KB" }, { "name": "Modal", "path": "dist/components/Modal/index.js", "limit": "5 KB" }, { "name": "Theme", "path": "dist/theme/index.js", "limit": "3 KB" }, { "name": "Full barrel", "path": "dist/index.js", "limit": "20 KB" } ], "scripts": { "size": "size-limit" } }
Запуск:
bashnpm run size --workspace ui-kit # Вывод: # Box: 0.8 KB ✓ (limit 2 KB) # Text: 1.2 KB ✓ (limit 2 KB) # Button: 1.5 KB ✓ (limit 3 KB) # Full barrel: 12.3 KB ✓ (limit 20 KB)
Если компонент превышает лимит — exit code 1, CI падает.
14.3 Changesets — версионирование и CHANGELOG
Установка (уже в boilerplate):
bashnpm install -D @changesets/cli
Как работает
- Студент делает PR с новой фичей (например, добавил
Reaction). - Запускает
npm run changeset:🦋 Which packages would you like to include? ◯ @myorg/ui-kit 🦋 Which type of semver bump? ◯ minor (new feature) 🦋 Summarize the change: Added Reaction component with pulsation animation - Создаётся файл
.changeset/reaction-pulsation.md:yaml--- "@myorg/ui-kit": minor --- Added Reaction component with pulsation animation - При merge в
main— Changesets bot автоматически:- Создаёт PR «Version Packages».
- Bumps version (
0.0.1→0.1.0). - Генерирует
CHANGELOG.md:markdown# @myorg/ui-kit ## 0.1.0 ### Minor Changes - Added Reaction component with pulsation animation
- При merge «Version Packages» PR — пакет публикуется на npm через CI.
Конфигурация (уже в boilerplate)
json// .changeset/config.json { "changelog": "@changesets/cli/changelog", "commit": false, "fixed": [], "linked": [], "access": "public", "baseBranch": "main", "updateInternalDependencies": ["patch", "minor"], "ignore": ["@myorg/app"] }
14.4 CI/CD pipeline (обновляем существующий)
Полный pipeline с размером бандла и changesets:
yaml# .github/workflows/ci.yml name: CI on: push: branches: [main] pull_request: jobs: quality: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '22' cache: 'npm' - run: npm ci - name: Lint run: npm run lint - name: Typecheck run: npm run typecheck - name: Unit tests + coverage run: npm run test:coverage - name: Build UI Kit run: npm run build:ui-kit - name: Size check run: npm run size --workspace ui-kit - name: Build App run: npm run build:app - name: Upload coverage if: always() uses: actions/upload-artifact@v4 with: name: coverage path: coverage/ e2e: runs-on: ubuntu-latest needs: quality steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: '22', cache: 'npm' } - run: npm ci - run: npx playwright install --with-deps chromium - run: npm run build:ui-kit && npm run build:app && npx playwright test --project=chromium release: runs-on: ubuntu-latest needs: [quality, e2e] if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '22' cache: 'npm' registry-url: 'https://registry.npmjs.org' - run: npm ci - name: Build UI Kit run: npm run build:ui-kit - name: Create release PR or publish uses: changesets/action@v1 with: publish: npm run publish:ui-kit env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
14.5 Публикация на npm (dry-run)
Студент не публикует реально, но проверяет что может:
bash# Собрать npm run build:ui-kit # Проверить содержимое пакета (что попадёт в npm) npm pack --workspace ui-kit --dry-run # Вывод: # npm notice === Tarball Contents === # npm notice 1.2kB dist/index.js # npm notice 0.8kB dist/components/Box/index.js # npm notice 1.5kB dist/components/Button/index.js # npm notice ... # npm notice 5.0kB dist/theme/index.js # npm notice 0.5kB package.json # npm notice 3.0kB README.md
Если в выводе есть node_modules/, .storybook/, src/ — files поле в package.json настроено неправильно. Должны быть только dist/ и README.md.
14.6 Антипаттерны
| Антипаттерн | Как правильно |
|---|---|
peerDependencies не declared | React/react-dom в peerDependencies, не в dependencies |
sideEffects: false без проверки CSS | Если есть side-effect CSS — "sideEffects": ["*.css"] |
Публикация без size-limit | size-limit в CI — overflow = fail |
| semver вручную | changesets: автоматический bump + CHANGELOG |
Нет files в package.json | Явно: "files": ["dist", "README.md"] |
latest версии в deps | Явные ^ semver диапазоны |
Компромиссы (Trade-offs)
| Решение | Плюсы | Минусы |
|---|---|---|
exports map (subpath) | Tree-shaking на entry, чистый импорт | Нужно поддерживать каждый путь |
sideEffects: false | Bundler tree-shake'ит всё | CSS-импорты могут потеряться |
size-limit в CI | Ловит рост бандла в PR | Лимиты нужно калибровать |
| Changesets | Авто-bump версии + CHANGELOG | Нужен бот и NPM_TOKEN |
npm publish из CI | Воспроизводимо, без человека | Сложнее откатить |
💻 14.7 Практика
- Установите
size-limitи@size-limit/file. - Настройте
size-limitвui-kit/package.json(§14.2). - Создайте changeset:
npm run changeset→.changeset/example.md. - Обновите CI workflow (§14.4).
- Запустите
npm pack --dry-run— проверьте что в пакете толькоdist/. - Запустите
npm run size— все компоненты в пределах лимитов.
bashnpm run verify npm run size --workspace ui-kit npm pack --workspace ui-kit --dry-run
14.8 Артефакты сдачи М14
| Артефакт | Критерий |
|---|---|
size-limit вывод | Все компоненты в пределах лимитов |
npm pack --dry-run | В пакете только dist/ + README.md + package.json |
.changeset/*.md | Минимум один changeset (например, «Added Modal component») |
| CI workflow | job release с changesets/action настроен |
🧩 Проверь себя
🔗 Мостик к Модулю 15
UI Kit опубликован как npm-пакет. Теперь свяжем фронтенд с Go-бэкендом через OpenAPI-контракт и проведём финальное ревью архитектуры.