Viteで古式ゆかしいasset環境を作る

実現したいこと

Vite で古式ゆかしい asset 環境を作る。

Vite | Next Generation Frontend Tooling

前提

古式ゆかしいとは以下の状態を指す。

  • JS としてのエントリポイントが複数ある
  • asset manifest でコントロールしない(hash を付与しない)

本来 Vite は Parcel Bundler 以降の時代の代物なので、HTML を食わせると元の HTML も含めて entry point から芋づるで関連ファイルを変換、圧縮などよしなにしてくれる。その際、処理後のファイル名に何らかの hash 値を付与してくれるので、特に難しいことを考えずとも CDN などでの強いキャッシュを実現でき、asset 転送の負荷が自動的に削減されるありがたい仕組みである。

しかし今回はこの挙動を変えて、上の状態を目指す。

Vite は基本的にはフロントエンド完結向けであり、バックエンドとの統合には asset manifest を利用してなんとかしてくれという立場だが、 今回の「古式ゆかしい」は manifest を利用しないことを前提にしているので、統合には一工夫要る。逆に言うと、

asset の読み込み時に manifest からパスを解決する仕組みを差し込めない環境1との統合はこの方法が正解

とも言える。

設定

基本形はこうなる。

  build: {
    assetInlineLimit: 0, // asset が自動的に埋め込まれてしまうのを防ぐ
    rollupOptions: {
      output: {
        entryFileNames: '[name][extname]',
        assetFileNames: '[name][extname]'
      },
      input: {
        name1: <root以下のパス>,
        name2: <root以下のパス>
      }
    }
  }
  • Vite 4.4.9 のデフォルトは manifest を生成しない。hash 値を付与しない運用を目指すのでこれはそのまま活かす
  • assetInlineLimit を 0 にすることで埋め込みを防ぐ
  • input に書いたものが entry point になるので、これがたくさんある場合はたくさん列挙しなければいけない。2 ファイル名ではなく entry 名が output の名前になることに注意
  • output は機械的に解決されるので、少なくとも entryFileNamesassetFileNames[hash] の入っていない名前を付ける必要がある

rollupOptionsはプレーンなrollupのoptionsではない

rollupOptions に rollup の option をそのまま書けるのかというそうではない。そのまま書ければ

[
  {
    input: <パス>,
    output: {
      file: <パス>
    }
  },
  {},
  ...
]

の形で書くことができるが、これは無理。

entryのHTMLは必要か?

Vite に読み込ませる HTML はあってもなくてもよい。あれば vite server 起動時にその HTML から entry の JS や asset をいい具合に読み込んでページを構成し、ページ読み込み時に JIT コンパイルが走るというだけで、dev server を使わずに

vite build -w -m development

と起動することで development 向けの asset を生成することはできる3

Vite に頼らずに静的な HTML やなんらかのレガシーな動的サイトを作っていたとしても、その HTML から読み込み可能な場所に vite build の成果物が生成されていれば、それで問題ない。

assetのパスが一緒くたになってしまう問題

Percel Bundler 以降、

  • HTML をそのまま食わせるといい具合に処理してくれる
  • asset のパスは一緒くたになる

という挙動がデフォルトになっており、古式ゆかしく

  • /javascripts/
  • /stylesheets/
  • /images/

みたいにディレクトリを掘っておいても出力時には無視されてしまう。

entryFileNames は基本的に JS なのでこれは /javascripts なり /js なり決め打ちで書いてしまえがよいが、asset はそうはいかない。そこでパスを含めた名前に変換するルールを作る必要がある。

簡単に言うと

assetFileNames: (asset) => preprendAssetPath(asset.name)

みたいなものを用意しておくとよい。この prependAssetPath() の中で拡張子に応じてディレクトリを分けてあげる。これは String に応じて String を返してあげる初歩的な JavaScript だ。

なに、階層が入れ子になっている? 何も処理しなくていいなら

publicDir

の中に置いてくれ。恐らく入れ子のまま扱いたい asset は画像なのでは? CSS はファイルを分けて書いていても出力時には一つにしておく、みたいな形になっていると予想されるので、あまり入れ子のまま扱いたいケースは多くないと予想できる。もし CSS が大量にあり、階層を入れ子にしたいは結構大きな仕組みだと思われるので、単純な Vite 一つの設定ではなく、もう少し複雑な仕組みを考える必要があるだろう。

  1. 例えばサーバ環境が自由にならないブログサービスなどに対して JS や画像を提供したい場合にも有効 

  2. もちろん公式のリファレンスにあるように自動的に解決するような function を作ってもよい 

  3. asset を読み込んだ HTML を表示するためにはなんらかの Web サーバを別途用意する必要がある 

More