Ruby
Rails
webpacker

webpackerでdigest値を付けずにコンパイルする

webpackerを用いてjsの管理をしているRailsのアプリケーションを開発していて、アプリの外から参照されるjsファイルとアプリ内で使うjsファイルがあるため、

  • アプリ内で使うファイル: digest値を付ける
  • アプリ外から参照されるファイル: digest値を付けない

ということをwebpackerで実現してみました。
ちなみに、同様のことをsprocketsで行う場合は独自にrakeタスク作ったり assets:precompile で通るコードにモンキーパッチ当てたりgemを使ったり等の方法があります。

バージョン

Rails: 5.1.0
webpacker: 3.0.1

ファイル構成

app/javascript/
└── packs
    ├── inside1.js
    ├── inside2.js
    └── outside.js

今回、 app/javascript/packs 配下にwebpackで管理したいjsのファイルを置き、inside1.js, inside2.jsはdigest値ありで、outside.jsはdigest値無しで出力するという形にしていきたいと思います。

方針

方針として、 bundle exec rails webpacker:install で生成されるコードをなるべく変更しない形でやっていきます。
上記のコマンドを実行すると、 config/webpack/development.js には以下のようなコードが生成されます。

config/webpack/development.js
const environment = require('./environment')

module.exports = environment.toWebpackConfig()

この environment.toWebpackConfig()https://github.com/rails/webpacker/blob/fb8dd8c09ccc9d93ef3e084838e6e0f0683aaeb0/package/environment.js#L68 が呼び出されています。ここで定義されている設定を必要なところだけ上書きしていきます。

コード

config/webpack/development.js
const environment = require('./environment');

const defaultConfig = environment.toWebpackConfig();
var entries = defaultConfig.entry;
var outsideEntry = {outside: entries['outside']};
delete entries['outside'];

var nonDigestFileConf = {
  entry: outsideEntry,
  output: {
    filename: 'outside.js',
    path: defaultConfig.output.path,
    publicPath: defaultConfig.output.publicPath
  },
  plugins: []
};

module.exports = [
  Object.assign({}, defaultConfig, {
    entry: entries
  }),
  Object.assign({}, defaultConfig, nonDigestFileConf)
];

(production環境の場合は config/webpack/production.js に書きます)

module.exportsに配列で設定を書き込めば順番に実行してくれるので、2回に分けてコンパイルを実行しています。

最初にdigest値を付けるファイルの扱いです。 environment.toWebpackConfig().entry でコンパイルされる対象のファイルの名前(拡張子無し)とそのファイルパスのhashが取得できるので、そこからdigest値を付けたくないファイルのkeyを抜きます。最後に抜いた後の entry を元の設定に上書きしてあげれば、必要なファイルだけdigest値を付けることが出来ます。

digest値を付けない設定は nonDigestFileConf にありますが、 entry に対象のファイルを指定して output.filename にファイル名を output.path は出力先のパス output.publicPath は参照する際のパスですが、 environment.toWebpackConfig() が持ってるのでそれを指定します。
pluginswebpack.EnvironmentPlugin , ExtractTextPlugin , ManifestPlugin の3つの設定がされてますが、どれも使っていなかったので空配列にしています。必要に応じて変える必要があると思いますが ManifestPlugin は外すなり何かしら手を加えないと既に生成された manifest.json を上書きしてしまいアプリ内で使ってる javascript_pack_tag が軒並みエラーになるので注意です。