Alfa Brain

Как создать пакет NPM

Алексей ВечкановАлексей Вечканов   

Создание собственного npm-пакета может показаться сложной задачей, но на деле это увлекательный процесс, который открывает новые возможности для разработки и обмена своими наработками с сообществом. В этой статье мы подробно разберем, как создать свой первый npm-пакет, шаг за шагом пройдем процесс его подготовки, публикации и управления. Независимо от того, хотите ли вы поделиться полезной библиотекой или автоматизировать задачи в вашем проекте, это руководство поможет вам начать путь к созданию собственного инструмента.

Изображение статьи

Эта статья - перевод и адаптация статьи How To Create An NPM Package от Matt Pocock

В этой статье будут использованы следующие технологии:

1) Git для контроля версий
2) TypeScript для написания кода и обеспечения его типобезопасности.
3) Prettier для форматирования кода
4) @arethetypeswrong/cli для проверки нашего экспорта
5) tsup для компиляции нашего кода TypeScript в CJS и ESM
6) Vitest для проведения наших тестов
7) GitHub Actions для запуска CI
8) Changesets для управления версиями и публикации нашего пакета

Если вы хотите увидеть готовый результат - посмотрите этот репозиторий.

1. Git

В этом разделе мы создадим новый репозиторий git, настроим .gitignore, создадим первоначальный коммит, создадим новый репозиторий на GitHub и отправим наш код на GitHub.

1.1: Инициализация репозитория

Выполните следующую команду, чтобы инициализировать новый репозиторий git:

git init

1.2: Настройка .gitignore

Создайте файл .gitignore в корне вашего проекта и добавьте следующее:

node_modules

1.3: Создание первоначального коммита

Выполните следующую команду, чтобы создать первоначальный коммит:

git add .
git commit -m "Initial commit"

1.4: Создание нового репозитория на GitHub.

У github есть замечательная консольная утилита gh для работы с github из консоли. Если до этого вы не пользовались cli утилитой - перейдите на сайт https://cli.github.com и установите утилиту по инструкции.

Пройдите аутентификацию вызвав команду:

gh auth login

Выполните следующую команду, чтобы создать новый репозиторий. Для этого примера я выбрал имя tt-package-demo:

gh repo create tt-package-demo --source=. --public

Команда gh repo create tt-package-demo --source=. --public выполнит следующие действия:

1. Создаст новый удалённый репозиторий с именем tt-package-demo на GitHub.

2. Укажет исходную директорию (в данном случае текущую директорию, обозначенную как .) для этого репозитория.

3. Сделает репозиторий публичным (благодаря флагу --public).

4. Привяжет удалённый репозиторий к вашему локальному репозиторию, добавив GitHub-репозиторий в качестве удалённого (origin).

Таким образом, после выполнения этой команды ваш локальный репозиторий будет связан с новым удалённым репозиторием на GitHub, и вы сможете отправлять коммиты с помощью команды git push origin main или аналогичных команд для других веток.

1.5: Push в GitHub

Выполните следующую команду, чтобы отправить свой код на GitHub:

git push --set-upstream origin main

2: package.json

В этом разделе мы создадим файл package.json, добавим поле license, создадим файл LICENSE и добавим файл README.md.

2.1. Создание файла package.json

Создайте файл package.json со следующим содержимым:

{
  "name": "tt-package-demo",
  "version": "1.0.0",
  "description": "A demo package for Total TypeScript",
  "keywords": ["demo", "typescript"],
  "homepage": "https://github.com/mattpocock/tt-package-demo",
  "bugs": {
    "url": "https://github.com/mattpocock/tt-package-demo/issues"
  },
  "author": "Matt Pocock <[email protected]> (https://totaltypescript.com)",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/mattpocock/tt-package-demo.git"
  },
  "files": ["dist"],
  "type": "module"
}

name - это имя, под которым пользователи будут устанавливать ваш пакет. Оно должно быть уникальным в npm. Для имени пакета можете создать так называемые organization scopes (например, имя пакета @total-typescript/demo где @total-typescript - пространство организации), это поможет сделать ваш продукт уникальным.

version - это версия вашего пакета. Он должен соответствовать семантическому версионированию (semantic versioning): формат 0.0.1. Каждый раз, когда вы публикуете новую версию, вы должны увеличивать это число.

description и keywords - это краткое описание вашего пакета. Они указаны в поиске в реестре npm.

homepage - это URL-адрес домашней страницы вашего пакета. По умолчанию можно использовать репозиторий на GitHub или сайт с документами, если он у вас есть.

bugs - это URL-адрес, по которому пользователи могут сообщать о проблемах с вашим пакетом.

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

repository - это URL-адрес хранилища вашего пакета. При этом в реестре npm создается ссылка на ваш репозиторий на GitHub.

files - это массив файлов, которые должны быть включены при установке вашего пакета. В данном случае мы включаем папку dist. Файлы README.md, package.json и LICENSE включены по умолчанию.

Значение type равно module, чтобы указать, что ваш пакет использует систему модулей ECMAScript, а не модули CommonJS.

2.2: Добавление поле лицензии.

Добавьте поле license в package.json. Выберите лицензию здесь. Я выбрал MIT.

{
  "license": "MIT"
}

2.3: Добавление файла ЛИЦЕНЗИИ

Создайте файл с именем LICENSE (без расширения), содержащий текст вашей лицензии. Для MIT это:

MIT License

Copyright (c) [year] [fullname]

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Измените заполнители [год] и [полное имя] на текущий год и свое имя.

2.4: Добавление README.md

Создайте файл README.md с описанием вашего пакета. Вот пример:

**tt-package-demo**

A demo package for Total TypeScript.

Это будет показано в реестре npm, когда люди будут просматривать ваш пакет.

3: TypeScript

В этом разделе мы установим TypeScript, настроим tsconfig.json, создадим исходный файл, создадим index файл, настроим сценарий сборки (build), запустим нашу сборку, добавим dist в .gitignore, настроим сценарий ci и настроим наш tsconfig.json для DOM.

3.1: Установка TypeScript

Запустите следующую команду, чтобы установить TypeScript:

npm install --save-dev typescript

Мы добавляем к команде флаг --save-dev для установки TypeScript в качестве зависимости разработки. Это означает, что он не будет включен при установке вашего пакета у пользователя.

3.2. Настройка tsconfig.json.

Создайте файл tsconfig.json со следующим содержимым:

{
  "compilerOptions": {
    /* Base Options: */
    "esModuleInterop": true,
    "skipLibCheck": true,
    "target": "es2022",
    "allowJs": true,
    "resolveJsonModule": true,
    "moduleDetection": "force",
    "isolatedModules": true,
    "verbatimModuleSyntax": true,

    /* Strictness */
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,

    /* If transpiling with TypeScript: */
    "module": "NodeNext",
    "outDir": "dist",
    "rootDir": "src",
    "sourceMap": true,

    /* AND if you're building for a library: */
    "declaration": true,

    /* AND if you're building for a library in a monorepo: */
    "declarationMap": true
  }
}

Эти параметры подробно описаны в шпаргалке по TSConfig.

3.3. Настройка tsconfig.json для DOM

Если ваш код выполняется в DOM (т. е. требует доступа к document, window, localStorage и т. д.), пропустите этот шаг.

Если вашему коду не требуется доступ к DOM API, добавьте в tsconfig.json следующее:

{
  "compilerOptions": {
    // ...other options
    "lib": ["es2022"]
  }
}

Опция `"lib": ["es2022"]` в настройках TypeScript указывает, какие встроенные библиотеки JavaScript должны быть доступны во время компиляции. В данном случае, указывая `["es2022"]`, вы говорите компилятору, что хотите использовать возможности и объекты, доступные в спецификации ECMAScript 2022.

Это позволяет вам использовать такие возможности, как методы и API, которые появились в ES2022, например, `Array.prototype.at`, улучшенные функции для работы с `WeakRefs` и другие.

Если ваш код должен поддерживать более старые версии JavaScript, можно выбрать соответствующие библиотеки, например, `"es5"` или `"es6"`.

При использовании настройки "lib": ["es2022"] поддержка типов для DOM (например, таких объектов, как document, window, HTMLElement и т. д.) не будет включена автоматически, потому что в данном случае подключается только стандартная библиотека ECMAScript 2022.

Чтобы добавить поддержку типов для DOM, нужно явно указать библиотеку DOM, добавив её в список `"lib": ["es2022", "dom"]`

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

Но в нашем случае это не требуется.

3.4: Создание исходный файл (Source File)

Создайте файл src/utils.ts со следующим содержимым:

export const add = (a: number, b: number) => a + b;

3.5: Создание индексного файла (Index File)

Создайте файл src/index.ts со следующим содержимым:

export { add } from "./utils.js";

Понимаю, расширение .js выглядит странно. Эта статья объясняет больше.

Если совсем кратко, то в статье объясняется, что при использовании настроек moduleResolution: node16 или nodenext в TypeScript требуется явное указание расширений файлов при импорте (например, ./module.js). Это сделано для соответствия спецификации Node.js, что упрощает процесс разрешения модулей. Если добавить расширение .ts, возникает ошибка, так как эта опция работает только при включенной настройке allowImportingTsExtensions. Чтобы избежать этого, рекомендуется использовать .js в импортах или изменить модульную конфигурацию.

3.6: Настройка сценария сборки

Добавьте секцию scripts в ваш package.json со следующим содержимым:

{
  "scripts": {
    "build": "tsc"
  }
}

Этот скрипт скомпилирует ваш код TypeScript в JavaScript.

3.7: Запуск сборки

Запустите следующую команду, чтобы скомпилировать код TypeScript:

npm run build

После выполнения команды будет создана директория dist с вашим скомпилированным кодом JavaScript.

3.8: Добавьте dist в .gitignore

Добавьте папку dist в ваш файл .gitignore файл:

dist

Это предотвратит включение скомпилированного кода в ваш репозиторий git.

3.9: Настройка ci-скрипта

Добавьте в package.json скрипт ci со следующим содержимым:

{
  "scripts": {
    "ci": "npm run build"
  }
}

Это даст нам быстрый доступ для выполнения всех необходимых операций в CI.

4: Prettier

В этом разделе мы установим Prettier, настроим .prettierrc, настроим скрипт форматирования (format), запустим скрипт форматирования (format), настроим скрипт проверки формата (check-format), добавим скрипт проверки формата (check-format) в наш скрипт CI и запустим CI сценарий.

4.1: Установка Prettier

Запустите следующую команду, чтобы установить Prettier:

npm install --save-dev prettier

4.2: Настройка .prettierrc

Создайте файл .prettierrc со следующим содержимым:

{
  "semi": true,
  "singleQuote": true,
  "trailingComma": "all",
  "printWidth": 80,
  "tabWidth": 2
}

Вы можете добавить в этот файл дополнительные параметры, чтобы настроить поведение Prettier. Полный список опций вы можете найти здесь.

4.3: Настройка сценария форматирования (format script)

Добавьте в package.json скрипт format со следующим содержимым:

{
  "scripts": {
    "format": "prettier --write ."
  }
}

Это приведет к форматированию всех файлов вашего проекта с помощью Prettier.

4.4: Запуск сценарий форматирования

Запустите следующую команду, чтобы отформатировать все файлы вашего проекта:

npm run format

Вы можете заметить, что некоторые файлы изменились. Зафиксируйте изменения с помощью команд:

git add .
git commit -m "Format code with Prettier"

4.5: Настройка сценария проверки формата (check-format)

Добавьте в package.json скрипт check-format со следующим содержимым:

{
  "scripts": {
    "check-format": "prettier --check ."
  }
}

Этот скрипт проверит, правильно ли отформатированы все файлы в вашем проекте.

4.6: Добавление CI-скрипта

Добавьте скрипт check-format в скрипт ci в package.json:

{
  "scripts": {
    "ci": "npm run build && npm run check-format"
  }
}

Это запустит скрипт проверки формата как часть вашего процесса CI.

5: exports, main и @arethetypeswrong/cli

В этом разделе мы установим @arethetypeswrong/cli, настроим скрипт check-exports, запустим скрипт check-exports, настроим main поле, снова запустим скрипт check-exports, настроим и запустим скрипт ci.

@arethetypeswrong/cli — это инструмент, который проверяет правильность экспорта вашего пакета. Это важно, поскольку в них легко ошибиться, и это может вызвать проблемы у людей, использующих ваш пакет.

5.1: Установка @arethetypeswrong/cli

Запустите следующую команду, чтобы установить @arethetypeswrong/cli:

npm install --save-dev @arethetypeswrong/cli

5.2: Настройка скрипта проверки экспорта

Добавьте в package.json скрипт check-exports со следующим содержимым:

{
  "scripts": {
    "check-exports": "attw --pack ."
  }
}

Это проверит, все ли экспорты из вашего пакета верны.

5.3: Запуск скрипта check-exports

Запустите следующую команду, чтобы проверить правильность всех экспортов из вашего пакета:

npm run check-exports

Вы должны заметить различные ошибки:

┌───────────────────┬──────────────────────┐
│                   │ "tt-package-demo"    │
├───────────────────┼──────────────────────┤
│ node10            │ 💀 Resolution failed │
├───────────────────┼──────────────────────┤
│ node16 (from CJS) │ 💀 Resolution failed │
├───────────────────┼──────────────────────┤
│ node16 (from ESM) │ 💀 Resolution failed │
├───────────────────┼──────────────────────┤
│ bundler           │ 💀 Resolution failed │
└───────────────────┴──────────────────────┘

Это означает, что ни одна версия Node или какой-либо сборщик не может использовать наш пакет.

Давайте это исправим

5.4: Установка main

Добавьте поле main в package.json со следующим содержимым:

{
  "main": "dist/index.js"
}

Это строчка подсказывает Node, где найти точку входа вашего пакета.

5.5: Пробуем еще раз проверить экспорт

Запустите следующую команду, чтобы проверить правильность всех экспортов из вашего пакета:

npm run check-exports

Теперь, вы должны заметить только одно предупреждение:

┌───────────────────┬──────────────────────────────┐
│                   │ "tt-package-demo"            │
├───────────────────┼──────────────────────────────┤
│ node10            │ 🟢                           │
├───────────────────┼──────────────────────────────┤
│ node16 (from CJS) │ ⚠️ ESM (dynamic import only) │
├───────────────────┼──────────────────────────────┤
│ node16 (from ESM) │ 🟢 (ESM)                     │
├───────────────────┼──────────────────────────────┤
│ bundler           │ 🟢                           │
└───────────────────┴──────────────────────────────┘

Это говорит нам о том, что наш пакет совместим с системами, на которых работает ESM. Людям, использующим CJS (часто в устаревших системах), потребуется импортировать его с помощью динамического импорта.

5.6 Исправление предупреждения CJS

Если вы не хотите поддерживать CJS модули (что я рекомендую), измените сценарий check-exports на:

{
  "scripts": {
    "check-exports": "attw --pack . --ignore-rules=cjs-resolves-to-esm"
  }
}

Теперь при запуске check-export все будет отображаться зеленым цветом:

┌───────────────────┬───────────────────┐
│                   │ "tt-package-demo" │
├───────────────────┼───────────────────┤
│ node10            │ 🟢                │
├───────────────────┼───────────────────┤
│ node16 (from CJS) │ 🟢 (ESM)          │
├───────────────────┼───────────────────┤
│ node16 (from ESM) │ 🟢 (ESM)          │
├───────────────────┼───────────────────┤
│ bundler           │ 🟢                │
└───────────────────┴───────────────────┘

Если вы предпочитаете двойную публикацию CJS и ESM, пропустите этот шаг.

5.7: Добавление в наш CI-скрипт

Добавьте скрипт check-exports в сценарий ci в package.json:

{
  "scripts": {
    "ci": "npm run build && npm run check-format && npm run check-exports"
  }
}

6. Использование tsup для двойной публикации

Если вы хотите опубликовать код CJS и ESM модулей, вы можете использовать tsup. Это инструмент, созданный на основе esbuild, который компилирует ваш код TypeScript в оба формата.

Моя личная рекомендация — пропустить этот шаг и отправлять только ES-модули. Это значительно упрощает настройку и позволяет избежать многих ошибок двойной публикации, таких как Dual Package Hazard.

6.1: Установите tsup

Запустите следующую команду, чтобы установить tsup:

npm install --save-dev tsup

6.2. Создайте файл tsup.config.ts

Создайте файл tsup.config.ts со следующим содержимым:

import { defineConfig } from "tsup";

export default defineConfig({
  entryPoints: ["src/index.ts"],
  format: ["cjs", "esm"],
  dts: true,
  outDir: "dist",
  clean: true,
});

entryPoints - это массив точек входа для вашего пакета. В данном случае мы используем src/index.ts.

format - это массив форматов для вывода. Мы используем js (CommonJS) и esm (модули ECMAScript).

dts - параметр, которое указывает tsup генерировать файлы объявлений.

outDir - это выходной каталог для скомпилированного кода.

clean указывает tsup очистить выходной каталог перед сборкой.

6.3: Изменение сценария сборки

{
  "scripts": {
    "build": "tsup"
  }
}

Теперь мы будем запускать tsup для компиляции нашего кода вместо tsc.

6.4: Добавьте поле exports

Добавьте поле exports экспорта в package.json со следующим содержимым:

{
  "exports": {
    "./package.json": "./package.json",
    ".": {
      "import": "./dist/index.js",
      "default": "./dist/index.cjs"
    }
  }
}

Поле exports сообщает программам, использующим ваш пакет, как найти версии вашего пакета CJS и ESM. В этом случае мы указываем пользователям, использующим импорт, на dist/index.js, а пользователям, использующим require, — на dist/index.cjs.

Также рекомендуется добавить ./package.json в поле экспорта. Это связано с тем, что некоторым инструментам необходим легкий доступ к вашему файлу package.json.

6.5: Пробуем еще раз проверить экспорт

Запустите следующую команду, чтобы проверить правильность всех экспортов из вашего пакета:

npm run check-exports

Теперь все зеленое:

┌───────────────────┬───────────────────┐
│                   │ "tt-package-demo" │
├───────────────────┼───────────────────┤
│ node10            │ 🟢                │
├───────────────────┼───────────────────┤
│ node16 (from CJS) │ 🟢 (CJS)          │
├───────────────────┼───────────────────┤
│ node16 (from ESM) │ 🟢 (ESM)          │
├───────────────────┼───────────────────┤
│ bundler           │ 🟢                │
└───────────────────┴───────────────────┘

6.6: Превращаем TypeScript в линтер

Мы больше не используем tsc для компиляции нашего кода. Но tsup на самом деле не проверяет наш код на наличие ошибок — он просто превращает его в JavaScript.

Это означает, что наш сценарий ci не выдаст ошибку, если в нашем коде есть ошибки TypeScript.

Давайте это исправим.

6.6.1: Добавляем noEmit в tsconfig.json.

{
  "compilerOptions": {
    // ...other options
    "noEmit": true
  }
}

6.6.2. Удаляем неиспользуемые поля из tsconfig.json.

Удалите следующие поля из вашего tsconfig.json:

outDir

rootDir

sourceMap

declaration

declarationMap

Они больше не нужны в нашей новой настройке «линтинга».

6.6.3: Изменение module на «Preserve»

При желании теперь вы можете изменить module на Preserve в вашем tsconfig.json:

{
  "compilerOptions": {
    // ...other options
    "module": "Preserve"
  }
}

Это означает, что вам больше не нужно будет импортировать файлы с расширениями .js. Это означает, что index.ts может выглядеть следующим образом:

export * from "./utils";

6.6.4: Добавление lint скрипта

Добавьте скрипт lint в свой package.json со следующим содержимым:

{
  "scripts": {
    "lint": "tsc"
  }
}

Это запустит TypeScript в качестве линтера.

6.6.5: Добавьте lint в ваш ci-скрипт

Добавьте сценарий lint в сценарий ci в package.json:

{
  "scripts": {
    "ci": "npm run build && npm run check-format && npm run check-exports && npm run lint"
  }
}

Теперь мы будем получать ошибки TypeScript как часть нашего процесса CI.

7. Тестирование с помощью Vitest

В этом разделе мы установим vitest, создадим тест, настроим скрипт test, запустим его, настроим скрипт dev и добавим test скрипт в наш CI-скрипт.

vitest — это современный инструмент для запуска тестов для ESM и TypeScript. Это как Jest, но лучше.

7.1: Установка vitest

Запустите следующую команду для установки vitest:

npm install --save-dev vitest

7.2: Создаем тест

Создайте файл src/utils.test.ts со следующим содержимым:

import { add } from "./utils.js";
import { test, expect } from "vitest";

test("add", () => {
  expect(add(1, 2)).toBe(3);
});

Это простой тест, который проверяет, возвращает ли функция hello правильное значение.

7.3: Настройка скрипта test

Добавьте скрипт test в package.json со следующим содержимым:

{
  "scripts": {
    "test": "vitest run"
  }
}

vitest run запускает все тесты в вашем проекте один раз, без просмотра.

7.4: Запускаем test скрипт

Выполните следующую команду, чтобы запустить тесты:

npm run test

Вы должны увидеть следующий вывод:

 ✓ src/utils.test.ts (1)
   ✓ hello

 Test Files  1 passed (1)
      Tests  1 passed (1)

Это означает, что ваш тест пройден успешно.

7.5: Установка скрипта dev

Обычный рабочий процесс — запуск тестов в режиме просмотра во время разработки. Добавьте в package.json скрипт dev со следующим содержимым:

{
  "scripts": {
    "dev": "vitest"
  }
}

Это запустит ваши тесты в режиме наблюдения.

7.6: Добавление в наш CI-скрипт

Добавьте test скрипт в свой ci-скрипт в package.json:

{
  "scripts": {
    "ci": "npm run build && npm run check-format && npm run check-exports && npm run lint && npm run test"
  }
}

8. Настройка CI с помощью GitHub Actions.

В этом разделе мы создадим рабочий процесс GitHub Actions, который запускает наш процесс CI при каждом запросе фиксации и извлечения.

Это решающий шаг в обеспечении того, чтобы наш пакет всегда находился в рабочем состоянии.

8.1: Создание workflow

Создайте файл .github/workflows/ci.yml со следующим содержимым:

name: CI

on:
  pull_request:
  push:
    branches:
      - main

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  ci:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Use Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Install dependencies
        run: npm install

      - name: Run CI
        run: npm run ci

Этот файл GitHub использует в качестве инструкций для запуска вашего процесса CI.

name - это название рабочего процесса.

on указывает, когда должен выполняться рабочий процесс. В этом случае он выполняется по запросам на загрузку и передается в главную ветвь.

concurrency предотвращает одновременный запуск нескольких экземпляров рабочего процесса, используя cancel-in-progress для отмены любых существующих запусков.

jobs - это набор заданий для запуска. В данном случае у нас есть одно задание под названием ci.

actions/checkout@v4 извлекает код из хранилища.

actions/setup-node@v4 устанавливает Node.js и npm.

npm install устанавливает зависимости проекта.

npm run ci запускает сценарий CI проекта.

Если какая-либо часть нашего процесса CI завершится сбоем, рабочий процесс завершится неудачно, и GitHub сообщит нам об этом, показав красный крестик рядом с нашим коммитом.

8.2: Тестирование нашего workflow

Запуште изменения на GitHub и проверьте вкладку «Actions» в своем репозитории. Вы должны увидеть, как работает ваш workflow.

Это даст нам предупреждение о каждом сделанном коммите и каждом запросе на добавление в репозиторий.

9. Публикация с Changesets

В этом разделе мы установим @changesets/cli, инициализируем наборы изменений, опубликуем выпуски набора изменений, установим для параметра commit значение true, настроим скрипт локального выпуска, добавим набор изменений, зафиксируем изменения, запустим сценарий локального выпуска и наконец-то увидите свой пакет на npm.

9.1: Установка @changesets/cli

Запустите следующую команду для инициализации Changesets:

npm install --save-dev @changesets/cli

9.2: Инициализация Changesets

Запустите следующую команду для инициализации наборов изменений:

npx changeset init

В вашем проекте будет создана папка .changeset, содержащая файл config.json. Здесь также будут храниться ваши Changesets.

9.3: Публикуем Changesets

В .changeset/config.json измените поле access на общедоступное:

// .changeset/config.json
{
  "access": "public"
}

Без изменения этого поля Changesets не опубликуют ваш пакет в npm.

9.4: Установка commit в true

В .changeset/config.json измените поле commit на true:

// .changeset/config.json
{
  "commit": true
}

Это зафиксирует Changesets в вашем репозитории после создания версий.

9.5: Настройка скрипта local-release

Добавьте в package.json скрипт local-release со следующим содержимым:

{
  "scripts": {
    "local-release": "changeset version && changeset publish"
  }
}

Этот скрипт запустит ваш процесс CI, а затем опубликует ваш пакет в npm. Это будет команда, которую вы запустите, когда захотите выпустить новую версию вашего пакета с вашего локального компьютера.

9.6 Запуск CI только в prepublishOnly

Добавьте в package.json скрипт prepublishOnly со следующим содержимым:

{
  "scripts": {
    "prepublishOnly": "npm run ci"
  }
}

Это автоматически запустит процесс CI перед публикацией пакета в npm.

Это полезно для отделения от скрипта local-release на случай, если пользователь случайно запустит npm publish без запуска локальной версии.

9.7: Добавление Changesets

Запустите следующую команду, чтобы добавить changeset:

npx changeset

Откроется интерактивное приглашение, в котором вы сможете добавить changeset. Changeset — это способ сгруппировать изменения и присвоить им номер версии.

Отметьте этот выпуск как выпуск patch и дайте ему описание, например «Initial release».

Это создаст новый файл в папке .changeset с набором изменений.

9.8: Фиксируем изменения

Зафиксируйте изменения в своем репозитории:

git add .
git commit -m "Prepare for initial release"

9.9: Запустите скрипт local-release

Выполните следующую команду, чтобы зарелизить пакет:

npm run local-release

Это запустит ваш процесс CI, версионирует ваш пакет и опубликует его в npm.

В вашем репозитории будет создан файл CHANGELOG.md с подробным описанием изменений в этом выпуске. Оно будет обновляться каждый раз при выпуске.

9.10: Смотрим свой пакет на npm

Перейдите к:

http://npmjs.com/package/<your package name>

Вы должны увидеть там свою посылку! Вы сделали это! Вы опубликовали в npm!

Выводы

Теперь у вас есть полностью настроенный пакет. Вы настроили:

- Проект TypeScript с новейшими настройками.
- Prettier, который одновременно форматирует ваш код и проверяет его правильность.
@arethetypeswrong/cli, который проверяет правильность экспорта вашего пакета.
tsup, который компилирует ваш код TypeScript в JavaScript.
vitest, который запускает ваши тесты
GitHub Actions, который запускает ваш процесс CI
Changesets: описания изменений в пакете

Для дальнейшего чтения я бы рекомендовал настроить действие GitHub Changesets и PR-бота, чтобы автоматически рекомендовать участникам добавлять changesets в свои PR.

А если у вас возникнут еще вопросы, дайте мне знать!



Поделиться: