2021-10-07

RailsからWebpackerを外してpureなwebpack構成にしてみる その2

前回、脱Webpackerすべくデフォルトの設定をいろいろ見て行ったが、今回はそれを参考に自分でwebpack.config.jsを作成し、Railsを繋ぎ込んでいこうと思う

Webpackerを除去する

とりあえずWebpackerと関連ファイルをきれいにしていく

  • Gemfileとpackage.jsonからWebpackerを削除する
  • ルートにある.browserslistrc、babel.config.js、postcss.config.jsの削除
  • config/webpackディレクトリを中身ごと削除
  • config/webpacker.ymlの削除
  • bin/webpackとwebpack-dev-serverの削除

サンプルリポジトリでは↓の作業
https://github.com/hakozaru/remove_webpacker_sample/commit/943ec0c53edb50abd83b93dca76fd91dbb5f05f7

webpackを入れてビルド環境を作っていく

いろいろパッケージ入れたりして、とりあえずビルドが通るところまでやっていく

  • yarn add --dev webpack webpack-cli でwebpackとcliツールを入れ、 touch webpack.config.js で設定ファイルを用意
  • 今回はES5にコンパイルできればOKでポリフィルは考えないという前提で進めることとし、トランスパイルはbabelではなくtscに任せる
    • yarn add --dev typescript でTSを入れ、 npx tsc --init で設定ファイルを準備
  • TSビルド時にエラーになるので型定義ファイルも入れておく yarn add --dev @types/react @types/react-dom
  • 必要なプラグインも入れておく => yarn add --dev case-sensitive-paths-webpack-plugin webpack-assets-manifest

ちなみにReactとReact DOMはwebpackerを入れた時に入っているので特にやることはない
ということでこの時点での各種設定ファイルは以下の通り

webpack.config.js

const path = require('path')
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin')
const WebpackAssetsManifest = require('webpack-assets-manifest')

module.exports = {
  mode: process.env.NODE_ENV,
  entry: {
    application: path.resolve(__dirname, "./app/javascript/packs/application.js"),
  },
  output: {
    filename: "js/[name]-[contenthash].js",
    chunkFilename: "js/[name]-[contenthash].chunk.js",
    hotUpdateChunkFilename: "js/[id]-[hash].hot-update.js",
    path: path.resolve(__dirname, "./public/packs"),
    publicPath: "/packs/",
  },
  resolve: {
    extensions: ['.js', '.jsx', '.tsx'],
    modules: [
      path.resolve(__dirname, "./app/javascript"),
      "node_modules"
    ]
  },
  module: {
    strictExportPresence: true,
    rules: [
      {
        test: /\.(ts|tsx)$/,
        use: [
          {
            loader: "ts-loader"
          },
        ],
      },
    ],
  },
  plugins: [
    new CaseSensitivePathsPlugin(),
    new WebpackAssetsManifest({
      entrypoints: true,
      writeToDisk: true,
      output: "manifest.json",
      publicPath: true
    }),
  ]
}

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "jsx": "react",
    "module": "commonjs",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true,
  }
}

これで webpack --progress --color --mode=development してビルドが通るようになった
ビルド通るところまでやったコミット

yarnからビルドできるようにnpm scriptを用意する

↑で使った普通にビルドするためのものと、開発中に変更検出させるためのものの2つを用意する

"scripts": {
  "dev": "webpack --progress --color --mode=development",
  "watch": "webpack --progress --color --watch --mode=development"
}

ビルドしたJSをRailsから読み込めるようにする

Railsから使えるようにするためにはWebpackerが用意している javascript_pack_tag のようなヘルパーが必要なんだけど、自分で用意してメンテしていくのは面倒なのでsimpacker gemに任せてしまおうと思う
Gemfileにsimpackerを入れて bin/rails simpacker:install で設定ファイルなどをインストールすれば使えるようになる
simpackerはインストール時に最低限のwebpack.config.jsも生成してくれるので、そちらをベースにして必要な設定を追加していくという進め方でもいいかもしれない

const path = require("path");
const WebpackAssetsManifest = require("webpack-assets-manifest");

const { NODE_ENV } = process.env;
const isProd = NODE_ENV === "production";

module.exports = {
  mode: isProd ? "production" : "development",
  devtool: "source-map",
  entry: {
    application: path.resolve(__dirname, "app/javascript/application.js"),
  },
  output: {
    path: path.resolve(__dirname, "public/packs"),
    publicPath: "/packs/",
    filename: isProd ? "[name]-[hash].js" : "[name].js",
  },
  resolve: {
    extensions: [".js"],
  },
  plugins: [
    new WebpackAssetsManifest({
      publicPath: true,
      output: "manifest.json",
    }),
  ],
};

simpackerは javascript_pack_tag を使えるようにしてくれるのでviewは特に変更しなくてもOK
あとはRailsを起動してwebpackをwatchモードで起動しておけば、webpack管理下のファイルの変更を感知して勝手にリビルドしてくれるようになる
ただし、現時点ではホットリロードには対応していないという致命的な欠点があるので、そこは別途やっていきたい

simpackerを入れたコミット↓ https://github.com/hakozaru/remove_webpacker_sample/commit/8377d357cfc95d8e00181ac5b93529e3d1e8f21b

ということで意外と簡単に脱Webpackerすることができた
改めてwebpack.config.jsを眺めると、これをWebpackerを通してあれこれやるのは確かに相当辛い
隠蔽が減ってシンプルに情報を得やすくなるのでとてもいい感じですね

参考記事