Создайте свой собственный пакет npm с накопительным пакетом (полный процесс)

rollup.js

В этой статье в общих чертах описан процесс использования rollup для создания собственного пакета npm, включая тестирование, документацию, публикацию и многое другое. Большинство из них являются разбором процесса, и детали процесса не излагаются по отдельности.Репозиторий исходного кодаЧитайте вместе.

1. Использование TypeScript

Установите TypeScript:yarn add typescript -D

Создайте файл tsconfig.json для настройки параметров компиляции ts:npx tsc --init

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",                                /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    "module": "esnext",                           /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */

    // 输出的目录
    "outDir": "./types",                              /* Redirect output structure to the directory. */

    /* Strict Type-Checking Options */
    "strict": true,                                 /* Enable all strict type-checking options. */
    "noImplicitAny": false,                       /* Raise error on expressions and declarations with an implied 'any' type. */

    /* Module Resolution Options */
		// 模块的解析策略
    "moduleResolution": "node",                  /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    // "allowSyntheticDefaultImports": true,        /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
    "esModuleInterop": true,                        /* Enables emit interoperability between CommonJS and ES Modules via creation

    /* Advanced Options */
    "skipLibCheck": true,                           /* Skip type checking of declaration files. */
    "forceConsistentCasingInFileNames": true,        /* Disallow inconsistently-cased references to the same file. */

    // 只生成类型文件,不转换代码
    "declaration": true,
    "emitDeclarationOnly": true,
  },
  // 只编译 src 目录下的文件
  "include": [
    "src"
  ],
  "exclude": [
    "test"
  ]
}

2. Напишите исходный код и экспортируйте

Напишите исходный код на TypeScript в каталоге src и вsrc/index.tsЭкспортируйте необходимый контент в

3. Добавьте ESLint для канонического исходного кода

Для кода библиотеки лучше всего иметь единую спецификацию кода, использовать ли пробелы в некоторых местах, или не использовать точки с запятой в конце строк и т.д. Итак, нам нужен ESLint для проверки нашего кода.

Установите ESLint:yarn add eslint -D 

Сгенерировать файл конфигурации:npx eslint --init

Новый в то же время.eslintignoreРазрешить eslint не проверять определенные файлы со следующим содержимым:

test/**
lib/**
types/**

4. Упаковка с роллапом

В критический момент наш код совместим с ESM и написан TS. Мы собираемся упаковать его для прямого использования Node или браузером, чтобы мы могли свернуть код.

Установить накопительный пакет:yarn add rollup -D

новыйrollup.config.jsКонфигурационный файл:

import typescript from '@rollup/plugin-typescript';  // 让 rollup 认识 ts 的代码
import pkg from './package.json';

// 为了将引入的 npm 包,也打包进最终结果中
import resolve from 'rollup-plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';

// 一段自定义的内容,以下内容会添加到打包结果中
const footer = `
if(typeof window !== 'undefined') {
  window._Dry_VERSION_ = '${pkg.version}'
}`

export default {
  input: './packages/index.ts',
  output: [
    {
      file: pkg.main,
      format: 'cjs',
      footer,
    },
    {
      file: pkg.module,
      format: 'esm',
      footer,
    },
    {
      file: pkg.browser,
      format: 'umd',
      name: 'Dry',
      footer,
    },
  ],
  plugins: [
    typescript(),
    commonjs(),
    resolve()
  ]
}

Добавьте команды скриптов в файл package.json: build и build:types для пакетов пакетов и файлов типов типов:

package.json

{
  "name": "@songjp/dry",
  "version": "0.0.1",
  "description": "",
  "main": "lib/bundle.cjs.js",
  "module": "lib/bundle.esm.js",
  "browser": "lib/bundle.browser.js",
  "types": "types/index.d.ts",
  "scripts": {
    // 打包出 cjs, esm, 和 umd 的包
    "build": "rollup -c",
    // 打包出类型文件
    "build:types": "tsc"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@rollup/plugin-commonjs": "^18.0.0",
    "@rollup/plugin-typescript": "^8.2.1",
    "rollup": "^2.45.1",
    "rollup-plugin-node-resolve": "^5.2.0",
    "typescript": "^4.2.4"
  },
  "dependencies": {
    // 一个单纯用于测试的, 第三方 npm 包
    "dayjs": "^1.10.4"
  }
}

Выполните сборку пряжи и сборку пряжи: типы, чтобы увидеть результаты упаковки.

5. Тестируйте код в шутку

Если не будет тест-кейса, я не рискну в дальнейшем менять код этой библиотеки, а вдруг изменение сломается? Итак, нам нужно добавить тестовые примеры в код.

Мы используемts-jestЧтобы протестировать код:yarn add jest ts-jest @types/jest -D

Создайте файл конфигурации jest:npx ts-jest config:init

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

package.json

{
  "scripts": {
    "test": "jest",
  }
}

Создайте новый тестовый каталог в корневом каталоге для хранения тестовых файлов, напримерtest/Queue.ts:

import { Queue } from '../packages/index';

describe('Queue', () => {
  test('Queue Methods', () => {
    const queue = new Queue()
    expect(queue.isEmpty()).toBe(true)
    queue.enqueue('john')
    queue.enqueue('jack')
    expect(queue.toString()).toBe('john,jack')
    queue.enqueue('camila')
    expect(queue.toString()).toBe('john,jack,camila')
    expect(queue.size()).toBe(3)
    expect(queue.isEmpty()).toBe(false)
    queue.dequeue()
    queue.dequeue()
    expect(queue.toString()).toBe('camila')
  });
});

Выполнение теста пряжи, чтобы увидеть тестовый пример

6. Документация с VuePress

Библиотека предоставляется для использования другими, и лучше иметь документацию, объясняющую, какие API-интерфейсы предоставляются библиотекой, как использовать эти API-интерфейсы и т. д. Итак, нам нужно создать сайт документации для этой библиотеки. мы выбралиVuePressЧтобы создать документацию, мы развернем документацию на Github Pages позже.

7. Настройте фиксацию спецификации хаски

Отправляя код в библиотеку, мы надеемся, что отправленный код «хорош», и мы надеемся, что фиксация может быть выполнена только после того, как пройдены и lint, и тест.

Кроме того, мы также можем стандартизировать формат сообщения о фиксации и требовать более значимого сообщения о фиксации перед фиксацией, что облегчает последующую генерацию CHANGELOG с помощью скриптов.Подробнее см.:conventional-changelog-cli

Поэтому мы можем использовать хаски для нормализации коммитов. Посмотреть деталиофициальная документация

В процессе использования первых двух дней я обнаружил, что способ использования хаски изменился. то есть у хаски естьОбновления для версий v4 - v6, исходное использование заключается в добавлении следующего содержимого в package.json:

{
  "hooks": {
    "pre-commit": "npm test && npm run foo"
  }
}

В новой версии воспользуйтесь новой конфигурацией gitcore.hooksPath, что меняет способ использования хаски. Сначала добавьте сценарии подготовки в package.json:

npm set-script prepare "husky install" && npm run prepare

Затем добавьте крючок:

npx husky add .husky/pre-commit "这里是你需要执行的命令,比如 npm test && npm run foo"

Уведомление:Каждый раз, когда вы удаляете каталог .git, вам нужно выполнить его снова.yarn prepare, эта команда изменяет git в текущем репозиторииcore.hooksPathКонфигурацию конкретной конфигурации можно посмотреть в файле .git/config:image.png

8. Настройте действия Github

Если вы не знакомы с действием GitHub, вы можете посмотреть сначалаДокументация по действиям на GitHub.

Короче говоря, Github Action может помочь вам автоматизировать некоторые вещи, например автоматическую проверку кода каждый раз, когда вы отправляете код, или автоматическую публикацию кода в репозиторий npm и развертывание документации каждый раз, когда вы помечаете его.

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

name: dry

on:
  push:
    tags: 
      - '*'           # Push events to every tag not containing /
  pull_request:
    branches:
      - main
  # 或者手动触发
  workflow_dispatch:       # 设置手动触发,参考文档 https://github.blog/changelog/2020-07-06-github-actions-manual-triggers-with-workflow_dispatch/

jobs:
  build:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v2   # 将我们提交的代码 checkout (拷贝) 一份出来
        with:
          persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal token
          fetch-depth: 0 # otherwise, you will failed to push refs to dest repo

      - uses: actions/setup-node@v1  # 会建立 node 环境,便于我们执行 node 脚本
      - uses: actions/cache@v2
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-
      - name: check code   # 检查代码
        run: |
          npm install
          npm run lint
          npm run test

      - run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
      - name: publish                          # 发布 npm 包
        if: ${{contains(github.ref, 'refs/tags/')}}  # 如果有新 tag
        run: |
          npm run build
          npm run build:types
          npm run docs:build
          npm publish --access public
      - name: deploy                          # 发布文档
        if: ${{contains(github.ref, 'refs/tags/')}}
        uses: JamesIves/github-pages-deploy-action@4.1.1
        with:
          branch: gh-pages # The branch the action should deploy to.
          folder: docs/.vuepress/dist # The folder the action should deploy.

Приведенная выше конфигурация в основном предназначена для того, чтобы Github Action помогал нам запускать lint и тестировать, и когда мыкогда тег нажимается( Напримерgit push origin --tags, что означает, что мы хотим выпустить новую версию), затем помогите нам отправить пакет в npm и развернуть последнюю документацию.

Уведомление:В файле конфигурации используется такая переменная, как secrets.NPM_TOKEN, которая является значением токена веб-сайта npm.С этим токеном его можно опубликовать. Вы можете выполнить логин npm локально, после успешного входа пройтиcat ~/.npmrcПроверьте это, затем установите значение токена в репозиторий Github.

9. Как опубликовать

После того, как мы написали код, если нам нужно выпустить новую версию, нам потребуются следующие шаги:

# 打 tag
npm version patch/minor/maior

# 推代码
git push

# 推 tag 触发 CI 执行发布
git push origin --tags

Этот шаг также можно оптимизировать, например, написав сценарий для выполнения всех шагов. использоватьInquirer.jsЭтот интерактивный метод командной строки, такой как выбор того, является ли новая версия патчем, младшим или основным через командную строку, после получения всех ответов, наконец, выполняется в сценарии.git pushоперация по .

10. Как использовать

После того, как наш пакет опубликован, мы можем использовать его следующими способами:yarn add @songjp/dry 

// 方式1: import
import { Queue } from '@songjp/dry'
console.log(new Queue().isEmpty())

// 方式2: require 的方式,适用于 NodeJS
const { Queue } = require('@songjp/dry')
console.log(new Queue().isEmpty())

// 方式3: 在 HTML 文件中使用 script 标签加载,此时会在 window 上挂载一个 Dry 的变量,比如
<script src="node_modules/@songjp/dry/lib/bundle.browser.js"></script>
<script>
  console.log(new Dry.Queue().isEmpty())
</script>

также доступен вyes1am.github.io/dry/Ознакомьтесь с документацией, которую мы написали, чтобы узнать, как работает @songjp/dry.

Эпилог

Если вы найдете это полезным, вы можетезаказать звездуС поддержкой, пожалуйста, укажите на любые ошибки и приветствуйте комментарии и обмены, если у вас есть какие-либо вопросы.