viteでパッケージをライブラリに公開する(TypeScript対応)

2021/10/30に公開

vite のLibrary Modeを使うことで簡単にパッケージが作ることができます。

本記事では加えて、TypeScript への対応方法も説明していきます。

vite の設定

まずは、vite.config.js から見ていきましょう。

const path = require("path");
const { defineConfig } = require("vite");

module.exports = defineConfig({
  build: {
    lib: {
      entry: path.resolve(__dirname, "lib/main.js"),
      name: "MyLib",
      fileName: (format) => `my-lib.${format}.js`,
    },
  },
});

build.lib がライブラリモードのオプションになります。説明すると、

entry

作成するバンドルのエントリーポイントを指定します。

このファイルで公開(export)されている関数や変数が公開されます。

name

<script />タグを用いてグローバルにインストールした場合に使用される名前になり、window.MyLibというアクセスが可能になります。(よって、CommonJS や ESM では関係ないです)

fileName

配布ディレクトリ(例:dist)に出力されるファイル名を指定できます。

省略した場合は、package.json の name オプションが使用されます。

出力形式(format)を引数に取る関数を指定すると、フォーマット毎に名前を変えることができます。

format はデフォルトでは es(ESM)と umd(UMD)ですが、rollup のオプションを指定することで、変更することもできます。

React や Vue のパッケージを作る場合

React Hooks 等のパッケージを公開する場合は、追加で設定が必要です。

下記は Vue の例です。

// vite.config.js
const path = require("path");
const { defineConfig } = require("vite");

module.exports = defineConfig({
  build: {
    lib: {
      entry: path.resolve(__dirname, "lib/main.js"),
      name: "MyLib",
      fileName: (format) => `my-lib.${format}.js`,
    },
    rollupOptions: {
      external: ["vue"],
      output: {
        globals: {
          vue: "Vue",
        },
      },
    },
  },
});

external はバンドルしてほしくないパッケージを指定します。

そして、globals を指定することで external で指定したパッケージが UMD ビルド時に<script>タグで読み込まれた際に使用されるべき変数名を設定できます。

ここで指定したパッケージは、peerDependencies として設定すると良いでしょう。

package.json の設定

vite に限った設定ではないのですが、説明します。

package.json の main フィールドに生成したファイルを指定し、パッケージに含める配布フォルダを files フィールドに指定します。

{
  ...
  "files": [
    "/dist"
  ],
  "main": "./dist/my-lib.umd.js",
  "module": "./dist/my-lib.es.js",
  "exports": {
    ".": {
      "import": "./dist/my-lib.es.js",
      "require": "./dist/my-lib.umd.js"
    },
  }
  ...
}

もし、ブラウザ環境のみをターゲットにしている場合は、main の代わりに browser フィールドを指定します。

{
  ...
  "browser": "dist/my-lib.umd.js"
  ...
}

ビルドとWatchスクリプトは以下のようにします。

{
  "build": "vite build",
  "watch": "vite build --watch",
  "prepare": "npm run build"
}

prepareを指定しておくことで、npm publish前に必ずビルドできます。

公開手順は、以下の記事で正しく解説されているので参照してください。

https://chaika.hatenablog.com/entry/2019/08/15/000000

TypeScript に対応

TypeScript に対応するには、型定義を提供する必要があります。(これも vite に限らない話になります)

パッケージのコードを JS で書いた場合は自分で作成する必要があり、TS で書いた場合は自動生成できます。(自動生成の方法も後で書きます)

まず、作成した型定義を指定する方法を説明します。

TypeScript のドキュメントによると、package.json の types フィールドに型定義を指定することで、利用する側は型定義ファイルを認識できるようになり、更に files フィールドに型定義を格納しているフォルダを指定し、同梱して公開します。

{
  ...,
  "types": "./types/main.d.ts",
  "files": [
    "/dist",
    "/types"
  ],
  ...,
}

ビルドスクリプトも以下のように変更します。(vue-tscによる型チェック+型定義生成を追加)

{
  "build": "vue-tsc && vite build",
  "watch": "concurrently \"vue-tsc --watch\" \"vite build --watch\"",
  "prepare": "npm run build"
}

次に、TS のコードから型定義を生成する方法を説明します。

型定義を自動生成するには tsconfig.json を設定する必要があります。

下記に示すように、noEmit オプションを消し、declaration(型宣言)に関わるオプションを追加します。

{
  "compilerOptions": {
    ...,
-   "noEmit": true,
+   "emitDeclarationOnly": true,
+   "declaration": true,
+   "declarationMap": true,
+   "declarationDir": "./types",
    ...
  },
  ...
}

ここで、declarationDir に指定したフォルダ(./types)に型定義が格納されます。

emitDeclarationOnly にする理由は、TSのコンパイル後のJSファイルはviteで出力されるため、型定義のみが必要だからです。

終わりに

vite を使うとパッケージの公開も非常に簡単になることが分かりました。

読んでいただいた方は、是非思いついたアイデアを公開して頂けたらと思います。

簡単に始めたい方は、Github のテンプレートも用意してあるので、ご利用ください。

https://github.com/coder-ka/vite-ts-lib-starter

また、テストの追加方法も載せているので、併せて読んでいただければと思います。

https://zenn.dev/drop_table_user/articles/lib-test-with-ava

Discussion