text.hmsk.me

Simpacker で Vite を使う

October 3, 2021

code

hokaccha プロダクトの Simpacker が好きで、携わっているプロジェクトで勧めていたところ、別の問題として webpack-dev-server で開発するのが重くて辛いので Vite でも使えないかねという話に至ったので調査ついでに動かしてみた。

結論からいうと、Simpacker を諦めて vite-ruby を使うのが良さそう。何となく vite-ruby が webpacker の代替感があって余計なことをしてくれちゃいそうだなって思っていたら、全然ミニマムな感じだったのでちゃんとコードを読んでからにしようねと反省した。

以下の問題は全部うまいこと vite-ruby に隠してもらえる。

Simpacker での Vite 対応

このサンプルを作るのを通して、どういうもんか見て回った。

当初は Vite のドキュメントに沿ってやれば1時間もかからないと思ったら、各所ドキュメント通りに行かない箇所があって3時間くらいを要してしまった。

manifest.json が違う問題

Vite が Webpack や Parcel とは違った manifest.json の構成をしている(前は同じだったけど変わってしまった)ので、Simpacker の方で config を通すなどしてサポートしてあげるのが自然っぽそうに見えた。ただ、名の通り Webpack を想定したものでもあるだろうから hokaccha さん的には微妙かもしれない。

加えて、"application.js": "/public/packs/application.hash.js" みたいな WebpackManifestPlugin の吐く形式だと、キーと値の間でパスが一貫していないのに対して、Vite のものは、どちらも同じパスを持つようになっていて、これも Vite の設定でどうにかできそうになく難儀した。

"application.js": {
  "file": "application.hash.js",
  "src": "application.js",
  "isEntry": true
}

最終的には伝統的な manifest.json を吐いてくれる Rollup のプラグインを使うというワークアラウンドが手軽だったので、それで凌いだ。

Vite の開発用サーバ使用時に manifest.json を使えない

webpack-dev-server を使っているときのように、Vite の開発用サーバ向けにも

<%= javascript_pack_tag 'application', type: 'module' %>

とだけ Rails 側に書ければ格好良いのだけど、Vite のホットリロード用のクライアントのスクリプトを読む必要があったり、開発用サーバのホストにパスをいじらないといけなかったりで、development/production で出し分けが必要になってしまう。manifest を開発用サーバ使用時にも吐けて、その時に必要なスクリプトも差し込んでくれるような vite のプラグインを書くなりするといいのかもしれない。

とりあえず動くようには出来たけれど、最終的な vite.config.js はエレガントとはほど遠い。

import { defineConfig } from 'vite'
// Use this instead of vite's native 'manifest' option
// Since manifest's structure is different from Webpack/Parcel
// https://vitejs.dev/guide/backend-integration#backend-integration
import manifestPlugin from 'rollup-plugin-output-manifest'

export default defineConfig({
  root: 'app/javascript',
  publicDir: '/', // development
  build: {
    emptyOutDir: true,
    outDir: '../../public/packs',
    assetsDir: '',
    rollupOptions: {
      input: 'app/javascript/application.js',
      plugins: [
        manifestPlugin({
          outputPath: 'public/packs',
          publicPath: '/packs/'
        })
      ]
    }
  }
})