Viteとtsupによるコンポーネントライブラリ公開の効率化
Emily Parker
Product Engineer · Leapcell

はじめに
急速に進化するフロントエンド開発の世界では、再利用可能で共有可能なUIコンポーネントを作成することが、効率的な開発と一貫したユーザーエクスペリエンスの維持のために不可欠です。コンポーネントライブラリは、このようなモジュール性の基盤となり、開発者が機能やスタイルを消化可能な単位にカプセル化できるようにします。しかし、コンポーネントライブラリの真の力は、他の人が簡単に利用できるようになって初めて解き放たれます。そのためには、堅牢なパッケージングとnpmのようなレジストリへの容易な公開が必要です。従来、コンポーネントライブラリのビルドパイプラインをセットアップすることは、複雑で時間のかかる作業であり、しばしば複雑なWebpack設定を伴いました。この記事では、Viteやtsupのようなモダンなツールがこのプロセスをどのように革新し、ReactまたはVueコンポーネントライブラリをnpmにビルドして公開することを著しく簡単かつ迅速にしたかを探ります。これらのツールのコア原則、実践的な実装、そしてフロントエンド開発者にもたらす具体的なメリットについて掘り下げていきます。
モダンなコンポーネントライブラリのワークフロー
Viteとtsupの詳細に入る前に、コンポーネントライブラリのビルドと公開に関わる主要な概念について共通の理解を深めましょう。
コンポーネントライブラリ: プロジェクト間で再利用するために設計された、しばしばReactやVueのようなフレームワークで書かれた、事前に構築されたUIコンポーネントのコレクション。 パッケージング(バンドリング): ソースコード(例:TSX、Vue SFC、CSS)を取得し、他のプロジェクトで容易に利用できる最適化されたブラウザ互換ファイル(例:JavaScript、CSS)に変換するプロセス。これには通常、トランスパイル、ミニフィケーション、ツリーシェイキングが含まれます。 モジュールフォーマット: JavaScriptモジュールを整理およびロードするための標準。モダンなWeb開発で最も一般的なのは次のとおりです。 * CommonJS (CJS): 主にNode.js環境で使用されます。 * ECMAScript Modules (ESM): JavaScriptモジュールの公式標準であり、ブラウザとNode.jsの両方に適しています。 型定義(d.tsファイル): TypeScriptのサポートと、利用するプロジェクトでの開発者エクスペリエンスの向上に不可欠な、JavaScriptモジュールの型情報を提供するファイル。 npm (Node Package Manager): 開発者がコンポーネントライブラリを含むJavaScriptパッケージを公開および共有する、世界最大のソフトウェアレジストリ。
歴史的に、Webpackは支配的なバンドラーであり、 immense (膨大な) な柔軟性を提供しましたが、設定の複雑さとビルド時間の遅さが伴うことが多かったです。「ノー・コンフィグ」または「ロー・コンフィグ」バンドラーの台頭は、パッケージングプロセスを劇的に簡素化しました。Viteとtsupはこの新しい世代を体現しており、開発者エクスペリエンスとパフォーマンスに焦点を当てています。
Viteによるコンポーネントライブラリのパッケージング(Reactの例)
Viteは、主にその驚異的に高速な開発サーバーで知られていますが、特にモダンなJavaScriptフレームワークにとってビルドツールとしても優れています。ライブラリモードの設定は驚くほど簡単です。
シンプルなReactコンポーネントライブラリの構造があると想像してみましょう。
my-react-library/
├── src/
│ ├── components/
│ │ ├── Button/
│ │ │ ├── Button.tsx
│ │ │ └── index.ts
│ │ └── Card/
│ │ ├── Card.tsx
│ │ └── index.ts
│ └── index.ts // メインのエントリポイント
├── package.json
├── tsconfig.json
├── vite.config.ts
src/index.ts
はコンポーネントを再エクスポートします。
// src/index.ts export * from './components/Button'; export * from './components/Card';
次に、vite.config.ts
をライブラリモード用に設定しましょう。
// vite.config.ts import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import { resolve } from 'path'; export default defineConfig({ plugins: [react()], build: { lib: { // 複数のエントリポイントもサポートされています entry: resolve(__dirname, 'src/index.ts'), name: 'MyReactLibrary', // UMDビルドのグローバル変数名 fileName: (format) => `my-react-library.${format}.js`, }, rollupOptions: { // ライブラリにバンドルされるべきでない依存関係を外部化する external: ['react', 'react-dom'], output: { // 外部化された依存関係のためにUMDビルドで使用するグローバル変数を提供する globals: { react: 'React', 'react-dom': 'ReactDOM', }, }, }, // tsc を使用して型定義を生成する // TypeScriptプロジェクトで有効にする // dts: true, // これは.d.tsファイルを生成します }, });
ビルドするには、package.json
にスクリプトを追加します。
// package.json { "name": "my-react-library", "version": "1.0.0", "main": "./dist/my-react-library.umd.cjs", "module": "./dist/my-react-library.es.js", "types": "./dist/index.d.ts", // TypeScriptにとって重要 "files": ["dist"], "scripts": { "build": "vite build && tsc --emitDeclarationOnly --declaration --outDir dist" }, "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" }, "devDependencies": { "@types/react": "^18.0.0", "@types/react-dom": "^18.0.0", "@vitejs/plugin-react": "^4.0.0", "react": "^18.0.0", "react-dom": "^18.0.0", "typescript": "^5.0.0", "vite": "^4.0.0" } }
package.json
の tsc --emitDeclarationOnly --declaration --outDir dist
の部分に注意してください。Viteには dts: true
がありますが、複雑な型エクスポートに対して常に十分な柔軟性を提供しない場合があります。このように tsc
を別途実行することで、堅牢な型定義生成が保証されます。package.json
の main
、module
、types
フィールドは、コンシューマーが正しいモジュールフォーマットと型定義を正しく取得するために不可欠です。files
は npm に公開する際に含めるものを指定します。peerDependencies
は、ライブラリがバンドルするのではなく、コンシューミングアプリケーションによって提供されることを 期待 する依存関係を宣言します。
npm run build
を実行すると、通常はESモジュール、CommonJS、UMDフォーマット、および型定義を含む dist
フォルダに出力されます。
tsupによるコンポーネントライブラリのパッケージング(Vueの例)
tsupは、特にTypeScriptプロジェクトのライブラリのバンドリングを簡素化する、もう一つの優れたツールです。Esbuildをベースにしており、驚異的に高速であり、しばしば最小限の設定で済みます。
Vueコンポーネントライブラリを考えてみましょう。
my-vue-library/
├── src/
│ ├── components/
│ │ ├── MyButton.vue
│ │ ├── MyCard.vue
│ │ └── index.ts // コンポーネントを再エクスポート
│ └── index.ts // メインのエントリポイント
├── package.json
├── tsconfig.json
├── tsup.config.ts
src/components/index.ts
は次のようになります。
// src/components/index.ts import MyButton from './MyButton.vue'; import MyCard from './MyCard.vue'; export { MyButton, MyCard };
そして src/index.ts
は再エクスポートするだけです。
// src/index.ts export * from './components';
次に、tsup.config.ts
を設定しましょう。
// tsup.config.ts import { defineConfig } from 'tsup'; import vue from '@vitejs/plugin-vue'; // 必要に応じて他のVueプラグイン export default defineConfig({ entry: ['src/index.ts'], format: ['cjs', 'esm'], // CommonJSとES Modules dts: true, // 型定義を生成する clean: true, // ビルド前に dist フォルダをクリーンアップする splitting: false, // シングルファイルコンポーネントライブラリの場合、通常は false sourcemap: true, external: ['vue'], // Vueはピア依存関係 esbuildPlugins: [ // .vueファイルを直接インポートする場合、ViteのVueプラグインを使用する // これはViteの設定でより一般的ですが、Vueファイル(SFC)の処理方法を示すものです。 // コンパイル済みのJS/TSのみをエクスポートする場合、tsup自体には厳密には必要ないかもしれませんが、ソースのビルドには有益です。 // tsupの場合、.vueファイルを先にJS/TSにコンパイルするか、より上位で処理されることを保証する方が簡単な場合が多いです。 ], // .vueファイルを処理する例:それらをJSモジュールに事前にコンパイルする。 // または、tsupがTSを直接処理し、コンパイル済みのVueコンポーネントをインポートする場合、esbuildプラグインの拡張が必要になります。 // Vueコンポーネントライブラリの一般的なアプローチは、`vue-tsc`を使用してSFCの.d.tsファイルを生成することです。 // tsupはTS/JSのみに優れており、SFCの場合、Viteやカスタムビルドステップと組み合わせることが多いです。 });
Vueコンポーネントライブラリでより実用的な tsup.config.ts
は、.vue
ファイルを個別にコンパイルする場合や、tsup
がコンパイル済みのJSモジュールをエクスポートする .ts
ファイルのみを消費する場合、よりシンプルになる可能性があります。.vue
ファイルを直接 tsup
に処理させたい場合は、esbuildプラグインが必要ですが、Viteほど簡単ではありません。シンプルさと堅牢性のために、多くの場合 Vite のビルド (vite build --lib
) が Vue SFC には好まれます。または、型定義のために vue-tsc
を使用し、次にコンパイルされた JS をバンドルするために tsup
を使用します。
.vue
ファイルを個別にコンパイルするか、tsup
がコンパイル済みのJSモジュールをエクスポートする .ts
ファイルのみを消費する場合、Vueコンポーネントライブラリにより実用的な tsup.config.ts
は、よりシンプルになる可能性があります。.vue
ファイルを直接 tsup
に処理させたい場合は、esbuildプラグインが必要ですが、Viteほど簡単ではありません。シンプルさと堅牢性のために、多くの場合 Vite のビルド (vite build --lib
) が Vue SFC には好まれます。または、型定義のために vue-tsc
を使用し、次にコンパイルされた JS をバンドルするために tsup
を使用します。
pm run build を実行すると、
dist/index.cjs、
dist/index.mjs、および
dist/index.d.tsが生成されます。
tsup` は、型定義の生成から複数の出力フォーマットまで、多くの定型処理を自動的に行います。
npmへの公開
ライブラリがビルドされたら、npmへの公開は簡単なプロセスです。
-
package.json
が正しいことを確認する:name
: パッケージの一意の名前。version
: セマンティックバージョニングは不可欠です(例:1.0.0
)。description
: ライブラリの簡単な概要。keywords
: 関連する検索用語。author
/license
: 重要なメタデータ。main
、module
、types
: ビルドされた出力ファイルへのポインタ。files
: 含めるファイル、通常は["dist"]
のみ。peerDependencies
: 外部依存関係を正しくリストする。
-
npmにログインする:
npm login
プロンプトに従って、npmのユーザー名、パスワード、メールアドレスを入力します。
-
公開する:
npm publish
スコープ付きパッケージ(例:
@your-scope/my-library
)を公開している場合は、次のようにする必要があるかもしれません。npm publish --access public
このコマンドは、パッケージをnpmレジストリにアップロードします。
-
更新(必要な場合): 後続の更新では、
package.json
のversion
をインクリメントし(例:npm version patch
、npm version minor
、またはnpm version major
を使用)、再度npm publish
を実行します。
高度な考慮事項
- CSS/SCSSの処理: Viteとtsupの両方でCSSのインポートを処理できます。コンポーネントライブラリの場合、生のCSSファイル(例:
dist/css
フォルダ)をエクスポートするか、コンシューマーがスタイルをインポートする方法に応じて、JavaScriptバンドルに直接コンパイルしたい場合があります。Viteのライブラリモードでは、適切な場合にCSSが自動的に抽出されます。tsupの場合、esbuildプラグインを使用するか、別のビルドステップ(例:CSSをコンパイルするpostcss
または SCSS をコンパイルするsass
)を使用する場合があります。 - Storybook/ドキュメント: コンポーネントライブラリのビルドは、しばしばそのドキュメント化と密接に関連しています。Storybookのようなツールを使用すると、コンポーネントを個別に開発、テスト、ドキュメント化でき、最終的には保守性と採用率の向上につながります。
- テスト: コンポーネントの信頼性を確保するために、単体テストと統合テストを実装します。Vitest(Viteプロジェクト用)やJestのようなツールは優れた選択肢です。
- モノレポ: 複数の関連パッケージ(例:共有ユーティリティライブラリとコンポーネントライブラリ)がある場合は、LernaやTurborepoのようなツールを使用したモノレポ設定を検討してください。
Viteまたはtsupを使用すると、パッケージングプロセスが大幅に簡素化され、開発者は複雑なビルド設定よりもコンポーネントのロジックやデザインに集中できます。これらのツールの速度と使いやすさは、モダンなフロントエンドライブラリ開発に理想的な選択肢となります。
結論
コンポーネントライブラリをパッケージ化してnpmに公開することは、再利用性を促進し、フロントエンド開発を加速するための重要なステップです。Viteやtsupのようなモダンなビルドツールは、多くの隠れた複雑さを抽象化し、特にReactやVueプロジェクト向けの、驚異的なビルド速度と直感的な設定を提供します。これらのツールを活用することで、開発者はソースコードを、型定義やさまざまなモジュールフォーマットを備えた最適化された利用可能なパッケージに効率的に変換し、npmを通じてより広い開発コミュニティと簡単に共有できます。これらのツールは、開発者が前例のない容易さで高品質でパフォーマンスの高いコンポーネントライブラリを構築および配布することを可能にします。