JS Tower
Node.jsМодуль 2: Экосистема npm

Создание своего пакета

Публикация в npm

Цель урока

В этом уроке ты научишься:

  • Создавать npm пакет
  • Публиковать в npm registry
  • Понимать семантическое версионирование

Структура пакета

my-package/
  ├── src/
  │   └── index.js
  ├── dist/           # Скомпилированный код
  ├── tests/
  ├── package.json
  ├── README.md
  ├── LICENSE
  └── .npmignore

package.json для пакета

{
  "name": "@username/my-package",
  "version": "1.0.0",
  "description": "Описание пакета",
  "main": "dist/index.js",
  "module": "dist/index.mjs",
  "types": "dist/index.d.ts",
  "files": [
    "dist"
  ],
  "scripts": {
    "build": "tsc",
    "prepublishOnly": "npm run build && npm test",
    "test": "jest"
  },
  "keywords": ["utility", "helper"],
  "author": "Имя <email@example.com>",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/username/my-package"
  },
  "bugs": {
    "url": "https://github.com/username/my-package/issues"
  },
  "homepage": "https://github.com/username/my-package#readme",
  "engines": {
    "node": ">=16.0.0"
  },
  "peerDependencies": {},
  "devDependencies": {
    "typescript": "^5.0.0",
    "jest": "^29.0.0"
  }
}

Важные поля

main, module, types

{
  "main": "dist/index.js",      // CommonJS точка входа
  "module": "dist/index.mjs",   // ES Module точка входа
  "types": "dist/index.d.ts"    // TypeScript типы
}

exports (современный подход)

{
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.js",
      "types": "./dist/index.d.ts"
    },
    "./utils": {
      "import": "./dist/utils.mjs",
      "require": "./dist/utils.js"
    }
  }
}

files

{
  "files": [
    "dist",
    "README.md"
  ]
}

Только эти файлы попадут в опубликованный пакет.


.npmignore

src/
tests/
*.test.js
.github/
node_modules/

Альтернатива

Лучше использовать поле files в package.json вместо .npmignore.


Публикация

Регистрация

# Создание аккаунта
npm adduser

# Вход
npm login

# Проверка
npm whoami

Публикация

# Проверка что будет опубликовано
npm pack --dry-run

# Публикация
npm publish

# Публикация scoped пакета публично
npm publish --access public

Обновление версии

# Patch: 1.0.0 -> 1.0.1
npm version patch

# Minor: 1.0.0 -> 1.1.0
npm version minor

# Major: 1.0.0 -> 2.0.0
npm version major

# Публикация новой версии
npm publish

Семантическое версионирование

Правила

ИзменениеВерсияКогда
MAJOR1.0.0 → 2.0.0Breaking changes
MINOR1.0.0 → 1.1.0Новые фичи
PATCH1.0.0 → 1.0.1Баг-фиксы

Pre-release

1.0.0-alpha.1
1.0.0-beta.1
1.0.0-rc.1
npm version prerelease --preid=alpha
npm publish --tag alpha

Пример пакета

src/index.ts

export function greet(name: string): string {
  return `Hello, ${name}!`;
}

export function sum(a: number, b: number): number {
  return a + b;
}

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "CommonJS",
    "declaration": true,
    "outDir": "./dist",
    "strict": true
  },
  "include": ["src"]
}

Сборка и публикация

npm run build
npm publish

Практика

Задание 1: Создание пакета

Задача: Создай пакет с функцией capitalize.

Решение:

// src/index.js
function capitalize(str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

module.exports = { capitalize };
{
  "name": "@username/capitalize",
  "version": "1.0.0",
  "main": "src/index.js",
  "files": ["src"]
}

Проверь себя

  1. Что означает MAJOR версия?
  2. Зачем нужно поле files?
  3. Как опубликовать scoped пакет публично?