webpackerからwebpackへ。

背景

Railsプロジェクトで、webpackerの引退に合わせて、webpackへの移行を行います。

移行の際に身につけたフロントの知識や、ポイントをまとめています。

基本の手順

基本は、webpackerのgem公式ページを参考にします。
上記のサイトでは、switching Guideが紹介されているので、それに従います。
ガイドに従えばできると思っていたのですが、いくつかポイントがありました。

そもそもwebpackerでは何を管理していたのか?

そもそも、RailsにはwebpackerとSprocketsという2つのフロントエンド関連のGemが入っていました。
上記のツールは、javascript, css, imageといった静的アセットを処理して、いい感じに配信してくれるためのモジュールハンドラーと呼ばれるものです。

ここで問題なのが、自分の使っているwebpackerではjs, css, imageのどれを管理対象にしていたのか?ということです。
基本webpackerはjavascriptを処理する前提で導入されているはずですが、設定によってはcss, imageも処理できてしまいます。

そして、それによって移行の手順も変わってきます。

フロントはビルドが必要

そもそもですが、webpackはフロンドの技術であり、ビルドが必要です。
Railsのようなフレームワークはrails sしてあげればよしなに処理してくれますが、webpackは都度ビルドが必要です。
私の場合は `blog/package.json` に以下のようなスクリプトを設置し、npm run buildを実行することでwebpackでビルをしています。

"scripts": {
    "build": "webpack --config ./config/webpack/webpack.config.js"
  }

実際の移行手順

javascrip,css,imageの全てをwebpackerで管理していたという前提で作業を進めていきます。

01. Javascriptをwebpackerへ移行

これは、手順通りにやればできます。
ただ、手順の中でディレクトリ階層が変化するので、application.jsでimportするパスを修正する必要があります。

npm run build
> blog@0.1.0 build
> webpack --config ./config/webpack/webpack.config.js

asset application.js 67.7 KiB [compared for emit] [minimized] (name: application) 1 related asset
runtime modules 718 bytes 3 modules
cacheable modules 99 KiB
  modules by path ./node_modules/ 98 KiB
    ./node_modules/@rails/ujs/lib/assets/compiled/rails-ujs.js 27.7 KiB [built] [code generated]
    ./node_modules/turbolinks/dist/turbolinks.js 37.6 KiB [built] [code generated]
    ./node_modules/@rails/activestorage/app/assets/javascripts/activestorage.js 32.6 KiB [built] [code generated]
  modules by path ./app/javascript/ 1.02 KiB
    ./app/javascript/packs/application.js 751 bytes [built] [code generated]
    ./app/javascript/channels/index.js 292 bytes [built] [code generated]
./app/javascript/channels/ sync _channel\.js$ 160 bytes [built] [code generated]
webpack 5.90.3 compiled successfully in 1981 ms

02. cssをwebpackへ移行。

一旦cssのimport業はコメントアウトしていましたが、ここで有効化してビルドするとエラーが発生します。

npm run build
ERROR in ./app/javascript/packs/application.js 11:0-28
Module not found: Error: Can't resolve 'packs/css/pack.css' in '/home/ubuntu/blog/app/javascript/packs'
resolve 'packs/css/pack.css' in '/home/ubuntu/blog/app/javascript/packs'

Optional: CSS + SASSの手順に沿って、css,scssをビルドする設定を記述します。
が、ここで不可解な単語が幾つが出てきます。

webpack.config.jsのmodule内にあるruleとは?
 module: {
    rules: [
      // Add CSS/SASS/SCSS rule with loaders
      {
        test: /\.(?:sa|sc|c)ss$/i,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
      },
    ],
  },

ruleはアセットの変換ルールのことのようです。
testは変換対象のファイルを正規表現で書いています。
useは変換するためのローダを指定しています。配列で複数指定する場合はuseというものを使用するようです。

こちらの記事で解説されています。

この時点でのwebpack.config.json
const path    = require("path")
const webpack = require("webpack")

// Extracts CSS into .css file
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// Removes exported JavaScript files from CSS-only entries
// in this example, entry.custom will create a corresponding empty custom.js file
const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts');

module.exports = {
  mode: "production",
  devtool: "source-map",
  entry: {
    application: [
      "./app/javascript/packs/application.js",
    ],
    custom: '/app/javascript/packs/css/pack.css',
  },
  output: {
    filename: "[name].js",
    sourceMapFilename: "[file].map",
    chunkFormat: "module",
    path: path.resolve(__dirname, '..', '..', 'app/assets/builds')
  },
  module: {
    rules: [
      // Add CSS/SASS/SCSS rule with loaders
      {
        test: /\.(?:sa|sc|c)ss$/i,
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
      },
    ],
  },
  resolve: {
    // Add additional file types
    extensions: ['.js', '.jsx', '.scss', '.css'],
  },
  plugins: [
    new webpack.optimize.LimitChunkCountPlugin({
      maxChunks: 1
    }),
    // Include plugins
    new RemoveEmptyScriptsPlugin(),
    new MiniCssExtractPlugin(),
  ]
}

ビルドに成功

ビルドには成功してbuildディレクトリにファイルが出力されました!

出力したcss類は、assets/stylesheets/application.cssでassets/buildsディレクトリごと読み込みました。

手順書には、ビルドまでした記述がないため、それ移行の出力はこちらで考える必要があります。

03. imageをwebpackへ移行。

こちらも基本的に手順書のOptional: Fonts, Images, SVGに従ってやります。

普通にやると、js,css同様`blog/app/assets/builds/`以下に画像が生成されるので、img_tagで出力できない。

img_tagの入力先を変えるっていう手もあるだろうけど、webpackのためにrailsの設定を変えたくないので、画像だけ出力先をimagesの中に変えたい!
が、いろいろ試してけれど、画像だけ出力先ディレクトリを変更することができなかった。。
そのうちまた試してみよう。(今後の課題)

現段階のwebpack.config.js
const path = require("path")
const webpack = require("webpack")

// Extracts CSS into .css file
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// Removes exported JavaScript files from CSS-only entries
// in this example, entry.custom will create a corresponding empty custom.js file
const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts');

module.exports = {
  mode: "production",
  devtool: "source-map",
  entry: {
    application: [
      "./app/javascript/packs/application.js",
      // "/app/assets/stylesheets/application.scss",
    ],
    pack: '/app/javascript/packs/css/pack.css',
  },
  output: {
    filename: "[name].js",
    sourceMapFilename: "[file].map",
    chunkFormat: "module",
    path: path.resolve(__dirname, '..', '..', 'app/assets/builds')
  },
  module: {
    rules: [
      // Add CSS/SASS/SCSS rule with loaders
      {
        test: /\.(?:sa|sc|c)ss$/i,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
            options: {
            }
          },
          {
            loader: 'css-loader'
          },
          {
            loader: 'sass-loader'
          },
        ],
      },
      {
        test: /\.(png|jpe?g|gif|eot|woff2|woff|ttf|svg)$/i,
        loader: 'file-loader',
        options: {
          // outputPath: path.resolve(__dirname, '..', '..', 'app/assets/aaa'),
          outputPath: (__dirname, '..', '..', 'images'),
        },
      },
    ],
  },
  resolve: {
    // Add additional file types
    extensions: ['.js', '.jsx', '.scss', '.css'],
  },
  plugins: [
    new webpack.optimize.LimitChunkCountPlugin({
      maxChunks: 1
    }),
    // Include plugins
    new RemoveEmptyScriptsPlugin(),
    new MiniCssExtractPlugin(),
  ]
}

残された課題

  1. webpackで管理する画像をimg_tagで利用できない
    • npm run buildすると、`app/assets/builds`の中にjs,css,imageの全てがビルドされる。
    • railsのimg_tagは`app/assets/images`の中の画像を出力するので、これでは利用できない。
    • npm run build実行時に画像だけ`app/assets/images`の中に入れば解決するが、その指定方法がいろいろ試してダメだった。
      • img_tagのinputを`app/assets/images`から変えることは避けたいので現状手詰まり。