Vue3 + vite + TypeScriptで作ったコンポーネントをnpmに公開してみる

はじめてのquita記事作成となります、つくだにと申します。
転職活動にあたってのポートフォリオ作成の中でviteにライブラリモードなるものがあることを知り、そちらを利用して作成したVue3のコンポーネントライブラリをnpmにアップロードしたので、一連の作業を備忘録的に記事にしてみようと思います。

## プロジェクトの作成

```bash:npm
npm create vite@latest
```

```bash:yarn
yarn create vite
```

コマンドを入力したら、あとは指示に従って項目を選ぶだけです。
今回はVue.jsとTypeScriptを選択します。
```
public
src
 ├ asset
 ├ compornents
 │ └ HelloWorld.vue
 ├ App.vue
 ├ main.ts
 ├ style.css
 └ vite-env.d.ts
.gitignore
index.html
package.json
README.md
tsconfig.json
tsconfig.node.json
vite.config.ts
```

こんな感じのディレクトリ構成になるかと思います。
あとは

```bash:npm
npm install
```

```bash:yarn
yarn install
```

パッケージをインストールすればプロジェクトの作成は完了です。

## コンポーネントの作成

compornents内にライブラリにしたいコンポーネントを作成します。
今回は適当に文字列を表示するコンポーネントにしておきます。

```vuejs:TextItem.vue
<script setup lang="ts">
defineProps<{ msg: string }>();
</script>

<template>
  <h1>{{ msg }}</h1>
</template>
```

出来たらとりあえず一回動かしてみます。
```diff_vue:App.vue
<script setup lang="ts">
<script setup lang="ts">
+ import TextItem from "./components/TextItem.vue";
</script>

<template>
  <div>
    <a href="https://vitejs.dev" target="_blank">
      <img src="/vite.svg" class="logo" alt="Vite logo" />
    </a>
    <a href="https://vuejs.org/" target="_blank">
      <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
    </a>
  </div>
+  <TextItem msg="compornent test" />
</template>
</script>
```

```bash:yarn
yarn dev
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953799/fa8f7772-3ed4-ff6b-6a3c-ca437a7a4480.png)
ちゃんとコンポーネントが出来ていますね。

## ライブラリモードの設定
では、ここから作成したコンポーネントをライブラリにしていきます。
まず、vite.config.tsのビルド設定でライブラリモードの設定を行います。

```diff_typescript:vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
+ import { resolve } from 'path'

export default defineConfig({
+    build: {
+        lib: {
+            entry: resolve(__dirname, 'src/main.ts'), // エントリポイント
+            name: 'text-item-kun', // ライブラリの変数名
+            fileName: "text-item-kun-lib", // 生成するファイル名
+            formats: ['es', 'umd'], // モジュール形式
+        },
+        rollupOptions: {
+            // バンドルしてほしくないパッケージを外部化
+            external: ['vue'],
+            output: {
+                // 外部化したパッケージをビルド時に参照する設定
+                globals: {
+                    vue: 'Vue',
+                },
+            },
+        },
+    },
    plugins: [vue()],
})
```

ここで設定したnameがライブラリとして呼び出す際の変数名、fileNameがnode_modules内でのファイル名になります。

## package.json の設定
次に、package.json の設定を行います。
```diff_json:package.json
{
...
+  "name": "text-item-kun",
+  "files": [
+    "/dist"
+  ],
+  "main": "./dist/text-item-kun-lib.umd.js",
+  "module": "./dist/text-item-kun-lib.js",
+  "exports": {
+    ".": {
+      "import": "./dist/text-item-kun-lib.js",
+      "require": "./dist/text-item-kun-lib.umd.js"
+    }
+  },
}
```

scriptsはいったん以下のようにします。
```diff_json:package.json
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "watch": "vite build --watch",
    "prepare": "npm run build"
  },
```

## 型定義ファイルの設定
viteのデフォルト設定だと型定義ファイルが生成されないので、typescriptに対応するためにこれを変更します。

```diff_json:tsconfig.json
{
...
  "compilerOptions": {
-    "noEmit": true,

+    "emitDeclarationOnly": true,
+    "declaration": true,
+    "declarationMap": true,
+    "declarationDir": "./dist",
  },
...
}
```

```diff_json:package.json
{
...
  "files": [
    "/dist"
  ],
  "main": "./dist/text-item-kun.umd.js",
  "module": "./dist/text-item-kun.js",
+  "types": "./dist/main.d.ts",
}
```


こちらの場合、scriptsはこのようにします。
```diff_json:package.json
  "scripts": {
    "dev": "vite",
    "build": "vite build && yarn run generate",
    "generate": "vue-tsc --declaration --emitDeclarationOnly --outdir ./dist",
    "preview": "vite preview",
    "watch": "concurrently \"vue-tsc --watch\" \"vite build --watch\"",
    "prepare": "npm run build"
  },
```

## ビルドしてみる

ビルドしてライブラリを作成するために、先ほどvite.config.tsでエントリポイントとして設定したmain.tsの内容を変更します。

```diff_typescript:main.ts
// import { createApp } from 'vue'
// import './style.css'
// import App from './App.vue'

// createApp(App).mount('#app')

import TextItem from "./components/TextItem.vue";
export { TextItem };
```
このファイル上でエクスポートされている要素がライブラリになります。
createApp(App)等のコードは作成したライブラリの動作テスト時に使うので、いったんコメントアウトしておきましょう。

ここまで来たら、ビルドしてみます。

```bash:yarn
yarn build
```

```
dist
 ├ public
 │ └ vite.svg
 ├ compornents
 │ ├ TextItem.vue.d.ts
 │ └ TextItem.vue.d.ts.map
 ├ App.vue.d.ts
 ├ App.vue.d.ts.map
 ├ main.d.ts
 ├ main.d.ts.map
 ├ text-item-kun-lib.js
 └ text-item-kun-lib.umd.cjs
```
distというファイルが作成され、ライブラリがビルドされました。
これがライブラリのファイルとなります。

しかし、プロジェクト作成時に初期生成されたviteの画像ファイルが入ってしまいました。ので、プロジェクトの方からpublicフォルダを削除します。

また、型定義ファイル（.d.tsと.d.ts.map）がmain.tsとApp.vueの2つ分生成されていますが、このうちApp.vueはvueのデバッグ時に使っているだけなのでライブラリに含める必要はありません。
tsconfig.jsonにてincludeの対象にvueファイルも含まれていることによるものなので、いったん削除します。
（こうした場合、エディタ上でApp.vueファイルを認識できなくなってしまいエラーが出ます。デバッグやビルドをするにあたって支障はありませんが、もっといい方法があればご教授ください。）

```diff_json:tsconfig.json
{
...
-  "include": ["src/main.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
+  "include": ["src/main.ts", "src/**/*.d.ts", "src/**/*.tsx"],
...
}
```

この状態でもう一度ビルドしなおせば、無駄なファイルのないライブラリになっているはずです。
```
dist
 ├ compornent
 │ ├ TextItem.vue.d.ts
 │ └ TextItem.vue.d.ts.map
 ├ main.d.ts
 ├ main.d.ts.map
 ├ text-item-kun-lib.js
 └ text-item-kun-lib.umd.cjs
```

## ライブラリの公開設定

ライブラリは完成したので、今度はnpmで公開する準備をしていきます。

まずは、npmで公開するにあたっての設定をpackage.jsonに記入していきます。
ビルドを行う前の設定で、公開するファイルやエントリポイント等の必須項目は記入済みです。
package.jsonではさらに、ライセンスや作成者名、GitのリポジトリURL等を設定できます。
また、"private"がtrueになっているとnpmへ公開ができないため、そうなっていたらfalseへ変えておきましょう。

```diff_json:package.json
{
 ...
-  "private": true,
+  "private": false,
 ...
+  "author": "hoge_hogetarou", // 作成者前
+  "license": "MIT", // ライセンス
+  "repository": {
+    "type": "git", // リポジトリ
+    "url": "https://github.com/..." // リポジトリのURL
+  },
+  "keywords": [ // キーワード 検索の際に参照
+        "Vue.js"
+  ],
+  "bugs": { // バグの報告先
+      "url": "https://github.com/.../text-item-kun/issues"
+  },
+  "homepage": "https://..." // 作成者のホームページ
 ...
}
```

ライブラリのライセンスは様々なものがありますが、「商業利用含め基本的に制限は設けないよ」ということであれば基本的にMITでいいと思います。（私もよくわかっていないですが）

## ライブラリの動作確認

npmに公開する前に、作成したライブラリが正常に動作しているかどうかを確認する必要があります。
npmのnpm linkコマンドを使うことで簡単にテストできます。
ライブラリを作成しているディレクトリのコンソールで

```bash
npm link
```

を実行し、続けて

```bash
npm link text-item-kun
```

を実行します。
これで、今回作成したライブラリをnode_modulesファイル内にインストールすることが出来ました。

この状態で、先ほどテスト用にコメントアウトで残していたmain.tsを元に戻し、
```diff_typescript:main.ts
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'

createApp(App).mount('#app')

// import TextItem from "./components/TextItem.vue";
// export { TextItem };
```

試しに動かしていたApp.vueのインポートをライブラリからに切り替えてみます。
先ほど削除した画像のimg要素も忘れず消しましょう。

```diff_vue:App.vue
<script setup lang="ts">
<script setup lang="ts">
- import TextItem from "./components/TextItem.vue";
+ import { TextItem } from "text-item-kun";
</script>

<template>
  <div>
-    <a href="https://vitejs.dev" target="_blank">
-      <img src="/vite.svg" class="logo" alt="Vite logo" />
-    </a>
-    <a href="https://vuejs.org/" target="_blank">
-      <img src="./assets/vue.svg" class="logo vue" alt="Vue logo" />
-    </a>
  </div>  
<TextItem msg="compornent test" />
</template>
</script>
```

この状態でデバッグしてみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953799/936267b1-80c4-c539-d29f-de88373537a1.png)

ライブラリからコンポーネントをきちんと取得できていることが確認できました。
確認できたら、main.tsとApp.vueを元に戻しておきましょう。（次に行う、npmへの公開でもう一度ビルドを行うためです。）
```diff_typescript:main.ts
// import { createApp } from 'vue'
// import './style.css'
// import App from './App.vue'

createApp(App).mount('#app')

import TextItem from "./components/TextItem.vue";
export { TextItem };
```

```diff_vue:App.vue
<script setup lang="ts">
<script setup lang="ts">
import TextItem from "./components/TextItem.vue";
// import { TextItem } from "text-item-kun";
...
```

## npmに公開してみる
ここまできたら、次はついに作成したライブラリをnpmに公開します。
まず、npmにアカウントがない場合はアカウントを作成する必要があります。
https://www.npmjs.com/signup
公式ページから新規登録をしましょう。
登録が済んだら、ライブラリを作成したディレクトリで

```bash
npm login
```
を実行し、コンソール上でログインしてください。ログイン出来たら、
```bash
npm publish
```
を実行してください。
エラーなく完了出来たらパッケージの公開完了です！
作成したパッケージはnpm公式サイトのマイページから確認することができます。


## npmに公開したパッケージをインストールしてみる

では、最後に公開したパッケージをnpmからダウンロードし、動作確認をしてみましょう。
先ほどnpm linkを使ってテストした環境を元に戻す場合、

```bash:npm
npm install
```
```bash:yarn
yarn install
```
で戻せます。
戻したらインストールコマンドを実行し、もう一度デバッグしてみましょう。
```bash:yarn
yarn add text-item-kun
```

npm linkを使ってのテストと同様にコンポーネントが表示されれば、ライブラリの完成です！

## 終わりに

長くなってしまいましたが、最後までお読みいただきありがとうございます。
viteのライブラリモードを使えば、私のような初学者でも簡単にライブラリを作成することができました。
勉強も兼ねて自分のライブラリを公開してみたい・・・という方の助けになれば幸いです。

この記事について「こっちのほうがいいぞ！」「この記述はまずいだろ！」等ありましたらコメントでお願いいたします。