🌟はじめに
本記事はプロもくチャット Adevent Calendar2023の5日目です
🤔 なんで必要なの?
🔥 Firestoreを使っている
僕が所属しているプロダクトでは、下記のような構成を取っています
- フロントエンド: Vue3/TypeScript
- バックエンド: Node.js/TypeScript
- インフラ: Firebase/GCP
📚 Firestoreはカスタムオブジェクト経由でRead/Writeできる
DBは基本的にはFirestore、一部CloudSQLを使っています。
フロントエンドから直接Firestoreの読み書きする部分が多く、随所でバックエンド経由でFirestoreへアクセスするため、フロントエンド・バックエンドともに同じドキュメントやコレクションにアクセスするのが多いです。
FirestoreはNoSQLのため基本的にはスキーマがありませんが、下記のようにモデルクラスと変換メソッド(toFirestore, fromFirestore)を定義することでカスタムオブジェクトを使って保存・読み取りすることができます。
https://firebase.google.com/docs/firestore/query-data/get-data?hl=ja#custom_objects
※このあたりの解説は今回は割愛します
🌐 共有したい、でも普通にnpmパッケージ化すると全世界に公開されてしまう…
上記のような状態なので、モデルクラスや変換メソッドをフロントエンド・バックエンドで共有する必要が出てきました。
別リポジトリをシンボリックリンク化してデプロイしてゴニョゴニョ…してましたが、デプロイが遅くCIのコードもかなり複雑化するため、npmパッケージ化したいと考えました。
が、npmパッケージは全世界に公開されてしまうため、ちょっと微妙…
そこでGithubPackagesを使ってプライベートなnpmパッケージを作成しそれらをVue, Node.jsそれぞれでnpm installして使うようにします。
🎯 ゴール
- モデルクラス・変換メソッドをGithubのプライベートリポジトリに作成する
- プライベートリポジトリのコードをGithubPackagesにパッケージを公開する
- 公開されたパッケージをVue3/Node.jsそれぞれで
npm install XXX@YY.YY.YYのような形で利用することができる
📝 本題
🛠️ モデルクラスをGithubのプライベートリポジトリに作成する
- さくっと共有したいリポジトリを作成します。
npm init -y npm install typescript -D npx tsc --init -
.gitignoreを作成(最低限しか入れてません)node_modules .DS_Store Thumbs.db dist/**/* - 共有したいコードを作る
src/index.ts
export class City { constructor( readonly name: string, readonly state: string, readonly country: string ) {} static toFirestore(city: City) { return { name: city.name, state: city.state, country: city.country, }; } } -
tsconfig.jsonを設定
僕の担当するプロダクトではNode.jsはCommonJS、VueはESModuleで動いているため、それぞれの形式でビルドする必要があります。このあたりの詳しい話は割愛します。
下記のような形でtsconfigをCommonJS用、ESModule用に作成します。(ベースとなるtsconfigはデフォルトのままです)tsconfig.cjs.json{ "extends": "./tsconfig.json", "compilerOptions": { "module": "CommonJS", "outDir": "./dist/cjs" }, "include": ["src/**/*"], "exclude": ["node_modules"] }tsconfig.esm.json{ "extends": "./tsconfig.json", "compilerOptions": { "module": "esnext", "outDir": "./dist/esm" }, "include": ["src/**/*"], "exclude": ["node_modules"] } -
npm scriptでビルドできるようにするpackage.json{ ... "scripts": { "build:esm": "tsc -d -p tsconfig.esm.json", "build:cjs": "tsc -d -p tsconfig.cjs.json" }, ... } -
npm run build:esm,npm run build:cjsで、distディレクトリに出力されることを確認 - Common.js, ESModule用にそれぞれエンドポイントを定義
package.json
{ ... "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "types": "dist/esm/index.d.ts", ... }
ここまでで、ファイル構成は下記のようになっていると思います
private-common % tree
.
├── dist
│ ├── cjs
│ │ ├── index.d.ts
│ │ └── index.js
│ └── esm
│ ├── index.d.ts
│ └── index.js
├── node_modules
├── package-lock.json
├── package.json
├── src
│ └── index.ts
├── tsconfig.cjs.json
├── tsconfig.esm.json
└── tsconfig.json
📦 プライベートリポジトリのコードをGithubPackagesにパッケージを公開する
-
PATを作成
- プライベートなパッケージの作成・インストールをするには、GithubのPersonal Access Token(PAT)を作成し、ローカルに配置する必要があります。
作り方はこのあたりを参照してください
https://docs.github.com/ja/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token -
write:packages権限をつけてあげてください
- プライベートなパッケージの作成・インストールをするには、GithubのPersonal Access Token(PAT)を作成し、ローカルに配置する必要があります。
-
パッケージ名を定義
プライベートとして発行するにはGithub名 or Oraganization名/リポジトリ名とする必要があります。これを下記のようにpackage.jsonに定義します。package.json{ "name": "@IkumaHayashi/private-common", ... } -
.npmrcファイルを作成
Github Packagesは、通常のnpmレジストリとは別の場所(https://npm.pkg.github.com)に公開されます。
このレジストリにプライベートなパッケージを公開するため、下記2点の情報をnpmコマンドに伝えてあげる必要があります- レジストリの場所(
https://npm.pkg.github.comのこと) - プライベートなパッケージをインストールするための秘密鍵(PATのこと)
この2点が載っているファイルを
.npmrcとして作成することで、npmが自動的にこのファイルの情報を読み取り、パッケージの公開やインストール時に使用します。
ただし、PATはリポジトリ管理するわけにはいきません。そこで、プロジェクト内ではレジストリの場所を、各開発者のホームディレクトリにはPATを配置します。
下記のようにファイルを作成します。プロジェクトルート/.npmrc@あなたのGithub名もしくはOraganization名:registry=https://npm.pkg.github.com~/.npmrc//npm.pkg.github.com/:_authToken=PAT - レジストリの場所(
-
.npmignoreを空ファイルで作成-
.npmignoreはパッケージを発行する際にどのファイルを無視するかを指定するファイルです。 - npm v9から、
.npmignoreがない場合暗黙的に.gitignoreが使用されます。今回の構成ではdistディレクトリをパッケージとして発行したいため、このままではsrcディレクトリなどしか発行されず、使うことができません。 - そのため空の
.npmignoreを定義して除外するファイルをなくすことで、distディレクトリを含めてあげます。
-
-
発行
パッケージの発行はnpm publishで行えます。--dry-runをつけると、発行前にどういう内容で発行されるか確認することができます。npm notice npm notice 📦 @IkumaHayashi/private-common@0.0.1 npm notice === Tarball Contents === npm notice 293B dist/cjs/index.d.ts npm notice 428B dist/cjs/index.js npm notice 293B dist/esm/index.d.ts npm notice 314B dist/esm/index.js npm notice 599B package.json npm notice 264B src/index.ts npm notice 174B tsconfig.cjs.json npm notice 172B tsconfig.esm.json npm notice 12.3kB tsconfig.json npm notice === Tarball Details === npm notice name: @IkumaHayashi/private-common npm notice version: 0.0.1 npm notice filename: @IkumaHayashi/private-common-0.0.1.tgz npm notice package size: 4.5 kB npm notice unpacked size: 14.8 kB npm notice shasum: 87328bb7206b4661afce03868d8e888ded15194a npm notice integrity: sha512-B3FeOLAppTO3w[...]cb/qtu6kFfrUg== npm notice total files: 9 npm notice npm notice Publishing to https://npm.pkg.github.com (dry-run) + @IkumaHayashi/private-common@0.0.1チェックすべきは下記の2点です
- パブリッシュ先が
https://npm.pkg.github.comになっている - 発行されるファイル(
Tarball Contents)にdistディレクトリが含まれている
上記が問題なければ
npm publishで実際に発行します。private-common % npm publish npm notice npm notice 📦 @IkumaHayashi/private-common@0.0.1 npm notice === Tarball Contents === ... npm notice Publishing to https://npm.pkg.github.com + @IkumaHayashi/private-common@0.0.1問題なく発行されました
- パブリッシュ先が
発行されるとこんな感じでGithubのリポジトリに表示されます

🚀 インストールしてみる
- 使用するリポジトリに
.npmrcファイルを作成プロジェクトルート/.npmrc@あなたのGithub名もしくはOraganization名:registry=https://npm.pkg.github.com npm install @IkumaHayashi/private-common@0.0.1
💡 補足
🤖 Github Actionsでnpm installするとコケる😭
GithubのPATがないことが原因だと思います。ただ、GithubActionsではルートディレクトリにファイルを作成することができません。
そんなときは、npm installするステップで環境変数にNODE_AUTH_TOKENにPATを定義してあげるとインストールできます
- name: 依存関係をインストール
run: |-
npm install
env:
NODE_AUTH_TOKEN: ${{ secrets.PAT }}
🙏 最後に
ここまで読んでくださりありがとうござました!
ちょっと時間がなくてインストール部分は確認できておりません… ![]()
コメントいただければ確認・修正行いますのでお気軽にコメントしてください🙆♂️