Nxで作成したReact ComponentをGitHub Package Registoryで公開する

社内利用などを前提としており、Publicに配布したくないライブラリを作る場合にGitHubのPackage Registoryは有力な公開先の1つです。そして、「1つのデザインシステムをベースに、複数のサービス・アプリ向けの拡張ライブラリを作る」というユースケースに対しては、NxやLernaなどを用いたmonorepoでのソース管理が効率的になることがあります。

この2つを組み合わせたものを、仕事でやる必要が出てきそうだったので試行錯誤していました。

セットアップ

まずはNxでセットアップから

$ npx create-nx-workspace nx-gh-registory --preset react 

スタイリングの方法などいろいろ聞かれますので、作りたいものの要件に合わせて回答しておきましょう。

Pacakgeの作成

続いてライブラリを作ります。--publishableをつける必要があることと、importPathで公開するライブラリの名前を設定する必要がある点に注意しましょう。

% yarn nx g @nrwl/react:library --name=ui --publishable --importPath @hideokamoto/nx-react-ui-example
% yarn nx g @nrwl/react:library --name=greeding --publishable --importPath @hideokamoto/nx-react-greeding-example

package.jsonにregistroy情報を追加する

GitHubのPackage Registoryに公開する場合は、追加した各Packageのpackage.jsonrepositorypublishConfigを追加する必要があります。

{
  "name": "@hideokamoto/nx-react-greeding-example",
  "version": "2.0.0",
  "repository": {
    "type": "git",
    "url": "https://github.com/hideokamoto/nx-react-github-registory"
  },
  "publishConfig": {
    "registry": "https://npm.pkg.github.com/@hideokamoto"
  }
}

repositoryにはGitHubリポジトリのURLを、publishConfig.registoryにはhttps://npm.pkg.github.com/@USERNAMEを指定しましょう。

.npmrcでレジストリの設定を行う

公開したいライブラリのScopeだけGitHubのレジストリを使うように設定しておきましょう。

//npm.pkg.github.com/:_authToken=${GITHUB_ACCESS_TOKEN}
@hideokamoto:registry=https://npm.pkg.github.com
registry=https://registry.npmjs.org
always-auth=false

まとめてnpm publishするスクリプト

最後にnpm publishで公開します。参考にした海外のブログ記事にあるスクリプトが「更新されたものだけ対象にリリースする」挙動をしていましたので、とりあえず今はこれを利用しています。

#!/usr/bin/env bash
set -o errexit -o noclobber -o nounset -o pipefail

# This script uses the parent version as the version to publish a library with

getBuildType() {
  local release_type="minor"
  if [[ "$1" == *"BREAKING CHANGE"* || "$1" == *"!:"* ]]; then
    release_type="major"
  elif [[ "$1" == *"feat"* ]]; then
    release_type="minor"
  elif [[ "$1" == *"fix"* || "$1" == *"docs"* || "$1" == *"chore"* ]]; then
    release_type="patch"
  fi
  echo "$release_type"
}

PARENT_DIR="$PWD"
ROOT_DIR="."
echo "Removing Dist"
rm -rf "${ROOT_DIR:?}/dist"
COMMIT_MESSAGE="$(git log -1 --pretty=format:"%s")"
RELEASE_TYPE=${1:-$(getBuildType "$COMMIT_MESSAGE")}
DRY_RUN=${DRY_RUN:-"False"}

AFFECTED=$(node node_modules/.bin/nx affected:libs --plain --base=origin/master~1)
if [ "$AFFECTED" != "" ]; then
  cd "$PARENT_DIR"
  echo "Copy Environment Files"

  while IFS= read -r -d $' ' lib; do
    echo "Setting version for $lib"
    cd "$PARENT_DIR"
    cd "$ROOT_DIR/libs/${lib/-//}"
    npm version "$RELEASE_TYPE" -f -m "chore: $RELEASE_TYPE"
    echo "Building $lib"
    cd "$PARENT_DIR"
    npm run build "$lib" -- --with-deps
    wait
  done <<<"$AFFECTED " # leave space on end to generate correct output

  cd "$PARENT_DIR"
  git add ./
  git commit -m "chore: $RELEASE_TYPE"

  while IFS= read -r -d $' ' lib; do
    if [ "$DRY_RUN" == "False" ]; then
      echo "Publishing $lib"
      npm publish "$ROOT_DIR/dist/libs/${lib/-//}" # --access=public
    else
      echo "Dry Run, not publishing $lib"
    fi
    wait
  done <<<"$AFFECTED " # leave space on end to generate correct output
else
  echo "No Libraries to publish"
fi

元のスクリプトではnpm versionの結果がcommitされていない状態でしたので、git commitするように少し手を入れています。

このあたりはSemantic Release系のライブラリを使ったり、Nxのプラグイン化したりすることである程度柔軟な使い方ができるのではないかなと思います。

公開の確認

あとはGitHubにアクセスして公開されているかを確認しておきましょう。

注意点

README.mdはプロジェクトルートのものが採用される

これはNxのビルド設定次第みたいですが、デフォルトだとプロジェクトルートにあるREADME.mdがビルドパッケージにバンドルされます。なのでライブラリの使い方などは、libs/PACKAGE/README.mdではなくREADME.mdに記述しましょう。

namespaceはGitHubのUsername / Organization nameのみ

基本的に「そのリポジトリのあるUsername / Organization name」を@NAMESPACEに指定します。

そのため別のnamespaceを利用したい場合は、organizationを作成してリポジトリをtransferする必要がありそうです。

そのほか

やってることは個別のnpm packageをReleaseしているだけですので、理論上NxでAngular / Vue / React / Web Componentをまとめて管理することも可能です。

ですのでIonic Frameworkのように「Web Componentをベースに、Angular / Vue / Reactで使うライブラリもリリースする」という作り方をNxで作ったmonorepoでやることは可能かなと思います。

GitHubのReleaseやCHANGELOG.mdでどのライブラリが更新されたかをわかりやすくする部分が今後の検討事項かなとは思いますが、Nxを使ったライブラリ管理は、知っておくといろいろと捗りそうです。

参考

Comment