webpack を使って Sass をコンパイルする方法

Sasswebpack を使ってコンパイルする方法の覚書です。webpack は JavaScript だけではなく、CSS や Sass をバンドルすることができます。

webpack 5 で新しく導入された Asset Modules など内容を version 5 に合わせて書き換えました。また、PostCSS Preset Env の使い方も追加しました(2022年1月5日)。

以下は Node.js がインストールされていることを前提にしています。また、主なパッケージのバージョンは以下になります(環境は Mac での例になります)。

作成日:2020年11月9日

関連ページ:

webpack のインストール

webpack は Node.js のパッケージマネージャ npm を使ってインストールすることができます。

この例では作業ディレクトリ(myProject)を作成し、そこへローカルインストールを行います。

作業用フォルダーを任意の場所に作成しそこに cd で移動します。

以下では mkdir でフォルダを作成していますが、右クリックから「新規作成」などでも OK です。

$ mkdir myProject  return //フォルダーを任意の場所に作成
        
$ cd myProject  return //作成したフォルダー(ディレクトリ)に移動

npm を使ってインストールする場合、まず npm init コマンドで package.json を生成します。デフォルトのオプションで package.json を生成すれば良いので -y オプションを指定します。

$ npm init -y return //package.json をデフォルトで生成

//生成された package.json のパスと内容が表示される
Wrote to /Applications/MAMP/htdocs/myProject/package.json:

{
  "name": "myProject",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

続いて npm install コマンドで webpack をインストールします。

webpack 4.0以降では、コマンドライン操作用のパッケージは webpack-cli という別パッケージで提供されているので併せてインストールします。

オプションの -D(または --save-dev)は開発環境で使うパッケージ(バンドルにのみ使用し、本番ビルドに含めない場合)に指定するオプションです。

以下のようにバージョンを指定しないでパッケージをインストールすると最新版がインストールされます。

$ npm install -D webpack webpack-cli  return //webpack と webpack-cli をインストール
        
added 119 packages, and audited 120 packages in 5s

インストールが完了すると、インストールされたパッケージは node_modules というフォルダに保存され、package.json 及び package-lock.json という npm の設定ファイルが更新されます。

以下は tree コマンドで myProject 内のディレクトリやファイルを表示する例です。

$ tree -L 2  return //ツリー表示
.
├── node_modules  //npm でインストールされるパッケージのディレクトリ
│   ├── @discoveryjs
│   ├── @types
│   ├── @webassemblyjs
│   ├── @webpack-cli
│
│    ・・・中略・・・
│
│   ├── webpack
│   ├── webpack-cli
│   ├── webpack-merge
│   ├── webpack-sources
│   ├── which
│   └── wildcard
├── package-lock.json  //npm の設定ファイル
└── package.json  //npm の設定ファイル

インストールされた webpack と webpack-cli のバージョンは npx コマンドに -v(または --version )オプションを指定して webpack を実行して確認できます。

$ npx webpack -v
webpack: 5.65.0
webpack-cli: 4.9.1
webpack-dev-server not installed

webpack guides: installation

関連ページ:

初期構成の作成

初期構成として以下のようなフォルダとファイルを作成します。

ビルドを実行するとデフォルトでは src フォルダのファイルがコンパイルされて dist フォルダに出力されます(dist フォルダは作成しなくても自動的に生成されます)。

myProject
├── dist  //ビルド時に自動的に生成されるフォルダ
├── index.html  //追加した HTML ファイル(表示用)
├── node_modules
├── package-lock.json
├── package.json
└── src  //追加したフォルダ
       ├── index.js  //追加した Javascript ファイル(エントリーポイント)
       └── style.scss  //追加した Sass ファイル

エントリーポイントの index.js では import を使って同じ src フォルダの Sass ファイル(style.scss)を読み込みます。

src/index.js
// スタイルシート(Sass)を読み込む
import './style.scss';

Sass ファイルには確認用に Sass 変数を使った設定を記述しています。

src/style.scss
$p_color: green;
$p_bg_color: yellow;
 
p {
  color: $p_color;
  background-color: $p_bg_color;
}

index.html では Sass がコンパイルされて出力される dist フォルダの CSS(style.css まだこの時点では存在しない)を読み込みます。

MiniCssExtractPlugin というプラグインを使うと CSS をバンドルせずに(CSS を抽出して別ファイルとして) dist フォルダに出力することができます。この例では style.css という名前で CSS ファイルを出力します。

この例では Javascript ファイルはバンドルしませんが、Javascript もバンドルする場合は dist フォルダに main.js というファイルが出力されるのでそれを読み込みます。以下ではコメントアウトしています。

index.html
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="dist/style.css">
<title>Sass 読み込みサンプル</title>
</head>

<body>
<div class="sass_sample">
  <h1>Sass</h1>
  <p>sample text.</p>
</div>
<!--<script src="dist/main.js"></script>-->
</body>
</html>

ローダーとプラグインのインストール

以下の基本的なローダープラグインをインストールします。

  • css-loader:CSSの @import や url() を JavaScript の import や require() に変換するモジュール
  • sass-loader:Sass を CSS へ変換するためのモジュール
  • sass:Sass をコンパイルするためのモジュール(Dart Sass
  • MiniCssExtractPlugin:CSS を別ファイルとして出力するためのプラグイン

Sass のモジュールは sass(Dart Sass)を使用しています。LibSass(Node Sass)は将来的には非推奨になるようです(LibSass is Deprecated)。

この例では MiniCssExtractPlugin.loader を使って CSS を別ファイルとして出力していますが、MiniCssExtractPlugin.loader を使わずに、style-loader をインストールして JavaScript から style 要素を生成して CSS を head 要素に挿入することもできます。

npm install コマンドに -D(または --save-dev) オプションを指定してモジュールとプラグインをインストールします。

$ npm install -D css-loader sass-loader sass mini-css-extract-plugin return
          
added 44 packages, and audited 164 packages in 3s

上記インストールを実行後、package.json を確認すると devDependencies にインストールしたモジュールとプラグインがリストされています。

package.json
{
  "name": "myproject",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-loader": "^6.5.1",
    "mini-css-extract-plugin": "^2.4.5",
    "sass": "^1.45.2",
    "sass-loader": "^12.4.0",
    "webpack": "^5.65.0",
    "webpack-cli": "^4.9.1"
  }
}

main の指定はパッケージを NPM で公開しない場合は不要なので、main の行(5行目)を削除し、プロジェクトを誤って公開しないようにするため "private": true を追加します。

description にはプロジェクトの説明などを記述しても良いですし、削除して問題ありません。keywords や author、license も削除しても問題ありません。

package.json(変更後)
{
  "name": "myproject",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-loader": "^6.5.1",
    "mini-css-extract-plugin": "^2.4.5",
    "sass": "^1.45.2",
    "sass-loader": "^12.4.0",
    "webpack": "^5.65.0",
    "webpack-cli": "^4.9.1"
  }
}

fibers

オプションで fibers というパッケージをインストールすることができます。

fibres を使用することでコンパイルの速度を速くすることができるようです(※注)。以下は sass-loader からの抜粋です(webpack の sass-laoder にも同じことが記述されています)。

sass(Dart Sass)を使用する場合、非同期コールバックのオーバーヘッドのため、同期コンパイルはデフォルトで非同期コンパイルの2倍の速度であることに注意してください。 このオーバーヘッドを回避するために、fibres パッケージを使用して、同期コードパスから非同期インポーターを呼び出すことができます。

※注

但し fibres のページには「廃止の注意-このプロジェクトの作者は、可能であればその使用を避けることをお勧めします」とあり、また、「node.js の v16.0.0 以降と互換性がありません」とも書かれています。

そのため、node.js の v16.0.0 以降を使う場合は sassOptions で fiber: false を指定して使用しないようにする必要があります。このサンプルの場合、node.js のバージョンは 16.12.0 なので fibers はインストールしていません。

webpack.config.js

webpack の設定ファイル webpack.config.js を作成します。

myProject
├── dist  
├── index.html 
├── node_modules
├── package-lock.json
├── package.json
├── src  
│   ├── index.js  
│   └── style.scss  
└── webpack.config.js   // webpack 設定ファイルを追加

以下が大まかなローダーの処理の流れです。

  • sass-loader を使って Sass を CSS へ変換
  • css-loader で CSS を JavaScript に変換
  • MiniCssExtractPlugin.loader で CSS を 抽出して別ファイルとして出力

指定する際は、MiniCssExtractPlugin.loader, css-loader, sass-loader の順に指定し、後ろから順番(sass-loader → css-loader → MiniCssExtractPlugin.loader の順)に適用されます。

以下は このサンプルの webpack.config.js の例です。

webpack.config.js
//path モジュールの読み込み
const path = require('path');
//MiniCssExtractPlugin の読み込み
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
 
module.exports = {
 
  //エントリポイント(デフォルトと同じなので省略可)
  entry: './src/index.js',  
  //出力先(デフォルトと同じなので省略可)
  output: { 
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      //SASS 及び CSS 用のローダー
      {
        //拡張子 .scss、.sass、css を対象
        test: /\.(scss|sass|css)$/i, 
        // 使用するローダーの指定(後ろから順番に適用される)
        use: [ MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader' ], 
      },
    ],
  },
  //プラグインの設定
  plugins: [
    new MiniCssExtractPlugin({
      // 抽出する CSS のファイル名
      filename: 'style.css',
    }),
  ],
  //source-map タイプのソースマップを出力
  devtool: 'source-map',
  // node_modules を監視(watch)対象から除外
  watchOptions: {
    ignored: /node_modules/  //正規表現で指定
  },
};

MiniCssExtractPlugin

MiniCssExtractPlugin はプラグインなので require() を使って別途読み込む必要があります(4行目)。

また、plugins プロパティに new 演算子で MiniCssExtractPlugin プラグインのインスタンスを生成し、引数に filename プロパティで出力される CSS のファイル名を指定します(27〜32行目)。

devtool

css-loader には CSS のソースマップを有効にするオプション sourceMap がありますが、sourceMap のデフォルトの値(有効かどうかの真偽値)は devtool の設定に依存します。

この例では devtool オプションに source-map を指定して CSS のソースマップも有効にしています(34行目)。JavaScript もバンドルされていればそれらのソースマップも有効になります。

watchOptions

watch オプションを指定して処理対象のファイルを監視する場合に、watchOptions で node_modules ディレクトリを監視対象から除外するように指定しています(36〜38行目)。

sass-loader implementation オプション

sass-loader の implementation オプションは使用する Sass の実装(Dart Sass か LibSass)を決定します。デフォルトでは、ローダーは依存関係に基づいて実装を解決するので、package.json に記述されている実装(sass なら Dart Sass、node-sass なら LibSass)を使用します。

但し、sass と node-sass の両方のモジュールがインストールされている場合は、sass-loader は sass (Dart Sass) を選択しますが、implementation オプションを使って以下のように明示的に sass (Dart Sass) を使用するように指示することもできます。

webpack.config.js
// Sass ローダー
{
  loader: 'sass-loader',
  options: {
    // ローダーに dart-sass を使用することを明示的に指定
    implementation: require('sass'),
  },
},

npm script

package.json の scripts フィールドにコマンド(npm script)を追加して npm run コマンドで webpack を実行できるようにしておきます。

以下では build(ビルド)、dev(開発モード)、watch(監視モード)及び sass-ver(Sass のバージョン表示) のコマンドを追加しています。

package.json
{
  "name": "myproject",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack --mode production",
    "dev": "webpack --mode development",
    "watch": "webpack --mode development --watch",
    "sass-ver": "sass --version"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-loader": "^6.5.1",
    "mini-css-extract-plugin": "^2.4.5",
    "sass": "^1.45.2",
    "sass-loader": "^12.4.0",
    "webpack": "^5.65.0",
    "webpack-cli": "^4.9.1"
  }
}

以下は Sass のバージョンを表示する例です。

$ npm run sass-ver  return 

> myproject@1.0.0 sass-ver
> sass --version

1.45.2 compiled with dart2js 2.15.1

ビルド

npm script に登録した build コマンド(npm run build)を実行するとビルド時に Sass を CSS に変換して出力することができます。

上記を実行すると、dist フォルダに Sass ファイル(style.scss )がコンパイルされた CSS ファイル style.css が出力されます。

main.js という Javascript ファイルが出力されますが、この例ではエントリポイント(index.js)で Sass を読み込んでいるだけなので、main.js は空のファイルになります。

また、ソースマップを有効にしているので、style.css.map や main.js.map も生成されます。

myProject
├── dist //ビルド時に自動的にコンパイルやバンドルされたファイルが出力されるビルドディレクトリ
│   ├── main.js
│   ├── main.js.map //ソースマップファイル(main.js が空の場合は出力されない)
│   ├── style.css
│   └── style.css.map //ソースマップファイル
├── index.html 
├── node_modules
├── package-lock.json
├── package.json
├── src  
│   ├── index.js  
│   └── style.scss  
└── webpack.config.js

npm run build

build コマンド(npm run build)の場合、style.css は以下のように outputStyle は compressed で出力されます。

style.css
p{color:green;background-color:#ff0}

/*# sourceMappingURL=style.css.map*/

npm run dev

dev コマンド(npm run dev)の場合、style.css は以下のようにコメントと共に outputStyle は expanded で出力されます。

/*!***********************************************************************!*\
  !*** css ./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[0].use[2]!./src/style.scss ***!
  \************************************************************************/
p {
  color: green;
  background-color: yellow;
}

/*# sourceMappingURL=style.css.map*/

npm run watch

watch コマンド(npm run watch)を実行すると、処理対象のファイルを監視してファイルが変更されると自動的に dvelopment モードでリビルド(再コンパイル)します。

dvelopment モードでリビルドするのは npm script で "watch": "webpack --mode development --watch" と登録しているためです。

watch モードを終了するには control + c を押します。

$ npm run watch  return 

CSS の画像

CSS で画像を読み込む設定をしている場合、v5 では新しく組み込まれた Asset Modules により画像がロードされるようになっています。

例えば、style.scss(Sass)を以下のように背景画像(background-image)を使うように変更し、src フォルダの中に images フォルダを作成してその中に画像を配置します。

src/style.scss
$p_color: green;
$p_bg_color: yellow;
 
p {
  color: $p_color;
  background-color: $p_bg_color;
}

div {
 /*  背景画像を指定 */
  background-image: url("./images/sample.jpg");
  background-size: cover;
  padding: 30px;
  max-width: 640px;
}
myProject
├── index.html 
├── node_modules
├── package-lock.json
├── package.json
├── src  
│   ├── images
│   │   └── sample.jpg  //background-image で使う背景画像を配置
│   ├── index.js  
│   └── style.scss  //背景画像のスタイルを追加
└── webpack.config.js

npm run build や npm run dev などでビルドを実行します。

$ npm run dev  return

> myproject@1.0.0 dev
> webpack --mode development

asset 98c94d5ec5a800ed1bf8.jpg 192 KiB [emitted] [immutable] [from: src/images/sample.jpg] (auxiliary name: main)  //画像が出力される
asset main.js 2.41 KiB [emitted] (name: main) 1 related asset
asset style.css 542 bytes [emitted] (name: main) 1 related asset
Entrypoint main 2.94 KiB (194 KiB) = style.css 542 bytes main.js 2.41 KiB 3 auxiliary assets
orphan modules 4.87 KiB (javascript) 192 KiB (asset) 1.02 KiB (runtime) [orphan] 10 modules
runtime modules 274 bytes 1 module
cacheable modules 122 bytes (javascript) 251 bytes (css/mini-extract)
  ./src/index.js 72 bytes [built] [code generated]
  ./src/style.scss 50 bytes [built] [code generated]
  css ./node_modules/css-loader/dist/cjs.js!./node_modules/sass-loader/dist/cjs.js!./src/style.scss 251 bytes [built] [code generated]
webpack 5.65.0 compiled successfully in 433 ms

出力先の dist フォルダには style.css などの他に background-image に指定した画像がコピーされて出力されます。

myProject
├── dist //ビルド時に自動的に出力されるビルドディレクトリ
│   ├── 98c94d5ec5a800ed1bf8.jpg  //背景画像が出力される
│   ├── main.js
│   ├── main.js.map
│   ├── style.css
│   └── style.css.map
├── index.html
├── package-lock.json
├── package.json
├── src
│   ├── images
│   │   └── sample.jpg
│   ├── index.js
│   └── style.scss
└── webpack.config.js

コンパイルされて出力された style.css を確認すると以下のようにコピーされた画像が設定されています。

コピーして出力される画像のファイル名(デフォルトはハッシュ値)や出力先などは Asset Modules のオプションで設定できます。

dist/style.css
p {
  color: green;
  background-color: yellow;
}

div {
  background-image: url(98c94d5ec5a800ed1bf8.jpg); /* コピーされた画像 */
  background-size: cover;
  padding: 30px;
  max-width: 640px;
}

Asset Modules

webpack のバージョン4 までは画像などのアセットファイルを読み込むには url-loader や file-loader などの追加のローダーをインストールして設定する必要がありましたが、version 5 からは Asset Modules が導入され、デフォルトで画像などのアセットファイルを扱えるようになっています。

version 5 では url-loader や file-loader、raw-loader は非推奨になっていて、代わりに Asset Modules を使うことが推奨されています。

以下は Asset Modules の設定を追加した webpack.config.js の例です。

15行目では出力先及びコピーして出力されるファイル名を指定しています(出力先の変更)。

29行目では対象とするアセットファイルの拡張子を正規表現で指定し、31行目で type に asset/resource を指定して、画像などのアセットファイルをコピーして出力先に指定しているディレクトリに出力するようにしています。

webpack.config.js
//path モジュールの読み込み
const path = require('path');
//MiniCssExtractPlugin の読み込み
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
 
module.exports = {
 
  //エントリポイント(デフォルトと同じなので省略可)
  entry: './src/index.js',  
  //出力先
  output: { 
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
    //Asset Modules の出力先の指定
    assetModuleFilename: 'images/[name][ext][query]'
  },
  module: {
    rules: [
      //SASS 及び CSS 用のローダー
      {
        //拡張子 .scss、.sass、css を対象
        test: /\.(scss|sass|css)$/i, 
        // 使用するローダーの指定
        use: [ MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader' ], 
      },
      //Asset Modules の設定
      {
        //対象とするアセットファイルの拡張子を正規表現で指定
        test: /\.(png|svg|jpe?g|gif)$/i,
        //画像をコピーして出力
        type: 'asset/resource'  
      },
    ],
  },
  //プラグインの設定
  plugins: [
    new MiniCssExtractPlugin({
      // 抽出する CSS のファイル名
      filename: 'style.css',
    }),
  ],
  //source-map タイプのソースマップを出力
  devtool: 'source-map',
  // node_modules を監視(watch)対象から除外
  watchOptions: {
    ignored: /node_modules/  //正規表現で指定
  },
};

上記のように webpack.config.js を変更してビルドすると、画像ファイルは dist/images に元の画像ファイルと同じ名前でコピーされて出力されます。

myProject
├── dist
│   ├── images
│   │   └── sample.jpg  //ビルドでコピーされて出力される画像ファイル
│   ├── main.js
│   ├── main.js.map
│   ├── style.css
│   └── style.css.map
├── index.html
├── package-lock.json
├── package.json
├── src
│   ├── images
│   │   └── sample.jpg  //背景画像のファイル
│   ├── index.js
│   └── style.scss
└── webpack.config.js

コンパイルされて出力された style.css を確認すると以下のようにコピーされた画像が設定されています。

dist/style.css
p {
  color: green;
  background-color: yellow;
}

div {
  background-image: url(images/sample.jpg); /* コピーされた画像 */
  background-size: cover;
  padding: 30px;
  max-width: 640px;
}

Asset Modules は画像以外にもフォントなどのファイルに使用できます(関連:フォントのロード)。

ソースマップの出力の制御

以下は環境変数 NODE_ENV を使って production モードと development モードでソースマップの出力を変更する例です。

webpack を実行する際に、NODE_ENV にパラメータとして production または development を渡して webpack.config.js で process.env.NODE_ENV の値を取得してモードを判定します。

この例では以下のように package.json の scripts フィールドに NODE_ENV を指定したビルドコマンド(npm script)を追加します(3行目)。

package.json 抜粋
"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "build": "NODE_ENV=production webpack --mode production",
  "dev": "webpack --mode development",
  "watch": "webpack --mode development --watch",
  "sass-ver": "sass --version"
},
{
  "name": "myproject",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "NODE_ENV=production webpack --mode production",
    "dev": "webpack --mode development",
    "watch": "webpack --mode development --watch",
    "sass-ver": "sass --version"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-loader": "^6.5.1",
    "mini-css-extract-plugin": "^2.4.5",
    "sass": "^1.45.2",
    "sass-loader": "^12.4.0",
    "webpack": "^5.65.0",
    "webpack-cli": "^4.9.1"
  }
}

webpack.config.js で以下のように変数 devMode を定義することで、右辺(process.env.NODE_ENV !== 'production')が評価され production モード以外の場合は devMode は true になります(6行目)。

そして devtool の設定で devMode の値を判定し、production モード以外の場合は source-map タイプのソースマップを指定します(45行目)。

devtool オプションに eval を指定すると、production モードではソースマップは出力されません。

webpack.config.js
//path モジュールの読み込み
const path = require('path');
//MiniCssExtractPlugin の読み込み
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
//変数 devMode は production モードの場合は false でその他の場合は true
const devMode = process.env.NODE_ENV !== 'production';

module.exports = {
 
  //エントリポイント(デフォルトと同じなので省略可)
  entry: './src/index.js',  
  //出力先
  output: { 
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
    //Asset Modules の出力先の指定
    assetModuleFilename: 'images/[name][ext][query]'
  },
  module: {
    rules: [
      //SASS 及び CSS 用のローダー
      {
        //拡張子 .scss、.sass、css を対象
        test: /\.(scss|sass|css)$/i, 
        // 使用するローダーの指定
        use: [ MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader' ], 
      },
      //Asset Modules の設定
      {
        //対象とするアセットファイルの拡張子を正規表現で指定
        test: /\.(png|svg|jpe?g|gif)$/i,
        //画像をコピーして出力
        type: 'asset/resource'  
      },
    ],
  },
  //プラグインの設定
  plugins: [
    new MiniCssExtractPlugin({
      // 抽出する CSS のファイル名
      filename: 'style.css',
    }),
  ],
  //production モード以外の場合は source-map タイプのソースマップを指定
  devtool: devMode ? 'source-map' : 'eval',
  // node_modules を監視(watch)対象から除外
  watchOptions: {
    ignored: /node_modules/  //正規表現で指定
  },
};

上記の設定と package.json に指定した npm script により、npm run build を実行するとソースマップファイルは生成されず、npm run dev または npm run watch を実行するとソースマップファイルが生成されます。

CSS や Sass のソースマップの設定はそれぞれのローダーの sourceMap オプションを使って指定することもできます(CSS のソースマップ)。

output.clean

output.clean オプションを使うと、ビルドの実行前に出力先の dist フォルダーをクリーンアップして、実際に使用しているファイルのみが生成されるようにすることができます(version 5.20.0 以上)。

例えば、前述のようにソースマップの出力をモードにより変更している場合、npm run build を実行するとソースマップファイルは生成されませんが、それ以前に npm run dev または npm run watch を実行して生成されたソースマップファイルはそのまま dist フォルダに残っています。

以下は output の clean に true を指定して、ビルドする前に出力先の dist フォルダー内をクリーンアップ(ファイルをすべて削除)してから、新たにファイルを出力するようにする例です(17行目)。

※ 単純に clean: true を指定すると、ビルド実行前に出力先フォルダ(ビルドディレクトリ)内のすべてのファイルが削除されます。手動で配置したファイルなども削除されてしまうので、必要に応じて keep オプションを使ってファイルやディレクトリを削除の対象から除外することができます。

参考:Cleaning up the /dist folderoutput.clean

postCSS

必要に応じて postCSS のプラグイン(モジュール)をインストールして利用することができます。

postCSS のモジュールを利用するには postcss-loaderと postcss をインストールする必要があります。

プラグイン 説明
PostCSS Preset Env 後方互換性を持って変換及びベンダープレフィックスを付与
CSS Declaration Sorter CSS プロパティをソート
PostCSS Sort Media Queries メディアクエリをまとめる

PostCSS Preset Env

PostCSS Preset Env は CSS の新しい仕様を後方互換性を持ってフォールバック(変換)するプラグインで、JavaScript での Babel の babel/preset-env のようなものです。

また、PostCSS Preset Env には Can I use を参照し CSS プロパティに自動でベンダープレフィックスを付与する機能(Autoprefixer)も含まれています。

以下は PostCSS Preset Env を利用する例です。

postcss-loader と postcss、postcss-preset-env をインストールします。

$ npm install -D postcss-loader postcss postcss-preset-env  return
            
added 60 packages, and audited 224 packages in 4s
package.json(例)
{
  "name": "myproject",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "NODE_ENV=production webpack --mode production",
    "dev": "webpack --mode development",
    "watch": "webpack --mode development --watch",
    "sass-ver": "sass --version"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-loader": "^6.5.1",
    "mini-css-extract-plugin": "^2.4.5",
    "postcss": "^8.4.5",
    "postcss-loader": "^6.2.1",
    "postcss-preset-env": "^7.2.0",
    "sass": "^1.45.2",
    "sass-loader": "^12.4.0",
    "webpack": "^5.65.0",
    "webpack-cli": "^4.9.1"
  }
}

webpack.config.js に設定を追加します。

css-loader と sass-loader の間に postcss-loader を追加します(css-loader や style-loader の前に使用しますが、sass などの他のプリプロセッサローダーの後に使用します)。

css-loader の設定では importLoaders オプションを追加して css-loader の前に適用されるローダーの数(2)を設定します(この設定は将来的には変更になる可能性があるようです)。

PostCSS の設定では、plugins に PostCSS Preset Env などのオプションを設定することができます。

webpack.config.js
//path モジュールの読み込み
const path = require('path');
//MiniCssExtractPlugin の読み込み
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
//変数 devMode は production モードの場合は false でその他の場合は true
const devMode = process.env.NODE_ENV !== 'production';

module.exports = {
 
  //エントリポイント(デフォルトと同じなので省略可)
  entry: './src/index.js',  
  //出力先(デフォルトと同じなので省略可)
  output: { 
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
    //ファイルを出力する前にディレクトリをクリーンアップ
    clean: true, 
    //Asset Modules の出力先の指定
    assetModuleFilename: 'images/[name][ext][query]'
  },
  module: {
    rules: [
      //SASS 及び CSS 用のローダー
      {
        //拡張子 .scss、.sass、css を対象
        test: /\.(scss|sass|css)$/i, 
        // 使用するローダーの指定
        use: [ 
          //CSS を別ファイルとして出力するプラグインのローダー
          MiniCssExtractPlugin.loader, 
          {
            loader: "css-loader",
            options: {
              // postcss-loader と sass-loader の場合は2を指定
              importLoaders: 2, 
              // 0 => no loaders (default);
              // 1 => postcss-loader;
              // 2 => postcss-loader, sass-loader
            },
          },
          // PostCSS の設定
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  [
                    "postcss-preset-env",
                    {
                      //必要に応じてオプションを指定
                      //stage: 0,
                      //browsers: 'last 2 versions',
                      //autoprefixer のオプション
                      //autoprefixer: { grid: true }
                    },
                  ],
                ],
              },
            },
          },
          //Sass ローダー
          'sass-loader',
        ], 
      },
      //Asset Modules の設定
      {
        //対象とするアセットファイルの拡張子を正規表現で指定
        test: /\.(png|svg|jpe?g|gif)$/i,
        //画像をコピーして出力
        type: 'asset/resource'  
      },
    ],
  },
  //プラグインの設定
  plugins: [
    new MiniCssExtractPlugin({
      // 抽出する CSS のファイル名
      filename: 'style.css',
    }),
  ],
  //production モード以外の場合は source-map タイプのソースマップを出力
  devtool: devMode ? 'source-map' : 'eval',
  // node_modules を監視(watch)対象から除外
  watchOptions: {
    ignored: /node_modules/  //正規表現で指定
  },
};

オプション

以下は PostCSS Preset Env のオプションの一部です。

オプション 説明
stage ポリフィルする CSS 機能を決定するオプションで 0(experimental)〜4(stable) の値、または false を指定できます(デフォルトは Stage 2 features)。0 や 1 を指定すると仕様策定中の次世代 CSS 構文などもトランスパイルすることができます。false を指定するとすべてのポリフィルが無効になります。The Staging Process/cssdb.org
features features オプションを使用して、設定されている stage オプションに関係なく、特定の CSS 機能のみを有効にすることができます。features は特定のポリフィルを有効または無効にします。特定の機能の ID(feature ID)に true を渡すとそのポリフィルが有効になり、false を渡すと無効になります。feature ID のリスト
browsers サポートするブラウザを指定できます。例: browsers: 'last 2 versions, > 0.5%'(各ブラウザの最後の2つのバージョンまたはシェア率0.5%以上のブラウザ)。標準の browserslist もサポートされています。
autoprefixer autoprefixer オプションを指定すると、追加のオプションautoprefixer に渡すことができます。autoprefixer: false を指定すると autoprefixer を無効にできます。

browserslist

以下は browsers オプションに指定する browserslist の記述例です。複数の条件を指定する場合はカンマで区切るか or を使います。

記述例 意味
> 0.5% 0.5%以上のシェアがあるブラウザ
> 1% in JP 日本で 1%以上のシェアがあるブラウザ
last 2 versions 最後の2バージョンのブラウザ
last 2 major versions 最新の2メジャーバージョンのブラウザ
last 2 iOS major versions iOS の最新の2メジャーバージョン
Firefox ESR 最新の Firefox ESR(Extended Service Release)版
dead 公式サポートが終了または24か月間の更新のないブラウザ
not dead dead ではないブラウザ(dead を除外したブラウザ)
not IE 11 IE 11 を除外
ie >= 9 IE 9以上
ie 6-8 IE 6~8
defaults > 0.5%, last 2 versions, Firefox ESR, not dead(デフォルト)

以下はブラウザの最後(最新)の2つのバージョンのみを対象とし、stage 3以降のすべての機能を使用し、stage 1の機能である nesting-rules を含めます。また、autoprefixer のオプションとして Grid Layout のベンダープレフィックスを有効にしています。

webpack.config.js 抜粋
{
  loader: "postcss-loader",
  options: {
    postcssOptions: {
      plugins: [
        [
          "postcss-preset-env",
          {
            /* オプション */
            browsers: "last 2 versions",
            stage: 3,
            features: {
              "nesting-rules": true
            },
            autoprefixer: { grid: true },
          },
        ],
      ],
    },
  },
},

例えば、以下のような CSS を style.scss に記述して PostCSS Preset Env のオプションを指定せず(デフォルトで)ビルドすると、

※以下の場合、背景画像を指定しているので、指定した画像が存在しない場合はコンパイル時に css-loader によりエラーになります。

$color: green;
$main_color: yellow;

::placeholder {
  color: $color;
}

@media (min-resolution: 2dppx) {
  .image {
    background-image: url(./images/sample.jpg);
  }
}

:root {
  --mainColor: #{$main_color};  /*カスタムプロパティと Sass のインターポレーション*/
}

body {
  color: var(--mainColor);
  font-family: system-ui;
  overflow-wrap: break-word;
}

PostCSS Preset Env により以下のようにベンダープレフィックスが付与され、カスタムプロパティなどが変換されます。この例の場合、Sass(sass-loader)も使っているので Sass もビルド時に CSS に変換されています。

::-moz-placeholder {
  color: green;
}

:-ms-input-placeholder {
  color: green;
}

::placeholder {
  color: green;
}

@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) {
  .image {
    background-image: url(images/sample.jpg);
  }
}
:root {
  --mainColor: yellow;
}

body {
  color: yellow;
  color: var(--mainColor);
  font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;
  word-wrap: break-word;
}

例えば、以下のように custom-media-queries や custom-selector を使った CSS の場合、

@custom-media --viewport-medium (width <= 50rem);
@custom-selector :--heading h1, h2, h3, h4, h5, h6;

:--heading {
  background-image: image-set(url(./images/sample.jpg) 1x, url(./images/sample.jpg) 2x);

  @media (--viewport-medium) {
    margin-block: 0;
  }
}

デフォルトの stage では変換されないので、stage オプションに 1 や 0 を指定するか、

webpack.config.js 抜粋
{
  loader: "postcss-loader",
  options: {
    postcssOptions: {
      plugins: [
        [
          "postcss-preset-env",
          {
            //stage オプションを指定
            stage: 1,
          },
        ],
      ],
    },
  },
},

または、stage はデフォルトのままで、features オプションを指定して、

webpack.config.js 抜粋
{
  loader: "postcss-loader",
  options: {
    postcssOptions: {
      plugins: [
        [
          "postcss-preset-env",
          {
            //features オプションを指定
            features: {
              "custom-media-queries": true,
              "custom-selectors": true
            },
          },
        ],
      ],
    },
  },
},

ビルドすると以下のように変換されます。

h1,h2,h3,h4,h5,h6 {
  background-image: url(images/sample.jpg);
  background-image: -webkit-image-set(url(images/sample.jpg) 1x, url(images/sample.jpg) 2x);
  background-image: image-set(url(images/sample.jpg) 1x, url(images/sample.jpg) 2x);
}

@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
  h1,h2,h3,h4,h5,h6 {
    background-image: url(images/sample.jpg);
  }
}

@media (max-width: 50rem) {
  h1,h2,h3,h4,h5,h6 {
    margin-top: 0;
    margin-bottom: 0;
  }
}

CSS Declaration Sorter

CSS Declaration Sorter を使うと CSS プロパティをソートすることができます。

CSS Declaration Sorter を利用するには css-declaration-sorter をインストールします。

$ npm install -D css-declaration-sorter  return

added 2 packages, and audited 226 packages in 943ms
package.json
{
  "name": "myproject",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "NODE_ENV=production webpack --mode production",
    "dev": "webpack --mode development",
    "watch": "webpack --mode development --watch",
    "sass-ver": "sass --version"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-declaration-sorter": "^6.1.3",
    "css-loader": "^6.5.1",
    "mini-css-extract-plugin": "^2.4.5",
    "postcss": "^8.4.5",
    "postcss-loader": "^6.2.1",
    "postcss-preset-env": "^7.2.0",
    "sass": "^1.45.2",
    "sass-loader": "^12.4.0",
    "webpack": "^5.65.0",
    "webpack-cli": "^4.9.1"
  }
}

webpack.config.js の postcss-loader に設定を追加します。order に指定できる値は以下になります。

  • alphabetical:アルファベット順(デフォルト)
  • smacss:最も重要なフローに影響を与えるプロパティから最も重要でないプロパティの順
    1. Box
    2. Border
    3. Background
    4. Text
    5. Other
  • concentric-css:ボックスモデルの外側に適用されるプロパティから内側の順
    1. Positioning
    2. Visibility
    3. Box model
    4. Dimensions
    5. Text

以下は order にデフォルトの alphabetical を指定しているので15〜17行目のオプションの部分は省略しても同じです。

webpack.config.js 抜粋
{
  loader: "postcss-loader",
  options: {
    postcssOptions: {
      plugins: [
        [
          "postcss-preset-env",
          {
            //postcss-preset-env と autoprefixer のオプション
          },
        ],
        //css-declaration-sorter を追加
        [
          'css-declaration-sorter', 
          { 
            order: 'alphabetical' //アルファベット順
          }
        ],
      ],
    },
  },
},
webpack.config.js
//path モジュールの読み込み
const path = require('path');
//MiniCssExtractPlugin の読み込み
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
//変数 devMode は production モードの場合は false でその他の場合は true
const devMode = process.env.NODE_ENV !== 'production';

module.exports = {
 
  //エントリポイント(デフォルトと同じなので省略可)
  entry: './src/index.js',  
  //出力先(デフォルトと同じなので省略可)
  output: { 
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
    //ファイルを出力する前にディレクトリをクリーンアップ
    clean: true, 
    //Asset Modules の出力先の指定
    assetModuleFilename: 'images/[name][ext][query]'
  },
  module: {
    rules: [
      //SASS 及び CSS 用のローダー
      {
        //拡張子 .scss、.sass、css を対象
        test: /\.(scss|sass|css)$/i, 
        // 使用するローダーの指定
        use: [ 
          //CSS を別ファイルとして出力するプラグインのローダー
          MiniCssExtractPlugin.loader, 
          {
            loader: "css-loader",
            options: {
              // postcss-loader と sass-loader の場合は2を指定
              importLoaders: 2, 
              // 0 => no loaders (default);
              // 1 => postcss-loader;
              // 2 => postcss-loader, sass-loader
            },
          },
          // PostCSS の設定
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  [
                    "postcss-preset-env",
                    {
                      //postcss-preset-env と autoprefixer のオプション
                    },
                  ],
                  [
                    'css-declaration-sorter', 
                  ],
                ],
              },
            },
          },
          //Sass ローダー
          'sass-loader',
        ], 
      },
      //Asset Modules の設定
      {
        //対象とするアセットファイルの拡張子を正規表現で指定
        test: /\.(png|svg|jpe?g|gif)$/i,
        //画像をコピーして出力
        type: 'asset/resource'  
      },
    ],
  },
  //プラグインの設定
  plugins: [
    new MiniCssExtractPlugin({
      // 抽出する CSS のファイル名
      filename: 'style.css',
    }),
  ],
  //production モード以外の場合は source-map タイプのソースマップを出力
  devtool: devMode ? 'source-map' : 'eval',
  // node_modules を監視(watch)対象から除外
  watchOptions: {
    ignored: /node_modules/  //正規表現で指定
  },
};

例えば、以下のような CSS を style.scss に記述してコンパイルすると、

.foo {
    display: block;
    z-index: 1;
    animation: none;
    color: #C55;
    border: 0;
}

以下のようにアルファベット順にソートされます。また、この例ではベンダープレフィックスも付与されています。

.foo {
  -webkit-animation: none;
          animation: none;
  border: 0;
  color: #C55;
  display: block;
  z-index: 1;
}

PostCSS Sort Media Queries

PostCSS Sort Media Queries を使うと分散されたメディアクエリをまとめることができます。

PostCSS Sort Media Queries を利用するには postcss-sort-media-queries をインストールします。

$ npm install --save-dev postcss-sort-media-queries  return
          
added 2 packages, and audited 228 packages in 2s
package.json
{
  "name": "myproject",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "NODE_ENV=production webpack --mode production",
    "dev": "webpack --mode development",
    "watch": "webpack --mode development --watch",
    "sass-ver": "sass --version"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-declaration-sorter": "^6.1.3",
    "css-loader": "^6.5.1",
    "mini-css-extract-plugin": "^2.4.5",
    "postcss": "^8.4.5",
    "postcss-loader": "^6.2.1",
    "postcss-preset-env": "^7.2.0",
    "postcss-sort-media-queries": "^4.2.1",
    "sass": "^1.45.2",
    "sass-loader": "^12.4.0",
    "webpack": "^5.65.0",
    "webpack-cli": "^4.9.1"
  }
}

webpack.config.js の postcss-loader に設定を追加します。sort に指定できる値は以下になります。

  • mobile-first:モバイルファーストでまとめる(デフォルト)
  • desktop-first:デスクトップファーストでまとめる
  • 独自の関数を指定

以下は sort にデフォルトの mobile-first を指定しているので21〜23行目のオプションの部分は省略しても同じです。

webpack.config.js 抜粋
{
  loader: "postcss-loader",
  options: {
    postcssOptions: {
      plugins: [
        [
          "postcss-preset-env",
          {
            //postcss-preset-env と autoprefixer のオプション
          },
        ],
        [
          'css-declaration-sorter', 
          { 
            order: 'alphabetical' 
          }
        ],
        [
          //PostCSS Sort Media Queries(mobile-first でソート)
          'postcss-sort-media-queries', 
          { 
           sort: 'mobile-first' 
          }
        ],
      ],
    },
  },
},
webpack.config.js
//path モジュールの読み込み
const path = require('path');
//MiniCssExtractPlugin の読み込み
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
//変数 devMode は production モードの場合は false でその他の場合は true
const devMode = process.env.NODE_ENV !== 'production';

module.exports = {
 
  //エントリポイント(デフォルトと同じなので省略可)
  entry: './src/index.js',  
  //出力先(デフォルトと同じなので省略可)
  output: { 
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
    //ファイルを出力する前にディレクトリをクリーンアップ
    clean: true, 
    //Asset Modules の出力先の指定
    assetModuleFilename: 'images/[name][ext][query]'
  },
  module: {
    rules: [
      //SASS 及び CSS 用のローダー
      {
        //拡張子 .scss、.sass、css を対象
        test: /\.(scss|sass|css)$/i, 
        // 使用するローダーの指定
        use: [ 
          //CSS を別ファイルとして出力するプラグインのローダー
          MiniCssExtractPlugin.loader, 
          {
            loader: "css-loader",
            options: {
              // postcss-loader と sass-loader の場合は2を指定
              importLoaders: 2, 
              // 0 => no loaders (default);
              // 1 => postcss-loader;
              // 2 => postcss-loader, sass-loader
            },
          },
          // PostCSS の設定
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  [
                    "postcss-preset-env",
                    {
                      //postcss-preset-env と autoprefixer のオプション
                    },
                  ],
                  [ 'css-declaration-sorter', { order: 'alphabetical' } ],
                  [ 'postcss-sort-media-queries', { sort: 'mobile-first' } ],
                ],
              },
            },
          },
          //Sass ローダー
          'sass-loader',
        ], 
      },
      //Asset Modules の設定
      {
        //対象とするアセットファイルの拡張子を正規表現で指定
        test: /\.(png|svg|jpe?g|gif)$/i,
        //画像をコピーして出力
        type: 'asset/resource'  
      },
    ],
  },
  //プラグインの設定
  plugins: [
    new MiniCssExtractPlugin({
      // 抽出する CSS のファイル名
      filename: 'style.css',
    }),
  ],
  //production モード以外の場合は source-map タイプのソースマップを出力
  devtool: devMode ? 'source-map' : 'eval',
  // node_modules を監視(watch)対象から除外
  watchOptions: {
    ignored: /node_modules/  //正規表現で指定
  },
};

例えば、以下のような CSS を style.scss に記述してコンパイルすると、

@media screen and (max-width: 640px) {
  .head { 
    color: #cfcfcf 
  }
}
@media screen and (max-width: 768px) {
  .footer { 
    color: #cfcfcf 
  }
}
@media screen and (max-width: 640px) {
  .main { 
    color: #cfcfcf 
  }
}
@media screen and (min-width: 1280px) {
  .mobile-first { 
    color: #cfcfcf 
  }
}
@media screen and (min-width: 640px) {
  .mobile-first { 
    color: #cfcfcf 
  }
}
@media screen and (max-width: 640px) {
  .footer { 
    color: #cfcfcf 
  }
}

mobile-first の場合、以下のようにまとめられます。

@media screen and (min-width: 640px) {
  .mobile-first {
    color: #cfcfcf;
  }
}
@media screen and (min-width: 1280px) {
  .mobile-first {
    color: #cfcfcf;
  }
}
@media screen and (max-width: 768px) {
  .footer {
    color: #cfcfcf;
  }
}
@media screen and (max-width: 640px) {
  .head {
    color: #cfcfcf;
  }
  .main {
    color: #cfcfcf;
  }
  .footer {
    color: #cfcfcf;
  }
}

webpack-dev-server

watch オプション(--watch)を利用すればファイルが変更されると自動的に webpack コマンドを実行するようにはできますがブラウザには反映されません。webpack-dev-server を使えばファイルの変更が自動的にブラウザに反映されるようにできます。

以下は webpack-dev-server を使って開発用サーバを立ち上げる例です。

webpack-dev-server をインストールします。

npm install -D webpack-dev-server  return 
          
added 196 packages, and audited 424 packages in 7s

npm scripts を追加

コマンドラインで簡単にサーバを起動できるように package.json の scripts フィールドに npm scripts を設定します(以下7行目)。

package.json 抜粋
"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
  "build": "NODE_ENV=production webpack --mode production",
  "dev": "webpack --mode development",
  "watch": "webpack --mode development --watch",
  "sass-ver": "sass --version",
  "start": "webpack serve --mode development"
},
package.json
{
  "name": "myproject",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "NODE_ENV=production webpack --mode production",
    "dev": "webpack --mode development",
    "watch": "webpack --mode development --watch",
    "sass-ver": "sass --version",
    "start": "webpack serve --mode development"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-declaration-sorter": "^6.1.3",
    "css-loader": "^6.5.1",
    "mini-css-extract-plugin": "^2.4.5",
    "postcss": "^8.4.5",
    "postcss-loader": "^6.2.1",
    "postcss-preset-env": "^7.2.0",
    "postcss-sort-media-queries": "^4.2.1",
    "sass": "^1.45.2",
    "sass-loader": "^12.4.0",
    "webpack": "^5.65.0",
    "webpack-cli": "^4.9.1",
    "webpack-dev-server": "^4.7.2"
  }
}

これでコマンドラインで npm start または npm run start で開発サーバを起動することができます。

オプションの設定

webpack.config.js に以下の webpack-dev-server のオプションを追加します。

この例の場合、公開するリソース(html ファイル)のドキュメントルートは webpack.config.js と同じ位置なので static オプションは './' としています(デフォルトは ./public)。

また、サーバー起動時にブラウザを自動的に起動するように open: true を指定しています。

webpack.config.js 抜粋
devServer: {  
  //表示する静的ファイルのディレクトリを指定
  static: './',  //または static: path.join(__dirname, ''),
  //サーバー起動時にブラウザを自動的に起動
  open: true,
},
フォルダ構成
myProject
├── dist  //ビルド先ディレクトリ
├── index.html  //HTML ファイル(表示する静的ファイル)
├── node_modules
├── package-lock.json
├── package.json
└── src  
       ├── index.js
       └── style.scss  

バンドルされたファイルを出力する

デフォルトでは webpack-dev-server で生成された(バンドルされた)ファイルはメモリ上に保存されて実際には出力されないので、ファイルを出力する(実際に書き出す)には devMiddleware オプションに writeToDisk: true を指定します。

webpack.config.js 抜粋
devServer: {  
  static: './',
  open: true,
  //webpack-dev-middleware 関連の設定
  devMiddleware: {
    writeToDisk: true, //バンドルされたファイルを出力する(実際に書き出す)
  },
},
webpack.config.js
//path モジュールの読み込み
const path = require('path');
//MiniCssExtractPlugin の読み込み
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
//変数 devMode は production モードの場合は false でその他の場合は true
const devMode = process.env.NODE_ENV !== 'production';

module.exports = {
 
  //エントリポイント(デフォルトと同じなので省略可)
  entry: './src/index.js',  
  //出力先(デフォルトと同じなので省略可)
  output: { 
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
    //ファイルを出力する前にディレクトリをクリーンアップ
    clean: true, 
    //Asset Modules の出力先の指定
    assetModuleFilename: 'images/[name][ext][query]'
  },
  //webpack-dev-server の設定
  devServer: {  
    //表示する静的ファイルのディレクトリを指定
    static: './',
    // または static: path.join(__dirname, ''),
    //サーバー起動時にブラウザを自動的に起動
    open: true,
    //webpack-dev-middleware 関連の設定
    devMiddleware: {
      writeToDisk: true, //バンドルされたファイルを出力する(実際に書き出す)
    },
  },
  module: {
    rules: [
      //SASS 及び CSS 用のローダー
      {
        //拡張子 .scss、.sass、css を対象
        test: /\.(scss|sass|css)$/i, 
        // 使用するローダーの指定
        use: [ 
          //CSS を別ファイルとして出力するプラグインのローダー
          MiniCssExtractPlugin.loader, 
          {
            loader: "css-loader",
            options: {
              // postcss-loader と sass-loader の場合は2を指定
              importLoaders: 2, 
              // 0 => no loaders (default);
              // 1 => postcss-loader;
              // 2 => postcss-loader, sass-loader
            },
          },
          // PostCSS の設定
          {
            loader: "postcss-loader",
            options: {
              postcssOptions: {
                plugins: [
                  [
                    "postcss-preset-env",
                    {
                      //postcss-preset-env と autoprefixer のオプション
                    },
                  ],
                  [ 'css-declaration-sorter', { order: 'alphabetical' } ],
                  [ 'postcss-sort-media-queries'],
                ],
              },
            },
          },
          //Sass ローダー
          'sass-loader',
        ], 
      },
      //Asset Modules の設定
      {
        //対象とするアセットファイルの拡張子を正規表現で指定
        test: /\.(png|svg|jpe?g|gif)$/i,
        //画像をコピーして出力
        type: 'asset/resource'  
      },
    ],
  },
  //プラグインの設定
  plugins: [
    new MiniCssExtractPlugin({
      // 抽出する CSS のファイル名
      filename: 'style.css',
    }),
  ],
  //production モード以外の場合は source-map タイプのソースマップを出力
  devtool: devMode ? 'source-map' : 'eval',
  // node_modules を監視(watch)対象から除外
  watchOptions: {
    ignored: /node_modules/  //正規表現で指定
  },
};

main.js の読み込み

今までの例では Javascript をバンドルしていないので html ファイル(index.html) では main.js の読み込みをコメントアウトしていましたが、コメントアウトを外して読み込むようにします。

index.html の例
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="dist/style.css">
<title>Sass 読み込みサンプル</title>
</head>
 
<body>
<div class="sass_sample">
  <h1>Sass</h1>
  <p>sample text.</p>
</div>
<script src="dist/main.js"></script><!-- main.js を読み込む -->
</body>
</html>

webpack-dev-server を起動すると、main.js に以下のような内容が書き込まれて出力されます(main.js を読み込んでいないとファイルに変更があった場合にライブリロードされません)。

開発サーバの起動

package.json の scripts フィールドに npm scripts を指定してあるので、コマンドラインで npm start または npm run start を実行すると開発サーバが起動します。

または、以下のように npx コマンドを実行しても同じです(--mode development は開発モード用のオプションです)。

$ npx webpack serve --mode development  return 

この例では port オプションでポート番号を指定していないので、デフォルトの http://localhost:8080/ でページが開きます。

index.html や style.scss を変更するとブラウザがリロードされ変更が自動的に反映されます。

サーバの終了

開発サーバを終了するには control + c を押します。

詳細は webpack の基本的な使い方 webpack-dev-server を御覧ください。