Shogo's Blog

Jun 23, 2024 - 3 minute read - typescript

npmとjsrにパッケージを公開してみた (認証バッジ付)

先日、同時実行数を制限しながら並行実行する関数を書きました。

便利関数を作ったら他のプロジェクトから参照したいですよね。 そこでパッケージレジストリに登録してみました。

正直コピペで実装で十分なのでは?という分量ですが、パッケージ公開の練習です。

ソースコードを準備する

まずは公開するソースコードを準備していきましょう。 ソースコードはGitHubで公開しました。

Denoの開発環境を整える

TypeScriptの開発環境を整えたいのですが、Node+TypeScriptの組み合わせはプロジェクトの立ち上げは意外と面倒です。 そこで今回は Deno を使ってみることにしました。 DenoはTypeScriptの実行ランタイムとして開発されており、特別な設定なしでTypeScriptを実行できます。 Brewでインストールしました。

brew install deno

僕は最近エディターには VS Code を使っているので、Deno用の拡張機能をインストールしました。

インストールしただけでは有効化されません。 ワークスペースの設定ファイル .vscode/settings.json を編集して、明示的に有効化します。

{
  "deno.enable": true,
  "deno.lint": true,
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "denoland.vscode-deno",
  "[typescript]": {
    "editor.defaultFormatter": "denoland.vscode-deno"
  },
}

開発環境が整ったらパッケージ本体のソースコードを書いていきます。

GitHub Actionsでテストを実行する

パッケージとして公開するのであれば、テストを書いて、CIを回しておきたいですよね。 Deno公式がセットアップするためのアクションを公開しているので、これを利用します。

あとは deno test コマンドを実行するだけです。

on:
  push:
  pull_request:

jobs:
  deno:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: denoland/setup-deno@v1
        with:
          deno-version: v1.x
      - name: test
        run: |
          deno test          

カバレッジを計測する

--coverage オプションをつけてテストを実行すると coverage ディレクトリにカバレッジが出力されます。 カバレッジはJSONファイルで出力されるのですが、これは Deno 独自のものらしく、そのままでは他のサービスと連携できません。 以下のコマンドで、一般的な lcov 形式に変換できます。

deno coverage --lcov > coverage.lcov

カバレッジは Codecov にアップロードすることにしました。

テスト関連のワークフローをまとめると、次のようになります。

on:
  push:
  pull_request:

jobs:
  deno:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: denoland/setup-deno@v1
        with:
          deno-version: v1.x
      - name: test
        run: |
          deno test --coverage
          deno coverage --lcov > coverage.lcov          
      - name: upload coverage
        uses: codecov/codecov-action@v4
        with:
          file: coverage.lcov
          token: ${{ secrets.CODECOV_TOKEN }}

JSRにパッケージを公開する

今回は Deno のパッケージとして作成したので、 JSR に公開するのがお手軽です。 ここにアップロードしてみましょう。

Denoのパッケージを作成する

JSRはGitHubアカウントでサインアップ可能です。 サインアップしたら jsr.io/new からパッケージを作成しましょう。

Denoのパッケージをアップロードする

パッケージのメタ情報を deno.json に記載します。

{
  "name": "@shogo82148/limit-concurrency",
  "version": "0.1.6",
  "exports": {
    ".": "./limit-concurrency.ts"
  }
}

あとは deno publish を実行するだけです。

deno publish

ここでブラウザーが起動するので、案内にしたがって公開許可を行います。


一度ログインしていればパスワードの入力は必要ありません。 今まで .hogerc にパスワードを書く方式しか知らなかったので、「時代は進化しているんだなぁ」と感動しました。

npmにパッケージを公開する

新興のレジストリも登場してきましたが、JavaScriptのレジストリと言ったら今も npm でしょう。 そういうわけで、 npm にも登録してみます。

Deno のパッケージを npm パッケージに変換する

npmはあくまでもJavaScriptのレジストリなので、TypeScriptのモジュールをアップロードするにはビルドが必要です。 denoland/dnt を使ってビルドしてみました。

dnt用の設定ファイルを用意します。

// ex. scripts/build_npm.ts
import { build, emptyDir } from "@deno/dnt";

await emptyDir("./npm");

await build({
  entryPoints: ["./limit-concurrency.ts"],
  outDir: "./npm",
  shims: {
    deno: true,
  },
  package: {
    // package.json properties
    name: "@shogo82148/limit-concurrency",
    version: Deno.args[0],
    description: "Limit the concurrency of tasks.",
    license: "MIT",
    repository: {
      type: "git",
      url: "git+https://github.com/shogo82148/limit-concurrency.git",
    },
    bugs: {
      url: "https://github.com/shogo82148/limit-concurrency/issues",
    },
  },
  postBuild() {
    // steps to run after building and before running the tests
    Deno.copyFileSync("LICENSE", "npm/LICENSE");
    Deno.copyFileSync("README.md", "npm/README.md");
  },
});

このスクリプトを実行すると npm/ ディレクトリに npm パッケージが出力されます。

deno run -A scripts/build_npm.ts 0.1.0

npmのパッケージをアップロードする

あとは npm publish を実行するだけです。

cd npm
npm publish --access public

ブラウザーが起動するので、案内にしたがって公開許可を行います。

GitHub Actionsでリリースを自動化する

せっかくなのでリリースも自動化しましょう。 タグを打ったら、自動的にJSRとnpmに公開するよう設定します。

GitHub Actionsでリリースを自動化する利点

もちろん、リリースの手間が減るというメリットはありますが、それだけではありません。

パッケージのページに

Built and signed on GitHub Actions

と書かれた バッジを表示できます

なんかカッコイイ!欲しい!

npmのアクセストークンを取得

npmの場合、公開のためにアクセストークンが必要です。 npmの設定から Access Token を発行し、GitHub の Secretsに保存しておきましょう。

GitHubからJSRへのアクセス許可を設定

JSRの場合、アクセストークンは不要です。 代わりに GitHub Actions からのアクセス許可が必要です。 パッケージの設定から許可を出しておきます。

GitHub Actions のOIDCを有効化する

パッケージのリリースにはOIDCが有効化されている必要があります。 リリースワークフローに以下の行を追加します。

permissions:
  contents: read
  id-token: write

publishスクリプトを書く

あとは GitHub Actions ワークフローの中で deno publish && npm publish するだけです。

#!/bin/bash

ROOT=$(git rev-parse --show-toplevel)

set -euxo pipefail
cd "$ROOT"

# publish to jsr
deno publish

# npm publish
deno run -A scripts/build_npm.ts "$(jq -r .version deno.json)"
cd npm
npm publish --access public --provenance ## --provenance オプションがついているのがポイント

まとめ

npmとjsrに簡単なパッケージを公開してみました。 さらに、リリースフローの一部を GitHub Actions で自動化しました。

皆さんもぜひ「Built and signed on GitHub Actions」のバッジをゲットしてください。

コンピューターの森でラビットが舞う
パッケージを変換、夢中で追う
自動リリースの風に乗り
npmとJSRへ、夢を運ぶ
🌟コードの魔法、今広がる🛤✨

by CodeRabbit

余談

Denoについて調べていたら、Denoの標準ライブラリに pooledMap という関数を見つけました。 AsyncIterableを受け取り、並列度を制限しながら並行実行するというもの。 これはまさに求めていたもの!

まあ、今回はパッケージ公開のお勉強だったのでヨシ!

参考