🛠️

unpluginでVite等におけるMSWの記述をスマートにする

2023/09/17に公開

本記事では unplugin というツールを利用し、APIのモックに便利な MSW の処理をスマートに記述する方法をご紹介します。

unpluginとは

unpluginはビルド用の統一プラグインシステムであり、Vite・Rollup・Webpack・esbuild・Rspackといったフロントエンドの主要ビルドツールにおけるプラグインを作成することが可能です。(Rspack のみ experimental )

基本はRollup plugin APIをベースに記述しますが、 Vite 独自の機能などフレームワーク特有のロジックも記述可能です。例えば Vite におけるサーバ設定を変更するconfigureServerを記述する場合は以下の様に記述します。

export const unplugin = createUnplugin((options: UserOptions, meta) => {
  return {
    // unpluginの共通処理
    name: 'unplugin-prefixed-name',
    transformInclude(id) { /* ... */ },
    transform(code) { /* ... */ },

    // フレームワーク特有の処理
    vite: {
      // Vite plugin
      configureServer(server) {
        // ここにサーバ設定を記述
      }
    }
  }
})

つまりunpluginは vite の plugin などを汎用的かつ、様々な環境で利用できるようにするためのプラグイン作成ツールとなります。

MSWにおけるworkerの記述

例として、Vite 上で MSW を利用しブラウザ上(worker)のリクエストをモックするには以下の様な記述となります。(リクエストハンドラの記述方法は省略)

if (import.meta.env.DEV) {
  (async() => {
    const { worker } = await import('./mocks/browser')
    worker.start()
  })()
}

上記の処理では、開発時のみ動的 import で worker 処理を実行しています。静的に import をしてしまうとビルド時にバンドルされてしまうためこのような記述になりますが、やや冗長な印象です。 MSW は開発時以外でも Storybook やテストなどでも利用可能なため、環境変数などによる利用制御をもう少しスマートにしたいところです。

unpluginにおける記述

unplugin を用いて実装した結果がこちらになります。

import { worker } from 'unplugin-msw/worker'
worker?.start()

非常にシンプルになりました。workerundefinedを含む形になっているのは本番環境など利用しない状態で動作させないようにしているためです。実行されない場合はバンドルもされません。

Vite における設定は以下の様になります。ここで利用可否などを制御可能です。

// vite.config.ts
import MswPlugin from 'unplugin-msw/vite'

export default defineConfig({
  plugins: [
    MswPlugin({
      mockPath: 'mock/handler', // リクエストハンドラのパス
      workerEnabled: process.env.NODE_ENV === 'development', // workerの稼働制御
    })
  ],
})

動作の仕組み

unplugin(rollup)の処理はビルド時に下図のようになります。(一部抜粋)

rollup

(※https://rollupjs.org/plugin-development/#plugins-overview より引用)

定義したID(resolveId)が読み込まれた際にload関数に記載された内容に従って処理を実行します。今回の例では以下のような処理になっていました。

import { worker } from 'unplugin-msw/worker'

unplugin-msw/workerというresolveIdで定義したパスを解決すると unplugin はビルド時に

import { setupWorker } from 'msw'
import handler from '[指定されたパス]'
export const worker = setupWorker(...handler)

として解決することが可能です。これにより記述をシンプルにすることが出来ました。

本番時などはunplugin-msw/workerを読み込んだ際に

export const worker = undefined

として解決されるため、MSW が import されずに実行されないという仕組みです。

試してみたい場合

npm でご利用いただけます。

npm i -D unplugin-msw

TypeScript を利用している場合はtsconfig.jsonに以下をご記載ください。

{
  "compilerOptions": {
    "types": [
      "unplugin-msw/globals"
    ]
  }
}

ソースコードはこちらです。unplugin の記述方法などはこちらをご参照ください。サーバ用のMSWも利用可能です。

https://github.com/esttom/unplugin-msw

参考

https://github.com/unjs/unplugin

https://mswjs.io/

Discussion