13
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

webpack4からwebpack5に移行しようとして格闘したお話

Last updated at Posted at 2023-05-22

先日、webpackについて学んでみたという記事を書いたところ
私の中では過去1番の反響を頂きました、ありがとうございます :bow_tone1:

今回は、前回の記事を書いた後にwebpackと格闘した(悩まされた)お話を書こうと思います。
前提として、今回webpack4で書かれた設定をwebpack5に移行した際に発生した問題となります。

1. CSSに書かれているurl()をwebpackが解決できない

webpackにはresolve.aliasという素敵な機能があります。(公式ページ
簡単に説明すると、今回問題となったようなCSSの中のURLや、JSのimportのパスなどを
webpackでビルドする際にいい感じに解釈してくれる機能です。

例(公式からコピペ)

webpack.config.js
const path = require('path');

module.exports = {
  //...
  resolve: {
    alias: {
      Utilities: path.resolve(__dirname, 'src/utilities/'),
      Templates: path.resolve(__dirname, 'src/templates/'),
    },
  },
};

こういう設定を書いておくと

import Utility from '../../utilities/utility';

という記述が

import Utility from 'Utilities/utility';

という風にスッキリ書けます、素敵ですね!

では、今回何が問題だったのかというと

_vars.scss
$img-path: '/assets/**/images';

という宣言をして

_style.scss
background-image: url(#{g.$img-path}/loading.svg);

と書いたてビルドしたところ

Module not found: Error: Can't resolve '/assets/**/images/loading.svg' in '/
DocRoot/src/**/public/assets/**/css'

と怒られました。

なお、ファイルの配置は以下のようになっています(先に書けよ)

/DocRoot
L **
  + /public
  | L /assets
  |   L /**
  |     L /css
  |       + /globals
  |       |  L _var.scss
  |       + /pages
  |       |  L _style.scss
  |       + /images
  |          L loading.svg
  +- webpack.config.js

あれ〜? resolve.aliasが効いてない?

webpack.config.js
resolve: {
  alias: {
   'assets': path.resolve(__dirname, './public/assets'),
  }
}

試行錯誤した結果、結論、以下で解決しました。

webpack.config.js
resolve: {
  alias: {
   '/assets': path.resolve(__dirname, './public/assets'),
  }
}

どうやら4までは名前?で解決?していたようですが、5からはパスで解決するようになったみたいです。
(すみません、これに関しては試行錯誤の結果で裏付けがないです)

でもまさか、先頭に/つけて解決するとは思ってませんでした…

2. file-loaderがないと怒られる

先の問題が解決して、無事にビルドも通ったし、さぁwebpack-dev-serverを立ち上げるぞ〜!
と意気込んだ矢先

Module not found: Error: Can't resolve 'file-loader' in '/Docroot/src/**'

え?何でやねん。
で、package.jsonを確認したところfile-loaderがなかったのでインストール

公式ドキュメント: file-loader

無事にエラーなくwebpack-dev-serverが立ち上がってめでたしめでたし:clap_tone1:
…とはなりませんでした。(エラーは解消したのですが)

3. ファイル名がハッシュ化される

無事にwebpack-dev-serverを立ち上げられたのですがページを開いたところコンソールにエラーが…

/assets/**e3a410f961d2d303a588.svg/ 404 Not found

途中のスラッシュ抜けてるしimages消えてるし誰だよお前な名前に変わっているしで混乱。
ちなみにwebpack.config.jsには以下のような記述をしていました。

webpack.config.js
module: {
  rules: [
    {
      test: /\.(png|jpg|gif|svg)$/,
      loader: 'file-loader',
      options: {
        name: '[name].[ext]',
        outputPath : 'images/',
        publicPath : function(path){
          return '/assets/**/images/' + path
        }
      }
    }
  ]
}

どうやらこの設定が効いていない様子。
試行錯誤した結果、

悲報、file-loader、いらない子であることが判明:sob:

実はfile-loaderが必要だったのは4までで、5からは非推奨でした…
この記事を書いていて気づいたのですが、file-loaderの公式ページも4のものでした…

じゃあどうするのか、というと、新しくAsset Modulesというものが追加になっていました。
この辺り、正しく書いてある記事などがなく、最終的には公式ドキュメントを読み込んで解決しました。

Asset Modulesから引用

Asset Modules is a type of module that allows one to use asset files (fonts, icons, etc) without configuring additional loaders.

Prior to webpack 5 it was common to use:

raw-loader to import a file as a string
url-loader to inline a file into the bundle as a data URI
file-loader to emit a file into the output directory
Asset Modules type replaces all of these loaders by adding 4 new module types:

asset/resource emits a separate file and exports the URL. Previously achievable by using > file-loader.
asset/inline exports a data URI of the asset. Previously achievable by using url-loader.
asset/source exports the source code of the asset. Previously achievable by using raw-loader.
asset automatically chooses between exporting a data URI and emitting a separate file. Previously achievable by using url-loader with asset size limit.
When using the old assets loaders (i.e. file-loader/url-loader/raw-loader) along with Asset Module in webpack 5, you might want to stop Asset Module from processing your assets again as that would result in asset duplication. This can be done by setting asset's module type to 'javascript/auto'.

翻訳にかけるとこんか感じのことが書かれています

アセットモジュールとは、アセットファイル(フォント、アイコンなど)を追加のローダーを設定することなく使用できるようにするモジュールの一種です。

webpack 5以前は、raw-loaderを使用するのが一般的でした:

raw-loaderは、ファイルを文字列としてインポートします。
url-loader ファイルをデータURIとしてバンドルにインライン化する。
file-loader ファイルを出力ディレクトリに出力する。
Asset Modulesタイプは、これらのローダーをすべて置き換えて、4つの新しいモジュールタイプを追加しています:

asset/resourceは、別のファイルを出力し、そのURLをエクスポートします。以前はfile-loaderを使用することで実現可能でした。
asset/inlineは、アセットのデータURIをエクスポートします。以前は、url-loaderを使用することで実現可能でした。
asset/source は、アセットのソースコードをエクスポートします。以前は raw-loader を使用することで達成可能でした。
asset は、データ URI をエクスポートするか、別のファイルを生成するかを自動的に選択します。以前は、アセットサイズに制限のあるurl-loaderを使用することで達成可能でした。
webpack 5でAsset Moduleと一緒に古いassets loader (例: file-loader/url-loader/raw-loader) を使用する場合、Asset Moduleがアセットを再度処理しないようにしたいことがあります。これは、アセットのモジュールタイプを「javascript/auto」に設定することで可能です。

すっごい極端に意訳すると「使えるけど使わないことをお勧めするよ:heart:」ということかと。
結果、公式ドキュメントを参考に以下のように書き換えることで事なきを得ました。

webpack.config.js
module: {
  rules: [
    {
      test: /\.(png|jpg|gif|svg)$/,
      type: 'asset/resource',
      generator: {
        filename: '[name][ext]',
        outputPath: 'images/',
        publicPath: () => '/assets/**/images/',
      }
    }
  ]
}

以上で、無事にwebpack-dev-serverも立ち上がるし、ビルドもきちんとできる環境が整いました。
今回、エラーメッセージをググったりして日本語だけじゃなく英語の記事なども見ましたが
やっぱり最後に頼りになるのは公式ドキュメントでした。(まれにそうじゃない場合もありますが)

また、今回の問題は日本語の記事だけで解決しようとしたら絶対に解決しなかったと思います。
もしくはとんでもなく時間がかかるか…

システムエンジニアとして働いていくには、やっぱり英語は重要だな、と改めて実感させられました。
今回もwebpackの一部にしか触れていませんが、この記事が誰かの参考になると幸いです。

それでは、またどこかで!

13
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?