TypeScriptでライブラリを作成して別アプリから参照して使ってみた

# はじめに

個人で運営しているサービスで、特定の大きな処理を再利用可能なライブラリとして外部化したい欲求に駆られた。
今まで外部化した経験がなかったため、実践経験を積むためにやってみた。

# やりたいこと

- ソースは TypeScript でかけること
- Github Private Repository で管理する
- ライブラリは特定の人物のみがダウンロードできること
- 利用側はNext.jsで利用できること

# 整理

`ライブラリ側` と `利用側` でまとめました。

## ライブラリ側

- PackageManagerは `npm`
- `typescript` は5系
- バンドラーは `rollup.js`
- レジストリは `GitHub Packages` 
- `Github Actions` を使って半自動化

## 利用側

- PackageManagerは `bun`
- `typescript` は5系
- Next.js 14系
- 本番環境は `vercel`

# ライブラリ作成、公開

## ソース以外の成果物

<details><summary>package.json</summary>

```json
{
  "name": "{@user}/repository_name",
  "description": "",
  "version": "0.0.1",
  "repository": {
    "type": "git",
    "url": "git@github.com:{user}/repository_name.git"
  },
  "main": "dist/index.js",
  "module": "dist/index.esm.js",
  "type": "module",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "rollup -c",
    "prepublishOnly": "npm run build"
  },
  "devDependencies": {
    "@rollup/plugin-typescript": "11.1.6",
    "rollup": "4.18.0",
    "rollup-plugin-dts": "6.1.1",
    "tslib": "2.6.3",
    "typescript": "5.5.2"
  },
  "publishConfig": {
    "access": "restricted",
    "registry": "https://npm.pkg.github.com"
  },
  "engines": {
    "node": ">=20.0.0"
  }
}
```

</details>

<details><summary>tsconfig.json</summary>

```json
{
  "compilerOptions": {
    "target": "ES6",
    "module": "esnext",
    "declaration": true,
    "declarationDir": "dist/types",
    "outDir": "dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "removeComments": true
  },
  "include": ["src"]
}
```

</details>

<details><summary>rollup.config.js</summary>

```javascript
import typescript from '@rollup/plugin-typescript'
import dts from 'rollup-plugin-dts'

export default [
  {
    input: './src/index.ts',
    output: [
      {
        file: 'dist/index.js',
        format: 'cjs',
      },
      {
        file: 'dist/index.esm.js',
        format: 'esm',
      },
    ],
    plugins: [typescript()],
  },
  {
    input: 'dist/types/index.d.ts',
    output: [
      {
        file: 'dist/index.d.ts',
        format: 'es',
      },
    ],
    plugins: [dts()],
  },
]
```

</details>

<details><summary>github/workflows/publish-to-private.yaml</summary>

```yaml
name: npm publish

on:
  workflow_dispatch:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4.1.7
      - name: Setup Node
        uses: actions/setup-node@v4.0.2
        with:
          node-version: 20
      - name: install
        run: npm ci
      - name: build
        run: npm run build
  publish-gpr:
    needs: build
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - name: Checkout
        uses: actions/checkout@v4.1.7
      - name: Setup Node
        uses: actions/setup-node@v4.0.2
        with:
          node-version: 20
          registry-url: "https://npm.pkg.github.com"
          scope: "{@user}" // ここにGithubのユーザー名
        env:
          NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - name: install
        run: npm ci
      - name: build
        run: npm run build
      - name: publish
        run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

</details>

## 説明

`npm run build` をすると、 `rollup -c` が動作し、
`./src/index.ts` から、 `dist/index.js` , `dist/index.esm.js` , `dist/types/index.d.ts` が生成されます。
Github Actions では　`npm publish` でレジストリに登録している。

# 利用について

`Github Packages` は、 `bun` の場合、
`bunfig.toml` を利用する。

## ソース以外の成果物

<details><summary>package.json</summary>

```json
{
    "devDependencies": {
        "{@user}/repository_name": "0.0.1",
    }
}
```

</details>

<details><summary>bunfig.toml</summary>

```toml
[install.scopes]
"{@user}" = { token = "$NODE_AUTH_TOKEN", url = "https://npm.pkg.github.com/" }
```

</details>

<details><summary>.env</summary>

```
NODE_AUTH_TOKEN={Github Personal Token}
```

</details>

## Github Personal Token

現在、Tokenは新/旧の2パターンで作成できるが、Permissionがいまいちよくわかっていない。
それっぽいものを指定したらうまく動作したが、「これだ！」というもので生成すると動かなかった。。。

# おわり、宣伝

私の運営している迷子ペット解決サービスで非公開ライブラリを利用しています！
-> つまり、本番環境で動作しています...！

https://qiita.com/mabubu0203/items/93c736a3c5b0f8d855df