webpackインストール後のセットアップパターンまとめ。Babel/ESLint/SASSなどを用いてフロントエンド開発環境を整える

公開
更新
注意最終更新から 7 年以上経過しています。最新の情報もあわせてご確認ください。

webpack を用いたフロントエンド開発環境構築 1. webpack インストール 2. webpack セットアップ

webpack を用いたフロントエンド開発環境構築を行います。環境やインストールについては前回の記事を参考にしてください。

最小構成のビルド

まずは webpack を使って最小のビルドを行ってみます。プロジェクトルート以下に JS ファイルと HTML ファイルを作成します。構成は以下の通りです。

project_root/
├─ assets
│   └─ js
│     └─ index.js
├─ htdocs
│   ├─ index.html
│   └─ js
│     └─ app.js
├─ node_modules/
├─ package.json
├─ package-lock.json
└─ webpack.config.js

黄色の字の部分のファイルを作成していきます。

htdocs ディレクトリ以下には、公開ソースを設置していきます。

htdocs/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>webpack sample</title>
</head>
<body>

<script src="js/app.js"></script>
</body>
</html>

HTML ファイルには、最少部分のみを記述しています。

htdocs/js/app.js は、未作成でも OK, もしくは空っぽの JS ファイルを作成しておきます。

assets ディレクトリ以下には開発ソースを設置していきます。

assets/js/index.js
alert('I built it with webpack!!');

アラートを表示させるスクリプトのみを記述しています。

最後に、webpack.config.js を作成します。このファイルで webpack の動作設定を行います。

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

module.exports = {
    mode: "development",
    entry: ["./assets/js/index.js"],
    output: {
        filename: "app.js",
        path: path.join(__dirname, 'htdocs/js'),
    }
};

上から解説します。

const path = require('path');

出力ファイルを指定する部分で絶対パスを作成する為に、Node.js の path モジュールを読み込みます。

Path モジュールは、ファイルパスについての操作をいい感じに行ってくれる Node.js の標準モジュールです。
https://nodejs.org/api/path.html

module.exports = {
    mode: "development",
    entry: ["./assets/js/index.js"],
    output: {
        filename: "app.js",
        path: path.join(__dirname, 'htdocs/js'),
    }
};

webpack の設定をここで行います。

webpack4 からは mode プロパティが登場し、実行時に引数を渡さない場合はここの設定は必須になっています。設定値は以下の3つです。

  • production
  • development
  • none

尚、未設定の場合は production 扱いとなり、警告が表示されます。

entry プロパティでは、エントリポイントを指定します。エントリポイントは、ビルドを行う基となるファイルを指定します。例えばここで指定した JS ファイルから派生する JS ファイル群(など)を1つのファイルへバンドルされる事になります。

output プロパティでは、出力先ファイルに関する設定を行います。 filename プロパティにはファイル名、path プロパティにはファイルまでのパスを指定します。

path プロパティでは、先頭で読み込んだ path モジュールを用い、join() メソッドで「プロジェクトルートまでのパス」と「プロジェクトルートから公開ファイルまでのパス」を連結しています。

最後に、package.json の script プロパティに以下を追記して、webpack コマンドを登録しておきます。

package.json
"scripts": {
    "dev": "webpack --mode development",
    "production": "webpack --mode production"
},

これで設定は完了です。コンソールからプロジェクトルートへ移動し、ビルドを行ってみます。

# プロジェクトルートへ移動
cd /path/to/project_root

# webpack でビルドを実行
npm run dev

# 実行結果
[demo@localhost src]$ npm run dev

> src@1.0.0 dev /var/www/html/src
> webpack --mode development

Hash: 75032354ef21564e4aaa
Version: webpack 4.28.2
Time: 170ms
Built at: 01/02/2019 3:02:21 PM
 Asset      Size  Chunks             Chunk Names
app.js  4.25 KiB    main  [emitted]  main
Entrypoint main = app.js
[0] multi ./assets/js/index.js 28 bytes {main} [built]
[./assets/js/index.js] 92 bytes {main} [built]

ビルドが完了すると、htdocs/js 配下に app.js が生成されます。

ブラウザからアクセスして動作を確認してみます。

webpack 動作確認

webpack で JS ファイルのビルドが行われ、app.js に出力されるまでを確認できました。

トランスコンパイラを用いたコンパイル&複数ファイルのバンドル

次は、ES6(ES2015)で記述した JS のコンパイルと、複数のファイルをビルド(バンドル)してみます。

Babel のインストール

ES6 等を扱う為にトランスコンパイラである Babel をインストールします。

Babel
https://babeljs.io/

# プロジェクトルートへ移動
cd /path/to/project

# Babel インストール
npm i -D babel-loader @babel/core @babel/preset-env

# 実行結果
[demo@localhost src]# npm i -D babel-loader @babel/core @babel/preset-env
+ babel-loader@8.0.4
+ @babel/core@7.2.2
+ @babel/preset-env@7.2.3
added 105 packages from 24 contributors and audited 63645 packages in 46.798s
found 0 vulnerabilities

webpack.config.js の設定

コンパイル時に Babel を用いる為に、webpack.config.js に以下の設定を追加します。

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

module.exports = {
    mode: "development",
    entry: ["./assets/js/index.js"],
    output: {
        filename: "app.js",
        path: path.join(__dirname, 'htdocs/js'),
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: [
                               '@babel/preset-env',
                            ]
                        }
                    }
                ]
            }
        ]
    }
};

module プロパティ部分が追加した部分です。

JS ファイル作成

設定は済んだので、次はコンパイルを行う JS ファイルを用意します。今回は ES6 での記法で且つ複数ファイルを扱うので、assets ディレクトリ以下をこんな構成にして単純な四則演算を行うスクリプトを作成していきます。

assets/
└─ js
    ├─ Component
    │   └─ calculatorComponent.js
    ├─ Controller
    │   └─ calculationController.js
    └─ index.js

それぞれを実装していきます。実行されると、コンソールに結果を表示するようにします。

assets/js/Component/calculatorComponent.js
export default class CalculatorComponent {
    constructor(value) {
        this.value = value;
        this.total = 0;
        this.operation = [];
    }
    getInitValue() {
        return this.value;
    }
    getValue() {
        this.total = this.getInitValue();
        for (let i=0; i<this.operation.length; i++) {
            this.calculation(this.operation[i].ope, this.operation[i].val);
        }

        return this.total;
    }
    calculation(operation, value) {
        switch (operation) {
            case "add": this.total += value; break;
            case "sub": this.total -= value; break;
            case "mul": this.total *= value; break;
            case "div": this.total /= value; break;
        }
    }
    reset() {
        this.operation = [];
    }
    cancel() {
        this.operation.pop();
    }
    add(value) {
        this.operation.push({"ope": "add", "val": value});
    }
    sub(value) {
        this.operation.push({"ope": "sub", "val": value});
    }
    mul(value) {
        this.operation.push({"ope": "mul", "val": value});
    }
    div(value) {
        this.operation.push({"ope": "div", "val": value});
    }
}
assets/js/Controller/calculationController.js
import Calculator from '../Component/calculatorComponent';

// 100 を基にする
let calculator = new Calculator(100);

// 130 加算
calculator.add(130);
// 20 減算
calculator.sub(20);
// 4 乗算
calculator.mul(4);
// 2 除算
calculator.div(2);

// 操作を1つキャンセル(除算取り消し)
calculator.cancel();

// 結果出力
console.log(
    '計算結果:' + calculator.getValue()
);

// 演算リセット
calculator.reset();

// 結果出力
console.log(
    'リセット結果:' + calculator.getValue()
);
assets/index.js
import calculation from './Controller/calculationController';

動作確認

それでは webpack でコンパイル&ビルドを実行します。

# プロジェクトルートへ移動
cd /path/to/project_root

# webpack でビルドを実行
npm run dev

# 実行結果
[demo@localhost src]$ npm run dev

> src@1.0.0 dev /var/www/html/src
> webpack --mode development

Hash: 9b4692b388fa64ca418a
Version: webpack 4.28.2
Time: 841ms
Built at: 01/02/2019 6:59:26 PM
 Asset      Size  Chunks             Chunk Names
app.js  8.75 KiB    main  [emitted]  main
Entrypoint main = app.js
[0] multi ./assets/js/index.js 28 bytes {main} [built]
[./assets/js/Component/calculatorComponent.js] 2.49 KiB {main} [built]
[./assets/js/Controller/calculationController.js] 415 bytes {main} [built]
[./assets/js/index.js] 61 bytes {main} [built]

ブラウザからアクセスして動作確認を行います。

Babel 動作確認画面

ES6記法が Babel によってトランスコンパイルされ、実行可能なソースコードへ変換された事と、複数の JS ファイルが app.js へ集約された事が確認できました。

コードテスト(静的コード解析)

webpack では、テストも同時に回す事が出来ます。コンパイル&ビルドを行うのと同時にテストも回す事で、スクリプトの信頼性も高まります。

今回は ESLint を導入して静的コード解析を行います。

ESLint のインストール

コードテストを回す為に ESLint をインストールします。

ESLint
https://eslint.org/

# プロジェクトルートへ移動
cd /path/to/project

# ESlint インストール
npm i -D eslint eslint-loader

# 実行結果
[demo@localhost src]$ npm i -D eslint eslint-loader
+ eslint-loader@2.1.1
+ eslint@5.11.0
added 1001 packages from 132 contributors and audited 20177 packages in 924.854s
found 0 vulnerabilities

webpack.config.js の設定

インストールが完了したら、webpack 実行時に静的コード解析を行うように設定ファイルに以下を追記します。

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

module.exports = {
    mode: "development",
    entry: ["./assets/js/index.js"],
    output: {
        filename: "app.js",
        path: path.join(__dirname, 'htdocs/js'),
    },
    module: {
        rules: [
            // Babel
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                '@babel/preset-env',
                            ]
                        }
                    }
                ]
            },
            // ESlint
            {
                test: /\.js$/,
                exclude: /node_modules/,
                enforce: 'pre',
                use: [
                    {
                        loader: 'eslint-loader',
                    },
                ]
            }
        ]
    }
};

.ESLintrc の作成

ESLint の解析ルールを設定する為に、プロジェクトルートに.eslintrc(ESLint の設定ファイル)を作成し、以下を記述します。

.eslintrc
{
  "extends": ["eslint:recommended"],
  "plugins": [],
  "parserOptions": {
    "sourceType": "module",
    "ecmaVersion": 2016
  },
  "env": {
    "browser": true,
    "es6": true
  },
  "globals": {},
  "rules": {
    "semi": "error",
    "no-console": "warn",
    "no-unused-vars": "warn"
  }
}

注目してほしいのは extends プロパティです。ここで eslint:recommended と指定する事で、ESLint が推奨するテスト項目が既に設定されます。

具体的にどんなチェック項目があるのかは、以下から確認できます。
https://eslint.org/docs/rules/

とはいえもちろん、個別に指定する事も可能です。その場合は rules プロパティに指定していきます。今回は、推奨設定に入っていない「セミコロン忘れ」を追加し、さらに、いちいちエラーにされると辛い console 出力メソッドと不使用変数のエラーレベルを引き下げています。

semi エラーに設定 ASI(自動セミコロン挿入)の使用を禁止とし、セミコロン忘れをチェックします。 no-console エラー → 警告に設定 console の使用を許可しない。 console.log() などがソースコードに残っていないかをチェックします。 no-unused-vars エラー → 警告に設定未使用の変数を許可しない。宣言・定義されているがその後、使用されていない変数がないかをチェックします。ルールや指定できる項目は沢山あるので、以下で確認してください。

Configuring ESLint
https://eslint.org/docs/user-guide/configuring

動作確認

それでは webpack でコンパイルと同時に静的コード解析を実行します。

# プロジェクトルートへ移動
cd /path/to/project_root

# webpack でコンパイルを実行
npm run dev

すると、以下のようなメッセージが出力されます。

/var/www/html/src/assets/js/Controller/calculationController.js
  19:1  warning  Unexpected console statement  no-console
  27:1  warning  Unexpected console statement  no-console

2 problems (0 errors, 2 warnings)

calculationController.js で console.log() を出力しているので、その部分で警告が出ています。これらを除去すれば、警告は表示されなくなります。

このようにして、webpack でコンパイルを行うのと同時にインデントとスペースの混合やコンソールメソッドなど、指定したルールの基でコードテストを実行する事が出来ます。

CSS を出力する

webpack では JavaScript だけでなく CSS も扱う事が出来ます。

複数の CSS ファイルをバンドルする

複数に分けた CSS ファイルを1つにまとめて出力します。また、JS ファイルと CSS ファイルは別々に出力する形で行います。

まずは CSS ファイルを作成し、style を記述していきます。 assets ディレクトリ配下に css ディレクトリを作成し、その配下に以下3つの CSS ファイルを作成します。

assets
├─ css
│   ├─ index.css
│   ├─ style_a.css
│   └─ style_b.css
style_a.css
body {
    background-color: #666666;
}
style_b.css
p {
    font-size: 16px;
    color: #FFFFFF;
}
index.css
@import "style_a.css";
@import "style_b.css";

次に、CSS の解釈に必要な「css-loader 」と、CSS として切り出す為に「mini-css-extract-plugin 」をインストールします。

# プロジェクトルートへ移動
cd /path/to/project

# インストール
npm i -D css-loader mini-css-extract-plugin

# 実行結果
[demo@localhost src]$ npm i -D css-loader mini-css-extract-plugin
+ css-loader@2.1.0
+ mini-css-extract-plugin@0.5.0

webpack.config.js を以下に設定します。

webpack.config.js
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

module.exports = {
    mode: "development",
    entry: {
        app: "./assets/js/index.js",
        style: "./assets/css/index.css"
    },
    output: {
        filename: "js/[name].js",
        path: path.join(__dirname, 'htdocs'),
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: "css/[name].css",
        })
    ],
    module: {
        rules: [
            // babel
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                '@babel/preset-env',
                            ]
                        }
                    }
                ]
            },
            // eslint
            {
                test: /\.js$/,
                exclude: /node_modules/,
                enforce: 'pre',
                use: [
                    {
                        loader: 'eslint-loader',
                    },
                ]
            },
            // CSS
            {
                test: /\.css$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                    },
                    'css-loader',
                ]
            }
        ]
    }
};
  • 定数 MiniCssExtractPlugin に mini-css-extract-plugin を読み込んでいます。
  • エントリポイントを JS と CSS で2つ設定しています。ここでのプロパティ名が filename プロパティで定義されている [name] に入ります。つまり出力される JS ファイルは「htdocs/js/app.js 」になります。
  • plugins プロパティで MiniCssExtractPlugin をインスタンス化し、出力ファイルを指定します。[name]にはエントリポイントで指定したプロパティ名がセットされるので、出力される CSS ファイルは htdocs/css/style.css になります。
  • rules プロパティに CSS の指定を行っています。

最後に、出力した CSS ファイルを読み込む記述を HTML ファイルに追記しておいます。

htdocs/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>webpack sample</title>
    <link rel="stylesheet" href="css/style.css" />
</head>
<body>
<p>webpack sample</p>

<script src="js/app.js"></script>
</body>
</html>

一通りの設定が終わったのでコンパイルを回して動作確認を行います。

# プロジェクトルートへ移動
cd /path/to/project_root

# webpack でコンパイルを実行
npm run dev

2つの CSS ファイルが統合され、htdocs/css 配下に style.css が生成されます。

htdocs
├─ css
│   └─ style.css
htdocs/css/style.css
body {
    background-color: #666666;
}

p {
    font-size: 16px;
    color: #FFFFFF;
}

ブラウザから確認してみます。

CSS 出力動作確認

出力された CSS ファイルが読み込まれ意図した通りの結果になった事を確認できました。

CSS ファイルを minify する

webpack4 からは、webpack.config.js の mode プロパティに production を指定するか、webpack コマンドでのビルドの際に --mode production を付与する事で自動的に JS ファイルが minify されますが、CSS には適用されないので、個別に設定する必要があります。

CSS の minify を行う為に「optimize-css-assets-webpack-plugin 」を導入しますが、同時に JS 側の minify も動作するように「terser-webpack-plugin 」も導入します。

optimize-css-assets-webpack-plugin
https://webpack.js.org/plugins/mini-css-extract-plugin/
https://github.com/NMFR/optimize-css-assets-webpack-plugin

terser-webpack-plugin
https://github.com/webpack-contrib/terser-webpack-plugin

# プロジェクトルートへ移動
cd /path/to/project_root

# インストール
npm i -D optimize-css-assets-webpack-plugin terser-webpack-plugin

webpack.config.js を以下に変更します。

webpack.config.js
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
    mode: "development",
    entry: {
        app: "./assets/js/index.js",
        style: "./assets/css/index.css"
    },
    output: {
        filename: "js/[name].js",
        path: path.join(__dirname, 'htdocs'),
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: "css/[name].css",
        })
    ],
    optimization: {
        // production minify
        minimizer: [
            new OptimizeCSSAssetsPlugin(),
            new TerserPlugin({
                terserOptions: {
                    ecma: 6,
                    compress: true,
                    output: {
                        comments: false,
                        beautify: false
                    }
                }
            })
        ],
    },
    module: {
        rules: [
            // Babel
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                '@babel/preset-env',
                            ]
                        }
                    }
                ]
            },
            // ESLint
            {
                test: /\.js$/,
                exclude: /node_modules/,
                enforce: 'pre',
                use: [
                    {
                        loader: 'eslint-loader',
                    },
                ]
            },
            // CSS
            {
                test: /\.css$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                    },
                    'css-loader'
                ]
            }
        ]
    }
};

インストールした2つを定数で読み込み、optimization プロパティを追加しています。

コンパイルを回して動作確認を行います。 production モードで回します。

# プロジェクトルートへ移動
cd /path/to/project_root

# webpack でコンパイルを実行
npm run production

出力された htdocs/js/app.js と htdocs/css/style.css を確認すると、両ファイルが minify されて出力されている事が確認できます。

SASS ファイルをコンパイルする

スタイルシート言語である SASS のコンパイルを行い CSS を出力します。

まずは SCSS ファイルを作成し、style を記述していきます。 assets ディレクトリ配下に sass ディレクトリを作成し、その配下に以下3つの SCSS ファイルを作成します。

assets
├─ sass
│  ├─ index.scss
│  ├─ _style_a.scss
│  └─ _style_b.scss

それぞれの SCSS ファイルに style を定義していきます。

_style_a.scss
body {
    padding: 20px;
    font-size: 18px;
}
_style_b.scss
$color_a: #dc143c;
$color_a_hover: #6495ed;

p {
    a {
        color: $color_a;
        &:hover {
            color: $color_a_hover;
        }
    }
}
index.scss
@import "style_a";
@import "style_b";

次に、SCSS ファイルをロードして CSS にコンパイル為に「sass-loader 」と「node-sass 」をインストールします。

sass-loader
https://github.com/webpack-contrib/sass-loader

node-sass
https://github.com/sass/node-sass

# プロジェクトルートへ移動
cd /path/to/project_root

# インストール
npm i -D sass-loader node-sass

webpack.config.js を以下に変更します。尚、今回はソースマップも有効にします。

webpack.config.js
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const TerserPlugin = require('terser-webpack-plugin');

const MODE = 'development';

const ENABLE_SOURCE_MAP = (MODE === 'development');


module.exports = {
    mode: MODE,
    entry: {
        app: "./assets/js/index.js",
        style: "./assets/sass/index.scss"
    },
    output: {
        filename: "js/[name].js",
        path: path.join(__dirname, 'htdocs'),
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: "css/[name].css",
        })
    ],
    optimization: {
        // production minify
        minimizer: [
            new OptimizeCSSAssetsPlugin(),
            new TerserPlugin({
                terserOptions: {
                    ecma: 6,
                    compress: true,
                    output: {
                        comments: false,
                        beautify: false
                    }
                }
            })
        ],
    },
    module: {
        rules: [
            // Babel
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                '@babel/preset-env',
                            ]
                        }
                    }
                ]
            },
            // ESLint
            {
                test: /\.js$/,
                exclude: /node_modules/,
                enforce: 'pre',
                use: [
                    {
                        loader: 'eslint-loader',
                    },
                ]
            },
            // SCSS
            {
                test: /\.scss$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            url: false,
                            sourceMap: ENABLE_SOURCE_MAP,
                            importLoaders: 2
                        },
                    },
                    {
                        loader: 'sass-loader',
                        options: {
                            sourceMap: ENABLE_SOURCE_MAP,
                        }
                    },
                ],
            },
        ]
    }
};

ソースマップを development 時でのみ有効にする為に、出力オプション(development OR production)を定数 MODE にセットし、その値でソースマップの有効・無効を定数 ENABLE_SOURCE_MAP にセットしています。

コンパイルして動作確認を行います。

# プロジェクトルートへ移動
cd /path/to/project_root

# webpack でコンパイルを実行
npm run dev

htdocs/css 配下に style.css が生成されます。

htdocs/css/style.css
body {
    padding: 20px;
    font-size: 18px;
}

p a {
    color: #dc143c; 
}
p a:hover {
    color: #6495ed;
}

SCSS ファイルがコンパイルされ、CSS ファイルが書き出されている事が確認できました。

画像を base64 エンコードでバンドルする

CSS で画像を背景などに設定する場合がありますが、それらの画像を base64 エンコード(data URI scheme)して CSS ファイルへ埋め込み出力します。

style で画像を扱う場合は、CSS スプライトと言って必要な画像を1つにまとめる事によってリクエスト数を削減する手法が用いられますが、画像自体を base64 エンコードで CSS ファイルに埋め込んでしまう事でもリクエスト数を削減できます。(データサイズと対応ブラウザに注意)

まずはファイルを base64 URI に変換する webpack 用のローダーである「url-loader 」をインストールします。

url-loader
https://github.com/webpack-contrib/url-loader

# プロジェクトルートへ移動
cd /path/to/project_root

# インストール
npm i -D url-loader

webpack.config.js を以下に変更します。

webpack.config.js
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const TerserPlugin = require('terser-webpack-plugin');

const MODE = 'development';

const ENABLE_SOURCE_MAP = (MODE === 'development');


module.exports = {
    mode: MODE,
    entry: {
        app: "./assets/js/index.js",
        style: "./assets/sass/index.scss"
    },
    output: {
        filename: "js/[name].js",
        path: path.join(__dirname, 'htdocs'),
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: "css/[name].css",
        })
    ],
    optimization: {
        // production minify
        minimizer: [
            new OptimizeCSSAssetsPlugin(),
            new TerserPlugin({
                terserOptions: {
                    ecma: 6,
                    compress: true,
                    output: {
                        comments: false,
                        beautify: false
                    }
                }
            })
        ],
    },
    module: {
        rules: [
            // Babel
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            presets: [
                                '@babel/preset-env',
                            ]
                        }
                    }
                ]
            },
            // ESLint
            {
                test: /\.js$/,
                exclude: /node_modules/,
                enforce: 'pre',
                use: [
                    {
                        loader: 'eslint-loader',
                    },
                ]
            },
            // SCSS
            {
                test: /\.scss$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            url: true,
                            sourceMap: ENABLE_SOURCE_MAP,
                            importLoaders: 2
                        },
                    },
                    {
                        loader: 'sass-loader',
                        options: {
                            sourceMap: ENABLE_SOURCE_MAP,
                        }
                    },

                ],
            },
            // image base64 encode
            {
                test: /\.(jpg|gif|png)$/,
                loaders: 'url-loader'
            },
        ]
    }
};

rules プロパティへ、画像ファイルに対して url-loader を用いる指定を行っています。

HTML は以下に変更しています。

htdocs/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>webpack sample</title>
    <link rel="stylesheet" href="css/style.css" />
</head>
<body>
<p>webpack sample <a href="">link</a></p>

<ul>
    <li>Webpack</li>
    <li>JavaScript</li>
    <li>ES6</li>
    <li>SCSS</li>
    <li>Base64</li>
</ul>

<script src="js/app.js"></script>
</body>
</html>

新しい SCSS ファイルを作成して画像を指定します。

assets/sass/_base64_sample.scss
ul {
    li {
        list-style-image : url("./images/sample_ico.png");
    }
}

リストマークを画像にする指定になっています。

コンパイルを行います。

# プロジェクトルートへ移動
cd /path/to/project_root

# webpack でコンパイルを実行
npm run dev

生成された style.css を確認すると、画像ファイルが base64 エンコードされている事が確認できます。

htdocs/css/style.css
ul li {
    list-style-image: url(data:image/png;base64,....

ブラウザから確認すると、正常に表示されている事が確認できます。

base64 エンコード確認画面

まとめ

以上で作業は終了です。 webpack では他にも様々な処理を扱えるので試してみてください。

Ritolabo