Выносил ли что-либо на проекте в отдельный npm пакет
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как вынести код в отдельный npm пакет
Это частый вопрос на собеседованиях. Работодатель хочет понять, может ли разработчик организовать переиспользуемый код, управлять версиями и работать с package managers. Рассмотрим полный процесс.
Зачем выносить код в npm пакет?
1. Переиспользуемость
- Один пакет используется в нескольких проектах
- Нет необходимости копировать код
- Единый источник истины
2. Версионирование
- Контроль версий (semantic versioning)
- Возможность обновлять разные проекты независимо
- Breaking changes явно обозначены
3. Управление зависимостями
- Пакет может иметь свои зависимости
- Избегаем дублирования больших библиотек
- Чётко определены peer dependencies
4. Масштабируемость
- Отделяется от основного приложения
- Может развиваться отдельно
- Легче искать баги в изолированном коде
Пример 1: Простой утилити пакет
Предположим, у вас есть функции для работы с датами и форматирования, которые используются в 3 проектах.
Шаг 1: Создайте директорию для пакета
mkdir my-utils-package
cd my-utils-package
npm init -y
Шаг 2: Создайте структуру
my-utils-package/
src/
index.ts
date.ts
format.ts
package.json
tsconfig.json
README.md
Шаг 3: Напишите код (src/date.ts)
export function formatDate(date: Date, format: string = 'dd.MM.yyyy'): string {
const day = String(date.getDate()).padStart(2, '0');
const month = String(date.getMonth() + 1).padStart(2, '0');
const year = date.getFullYear();
return format
.replace('dd', day)
.replace('MM', month)
.replace('yyyy', year);
}
export function addDays(date: Date, days: number): Date {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
Шаг 4: Создайте точку входа (src/index.ts)
export * from './date';
export * from './format';
Шаг 5: Обновите package.json
{
"name": "@mycompany/utils",
"version": "1.0.0",
"description": "Common utilities for our projects",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"test": "jest",
"publish": "npm run build && npm publish"
},
"keywords": ["utils", "date", "format"],
"author": "My Company",
"license": "MIT",
"devDependencies": {
"typescript": "^5.0.0",
"jest": "^29.0.0"
}
}
Шаг 6: Настройте TypeScript (tsconfig.json)
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020"],
"declaration": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
Шаг 7: Соберите пакет
npm run build
Пример 2: React компонент пакет
Если выносите React компоненты:
Структура:
my-components/
src/
Button/
Button.tsx
Button.test.tsx
Button.css
index.ts
Input/
Input.tsx
Input.test.tsx
index.ts
index.ts
package.json
src/Button/Button.tsx
import React from 'react';
import './Button.css';
interface ButtonProps {
label: string;
onClick: () => void;
variant?: 'primary' | 'secondary';
}
export function Button({ label, onClick, variant = 'primary' }: ButtonProps) {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
>
{label}
</button>
);
}
package.json для React пакета
{
"name": "@mycompany/components",
"version": "1.0.0",
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
},
"devDependencies": {
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}
Как опубликовать пакет
Вариант 1: На npm публичный реестр
# Создайте аккаунт на npm.com
# Логинитесь
npm login
# Публикуйте
npm publish --access public
# После этого пакет будет доступен для установки
npm install @mycompany/utils
Вариант 2: На приватный реестр компании
# Настройка в .npmrc
npm config set registry https://npm.mycompany.com/
npm login --registry https://npm.mycompany.com/
npm publish
Вариант 3: GitHub Packages
# .npmrc в проекте
@mycompany:registry=https://npm.pkg.github.com
# Публикация
npm publish
Как использовать пакет в проектах
Установка
npm install @mycompany/utils
Использование в коде
import { formatDate, addDays } from '@mycompany/utils';
const today = new Date();
const tomorrow = addDays(today, 1);
console.log(formatDate(tomorrow));
Semantic Versioning
Важно правильно нумеровать версии:
Формат: MAJOR.MINOR.PATCH
- MAJOR (1.0.0 -> 2.0.0): Breaking changes (удалили функцию, изменили сигнатуру)
- MINOR (1.0.0 -> 1.1.0): Новая функциональность, обратно совместимо
- PATCH (1.0.0 -> 1.0.1): Исправления багов
# Обновите версию
npm version patch # 1.0.0 -> 1.0.1
npm version minor # 1.0.0 -> 1.1.0
npm version major # 1.0.0 -> 2.0.0
# Опубликуйте
npm publish
Лучшие практики
1. Документация
# My Utils Package
## Установка
npm install @mycompany/utils
## Использование
import { formatDate } from '@mycompany/utils';
const formatted = formatDate(new Date());
## API
### formatDate(date, format)
- date: Date
- format: string (dd.MM.yyyy)
- returns: string
2. Тестирование
npm run test
3. Минимизированный бандл
{
"scripts": {
"build": "tsc && esbuild src/index.ts --bundle --minify --outfile=dist/index.js"
}
}
4. TypeScript типы
{
"main": "dist/index.js",
"types": "dist/index.d.ts"
}
На собеседовании
Если у вас нет реального опыта, ответьте:
"Я вынес X функциональность в отдельный пакет, потому что она использовалась в нескольких проектах. Я:
- Создал структуру пакета с src/, типами TypeScript
- Опубликовал на npm (или приватный реестр)
- Использовал semantic versioning для управления версиями
- Написал документацию и тесты
- Обновил зависимости в основных проектах
Это помогло избежать дублирования кода и облегчило поддержку."