【メモ】webpack設定ファイルの書き方


試行錯誤しながらwebpackが動くところまで実装したので、備忘録として残して。

実装のゴール

resources配下のjs,scssファイルを、ファイル構成を保ったままpublic配下のjs,cssファイルに出力したい。
例えば、resources/js/post/index.jspublic/js/post/index.jsに出力する。

ファイル構成

ブラウザ側で読み込まれるのは、public配下のjs,cssファイル。
そのためresources配下のjs,scssファイルをコンパイル・圧縮したものをpublic配下のjs,cssファイルへ出力している。

sample_app
    ├─ public
    │   ├─ js
    │   │   └─ post
    │   │       ├─ index.js
    │   │       └─ show.js
    │   └─ css
    │        └─ post
    │            ├─ index.css
    │            └─ show.css
    │
    ├─ resources
    │   ├─ js
    │   │   ├─ _share
    │   │   │  └─ utility.js
    │   │   │
    │   │   └─ post
    │   │       ├─ index.js
    │   │       └─ show.js
    │   └─ scss
    │        └─ post
    │            ├─ index.scss
    │            └─ show.scss
    │
    └─ webpack.config.js

各ファイルの記述内容

例としてresources/js/post/index.jsにて下記ファイルを読み込む

  • resources/scss/post/index.scss
  • resources/js/_share/utility.js
resources/js/post/index.js
// scssのimport
import '@scss/post/index.scss';

// jsのimport
import * as $util from '@js/_share/utility';

$util.introduction();

const msg = 'This is post/index';
console.log(msg);
resources/scss/post/index.scss
h1 {
    font-size: 20px;
    color: skyblue;
}
resources/js/_share/utility.js
export const introduction = () => {
    const msg = 'This is utility file';
    console.log(msg);
};

本題のwebpack.config.js

webpack.config.js
// Node.jsに組み込まれているモジュール。出力先などの指定を手軽に絶対パスで記述できる。
const path = require('path');

// scssのcompileに使用。個別のcssファイルへの出力を可能にする。
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

// eslintを導入するならこれ
// eslint-loaderは非推奨になる & failOnErrorが機能しないので、こちらを使用
// ref:https://github.com/webpack-contrib/eslint-loader/issues/334
const EslintWebpackPlugin = require('eslint-webpack-plugin');

// 複数entryを実装するのに便利なパッケージ
const glob = require('glob');

const srcDir = './resources/js';
const entries = {};

// globを使用して任意のjsファイルを取得し、複数entry用のobjectを生成
glob.sync('**/*.js', {
    // compileから除外するパターンを指定(importされる前提の共通ファイルなど)
    // 複数パターンを指定する場合は配列で記述
    ignore: '**/_*/*.js',

    // ルートとなるディレクトリを指定
    // 下記jsFileNameに入るのはこのディレクトリ以降のパスになる
    cwd: srcDir,

// cwdの指定により、./resources/js/post/index.jsの場合、 jsFileNameにはpost/index.jsとが入る
}).forEach(jsFileName => {
    // outputの[name]に対応するため、entriesのkeyからは.js拡張子を除く
    const fileNameExceptExt = jsFileName.replace(/\.js$/, '');

    // path.resolveにより、絶対パスが入る
    // { 'post/index': 'sample_app/resources/js/post/index.js', ... } のようになる
    entries[fileNameExceptExt] = path.resolve(srcDir, jsFileName);
});

module.exports = {
    // modeによって処理を分岐したければ process.env.NODE_ENV にmodeが格納されているので、それを使う
    mode: 'development',
    // developer toolで見れるソースマップの種類のオプション
    devtool: 'eval-cheap-module-source-map',
    // 上で複数entry用にglobを使用して作ったやつ
    entry: entries,
    output: {
        // 出力先はsample_app/publicディレクトリ
        path: path.resolve(__dirname, 'public'),
        // [name]にはentriesのkeyに指定したパスが入る
        // [name]がpost/indexなら、sample_app/public/post/index.jsに出力される
        filename: 'js/[name].js',
    },
    resolve: {
        alias: {
            // import時の起点となるディレクトリを指定できる。
            // これにより、resources/js/post/index.js では
            // import * as $form from '../_share/form.js' と書かずに
            // import * as $form from '@js/_share/form' と書ける
            '@scss': path.resolve(__dirname, 'resources/scss'),
            '@js': path.resolve(__dirname, 'resources/js'),
        },
    },
    module: {
        // loaderを使用する際のルールを記述
        rules: [
            {
                // include配下のtestに合致するファイルだけ、useで指定したloaderを使用
                test: /\.js$/,
                // excludeで指定も可能
                include: path.resolve(__dirname, 'resources/js'),
                // useに記述したloaderは、記述と逆順に実行されるので複数指定時は注意
                use: ['babel-loader'],
            },
            {
                test: /\.scss$/,
                include: path.resolve(__dirname, 'resources/scss'),
                use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
            },
        ],
    },
    plugins: [
        new MiniCssExtractPlugin({
            // [name]にはentryで指定したkeyが入る
            // post/index.jsで読み込んでいるscssはpublic/css/post/index.cssに出力される
            filename: 'css/[name].css',
        }),
        new EslintWebpackPlugin({
            fix: false,
            // eslintエラー時、compileを中断するか
            failOnError: false,
        }),
    ],
};

package.jsonはこんな感じ

package.json
"scripts": {
    "watch": "webpack --watch",
    "dev": "webpack",
    "prod": "webpack --mode production --no-devtool"
},
"devDependencies": {
    "@babel/core": "^7.13.10",
    "@babel/preset-env": "^7.13.12",
    "babel-loader": "^8.2.2",
    "css-loader": "^5.2.0",
    "mini-css-extract-plugin": "^1.4.0",
    "sass": "^1.32.8",
    "sass-loader": "^11.0.1",
    "eslint": "7.7.0",
    "eslint-config-airbnb": "18.2.0",
    "eslint-plugin-import": "2.22.0",
    "eslint-plugin-jsx-a11y": "6.3.1",
    "eslint-plugin-react": "7.20.6",
    "eslint-webpack-plugin": "^3.1.1",
    "glob": "^7.2.0",
    "webpack": "^5.30.0",
    "webpack-cli": "^4.6.0",
    "webpack-merge": "^5.7.3"
}

npm run prodを実行してみる

コンパイル・圧縮された記述がそれぞれjs,cssファイルに出力される

public/js/post/index.js
(()=>{"use strict";console.log("This is post/index"),console.log("This is utility file")})();
public/css/post/index.css
h1{font-size:20px;color:skyblue}

参考書籍・サイト