いちばんやさしい webpack 入門
webpack is 何?
webpack とは、一言で言うと JavaScript 向けのモジュールバンドラーです。
複数の JavaScript モジュールを一つ(またはいくつか)のファイルへバンドル(=bundle: 束にする、包む
)してくれます。
複数の JS モジュールを(場合によっては CSS や画像などのアセット類も)一つにまとめる
使うメリットは何?
- モジュールを 1 つ(もしくは少数)にまとめることでブラウザからのリクエスト数を減らし、ファイル転送の効率が向上します。
- ES Modules や CommonJS 形式のモジュールなど、さまざまな形式のモジュールに対応しています。
- 上記の JS モジュールのみならず、CSS や画像ファイルもバンドルすることができます。
とりあえず webpack を使ってみる
1. プロジェクト・フォルダの作成
"zenn-webpack" という名前のフォルダを作成し、Node.js のプロジェクトとして初期化します。
mkdir zenn-webpack
cd zenn-webpack
# Node.js プロジェクトとして初期化
npm init --yes
2. webpack のインストール
プロジェクトに webpack をインストールします。
- 本体
npm install --save-dev webpack
- webpack を CLI から利用するために必要なツール
npm install --save-dev webpack-cli
3. ソースファイルの用意
バンドル前の hello.js
モジュールと、そのモジュールを利用する index.js
スクリプトを src
フォルダ内へ作成します。
export class Hello {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello, ${this.name}!`);
}
}
import { Hello } from "./hello";
new Hello("taro").greet();
ここまででプロジェクトフォルダの構成は以下のようになりました。
% tree -I 'node_modules'
.
├── package-lock.json
├── package.json
└── src
├── hello.js
└── index.js
1 directory, 4 files
4. バンドルの実行
webpack はデフォルトで、src/index.js
(=エントリーファイル)とそこから読み込まれているモジュールをバンドルして dist/main.js
というファイルを出力します。
webpack を起動してバンドルを実行します。
- バンドルの実行
./node_modules/.bin/webpack
- または npx コマンド(npm に同梱)を利用する
npx webpack
asset main.js 127 bytes [emitted] [minimized] (name: main)
orphan modules 138 bytes [orphan] 1 module
./src/index.js + 1 modules 218 bytes [built] [code generated]
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value.
Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/configuration/mode/
webpack 5.72.0 compiled with 1 warning in 102 ms
dist
フォルダが作成され、main.js
というバンドルファイルが出力されました。
.
+ ├── dist
+ │ └── main.js
├── package-lock.json
├── package.json
└── src
├── hello.js
└── index.js
5. ローカルサーバと Watch モードの利用
では、上で出力された dist/main.js
を読み込む HTML ファイルを作成し、それをローカルサーバでサーブしてみます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Webpack</title>
</head>
<body>
<script src="./main.js"></script>
</body>
</html>
.
├── dist
│ ├── index.html
│ └── main.js
├── package-lock.json
├── package.json
└── src
├── hello.js
└── index.js
ローカルサーバには、VS Code の Live Server 拡張機能を利用します。
この拡張機能をインストールしたら、VS Code でプロジェクトフォルダ内の dist/index.html
を開いた状態でステータスバーの 📡 "Go Live" をクリックします。
ブラウザで localhost:5500 が開かれるので、デベロッパーコンソール(※)を確認すると Hello, taro!
と出力されているはずです。
webpack にはファイルの変更を検知して自動的に再バンドルする watch モードも用意されています。
--watch
オプションを付加してもう一度バンドルを実行してみましょう。
npx webpack --watch
ソースファイルを変更してみます。
import { Hello } from "./hello";
- new Hello("taro").greet();
+ new Hello("Jiro").greet();
出力内容が Hello, Jiro!
に代わった
6. バンドルファイルの確認
上で出力された dist/main.js
の内容を見てみましょう(見やすいように整形済み)。
(() => {
'use strict';
new (class {
constructor(e) {
this.name = e;
}
greet() {
console.log(`Hello, ${this.name}`);
}
})('Jiro').greet();
})();
ソースファイルがそうなっているのだから当たり前ですが、このバンドルファイルでは class
などの ES2015 以降の記法が使用されています。
つまり、このスクリプトは Internet Explorer やその他の古いブラウザでは実行できません。
このような古い環境で動作させるには、ポリフィル/トランスパイラによる ES 記法への変換が必要となります。
webpack へ Babel を追加
Babel は JavaScript 用のトランスパイラ(ポリフィル)です。
ES6 (ES2015+) を ES5 へ変換するだけではなく、JSX などの非標準の JavaScript 構文を ES* へ変換することもできます。
Babel の導入
このプロジェクトへ Babel を追加インストールして、webpack でのバンドル時にトランスパイルも併せて行えるようにします。
- Babel 本体
npm install --save-dev @babel/core
- ES6 -> ES5 の変換に必要なプリセット
npm install --save-dev @babel/preset-env
ローダーの導入
webpack では、JS の形式を変換したり、CSS(スタイルシート)やアセット画像などを JS へバンドルしたりするときにはローダーと呼ばれるプログラムを利用します。
変換の形式(jsx -> js
, ts -> js
など)やバンドルしたいファイル形式に応じて適切なローダーを webpack に利用させる必要があります。
ここでは Babel に「ES6 から ES5 へ」の変換をさせるので babel-loader をインストールします。
npm install --save-dev babel-loader
.babelrc
の作成
設定ファイル Babel の設定はプロジェクトフォルダ直下の .babelrc
ファイルへ記述します。
ここでは上でインストールしたプリセットを指定するだけです。
{
"presets": ["@babel/preset-env"]
}
webpack.config.js
を作成する
ここまではとくに何かを設定することもなく webpack を直接実行してきましたが、Babel を併せて利用するための設定ファイルを作成する必要が出てきました。
webpack の設定ファイルを JavaScript で記述する場合には CommonJS 形式を用います(その理由は本稿では省略します)。
プロジェクトフォルダ直下へ webpack.config.js
を作成します。
/** ↓ エディタで補完を効かせるための JSDoc */
/** @type {import('webpack').Configuration} */
module.exports = {
module: {
rules: [
{
// 拡張子 js のファイル(正規表現)
test: /\.js$/,
// ローダーの指定
loader: "babel-loader",
},
],
},
};
変換やバンドルのルールは module.rules
配列に指定します。
多くの場合、test
でファイル形式を指定し、loader
(または use
配列)へローダーを指定することになります。
ふたたび webpack を起動してバンドルしてみましょう。
npx webpack
出力された dist/main.js
(整形済み)を確認すると ES5 形式へ変換されていることが分かります。
(() => {
"use strict";
function e(e, n) {
for (var t = 0; t < n.length; t++) {
var r = n[t];
(r.enumerable = r.enumerable || !1),
(r.configurable = !0),
"value" in r && (r.writable = !0),
Object.defineProperty(e, r.key, r);
}
}
モードとソースマップ
モードの指定
ここまでバンドルしてきた中で、以下のような警告メッセージが表示されていました。
WARNING in configuration
Set 'mode' option to 'development' or 'production'
to enable defaults for each environment.
mode
オプションを 'development'
(開発モード)または 'production'
(本番モード)の環境に応じて設定する必要があるようです。
production
モードではスクリプト実行に必要のない部分(コメントや改行など)がすべて削除され、実行に適した形式へと最適化(ファイルサイズの縮減など)が行われます。
一方、development
モードではこれらの最適化が行われないのでデバッグに適しています。
モードをコマンドラインから指定する場合は次のようにします:
npx webpack --mode development
設定ファイル webpack.config.js
へ指定することもできます:
module.exports = {
+ mode: "development",
module: {
rules: [
ソースマップ
ここでは Babel による ES6 から ES5 への変換を行なっているため、変換前後のコード対応表のようなものがあればデバッグに役立ちます。それがソースマップです。
webpack にソースマップも出力させるには devtool
エントリを指定します。
mode: "development",
devtool: "source-map", // または 'inline-source-map' など
ソースマップが存在する場合には、ブラウザの開発者ツールの Sources タブから変換前のソースコードを見ることができます。
webpack-dev-server でホットリロードしよう
ここまでは VS Code の拡張機能 LiveServer を利用してきましたが、ローカルサーバも webpack から立ち上げることができます。webpack-dev-server をインストールします。
# npm install --save-dev と同義
npm i -D webpack-dev-server
デフォルトでは devServer はホストのルートディレクトリ /
を起点として起動するため、サーブすべきディレクトリを webpack.config.js
内で指定してあげる必要があります。
ここでは index.html
を置いているプロジェクト直下の ./dist
を指定します。
devtool: "source-map",
devServer: {
static: {
directory: "./dist",
},
},
devServer の起動には serve
を付け加えます。
npx webpack serve --mode development
localhost:8080 をブラウザで開くとバンドル結果が表示されているはずです。
また、serve
コマンドを用いて起動した場合には --watch
オプションと同様に自動的にファイルへの変更が反映(=ホットリロード)されます。
JSX (React) もバンドルしてみる
1. Babel プリセットを追加
上で Babel は「JSX などの非標準の JavaScript 構文を ES* へ変換することもできます」と書きましたが、そのためには JSX -> JS
変換用のプリセットを追加する必要があります。
npm i -D @babel/preset-react
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
2. React のインストール
React をインストールします。
# npm install --save と同義
npm i -S react react-dom
3. React アプリを準備
dist
フォルダ内のバンドルファイルを削除し、src
フォルダ内のファイルをシンプルな React アプリ (JSX) で置き換えます。
rm dist/*.js* src/*.js
import React from "react";
export const App = () => {
return (
<div className="container">
<h1>Hello.</h1>
</div>
);
};
import React from "react";
import { createRoot } from "react-dom/client";
import { App } from "./App";
createRoot(document.getElementById("root")).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
dist
フォルダ内の index.html
へ React アプリのマウントポイントを追加します。
<body>
+ <div id="root"></div>
<script src="./main.js"></script>
</body>
% tree -I 'node_modules'
.
├── dist
│ └── index.html
├── package-lock.json
├── package.json
├── src
│ ├── App.jsx
│ └── index.jsx
└── webpack.config.js
2 directories, 6 files
4. エントリーファイルと依存関係の解決
最初の方で「デフォルトで src/index.js
(=エントリーファイル)とそこから読み込まれているモジュールをバンドルして」と書きましたが、このデフォルトの状態はもう使えなくなりました。
すでに src/index.js
は存在せず、他のモジュールをインポートするエントリーファイルは src/index.jsx
となったからです。
従前のバンドルのためのコマンドを実行すると次のようなエラーとなります。
$ npx webpack serve --mode development
ERROR in main
Module not found: Error: Can't resolve './src' in '/Foo/zenn-webpack'
『src
でのモジュール解決(=resolve)が出来ません』とのメッセージ通り、モジュール間の依存関係の解決にどの種類のファイルを参照すべきなのかを明示的に webpack へ伝える必要が生じました。
これには resolve
エントリーを用います。ここへ .jsx
を加えることで webpack は src/index.jsx
をエントリーファイルとして認識できるようになります。
module.exports = {
// 依存関係解決に参照するファイルの拡張子を指定
resolve: {
extensions: [".js", ".json", ".jsx"],
},
また、変換のルールも変更となったため module.rules
を更新しなければなりません。
module: {
rules: [
{
// 拡張子 js または jsx のファイル(正規表現)
+ test: /\.jsx?$/,
- test: /\.js$/,
loader: "babel-loader",
},
],
},
再度 devServer を起動します。
npx webpack serve --mode development
localhost:8080 で React アプリが動作
エントリーファイル名や出力ファイル名をカスタマイズする
上の resolve
設定によって src/index.jsx
をエントリーファイルとすることが出来ましたが、index
以外のファイル名(たとえば main.jsx
など)を使いたい場合もあるでしょう。
そういう場合は entry
エントリーへそのファイル名を明示的に指定する必要があります。
module.exports = {
resolve: {
extensions: [".js", ".json", ".jsx"],
},
+ entry: "./src/main.jsx",
加えて、エントリーファイル同様に出力されるバンドルファイルの名前や出力先フォルダもカスタマイズすることができます。これには output
エントリを用います。
// Node.js の path モジュールをインポート
const path = require("node:path");
module.exports = {
entry: "./src/main.jsx",
output: {
// ファイル名
filename: "bundle.js",
// 出力するフォルダ
path: path.resolve(__dirname, "dist"),
},
ここでは node:path
モジュールを使って出力フォルダの絶対パスを指定しています。
entry
でエントリーファイルにチャンク名を付けた場合には出力ファイルにもその名前を適用することができます。
entry: {
// チャンク名 "app"
app: "./src/main.jsx",
},
output: {
// [name] -> "app"
filename: "[name].js",
path: path.resolve(__dirname, "dist"),
},
複数のエントリーファイルがある場合などに自動的に出力名を割り振ってくれるので便利です。
プロダクションビルドする
本番環境にデプロイするためのビルドは、モードに production
を指定し、serve
コマンドを build
へ置き換えるだけです。
npx webpack build --mode production
NPM スクリプトを登録する
毎回ターミナルへ長いコマンドを打ち込むのも疲れてきたので、package.json
の scripts
へ NPM スクリプトを登録します。
NPM スクリプトは npm run スクリプト名
で実行することができます。
"scripts": {
"dev": "webpack serve --mode development",
"build": "webpack build --mode production"
},
# 開発時
npm run dev
# プロダクションビルド
npm run build
NODE_ENV で処理を分岐する
ソースマップを作成するか否かなどの処理を環境変数 NODE_ENV
の値によって分岐できるようにします。
Windows では NPM スクリプト(コマンドライン)に環境変数を付加するには cross-env パッケージのインストールが必要です。
npm i -D cross-env
"scripts": {
"dev": "cross-env NODE_ENV=\"development\" webpack serve",
"build": "cross-env NODE_ENV=\"production\" webpack build"
},
※ macOS や Linux では cross-env
は不要
webpack.config.js
へ処理の分岐を書き加えます。
// development モードか否か?
const isDev = process.env.NODE_ENV === "development";
/** @type {import('webpack').Configuration} */
module.exports = {
// モードの切り替え
mode: isDev ? "development" : "production",
// dev モードではソースマップをつける
devtool: isDev ? "source-map" : undefined,
entry: "./src/index.jsx",
CSS(スタイルシート)もバンドルしてみる
CSS ファイルも JS へバンドルしてしまうことが可能です。
webpack で CSS をバンドルするには、style-loader
と css-loader
の 2 つのローダーが必要です。
-
style-loader
:<link />
タグへ CSS を展開します。 -
css-loader
: CSS を JS へバンドルします。
ローダーは css-loader
-> style-loader
の順で適用する必要があります。
1. ローダーの追加インストール
npm i -D style-loader css-loader
module.rules
の追加
2. CSS 用のバンドルルールを module.rules
配列に追加します。
複数のローダーを適用する場合には loader
の代わりに use
配列を用います。
module: {
rules: [
{
test: /\.jsx?$/,
loader: "babel-loader",
},
{
// 拡張子 css のファイル(正規表現)
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
use
配列のローダーは配列の最後尾から順に適用されます。よって css-loader
-> style-loader
の順となります。
3. ソースファイルの用意
適当な CSS を styles.css
へ記述して、それを App.jsx
の中でインポートします。
body {
margin: 0;
}
.container {
height: 100vh;
display: grid;
place-content: center;
place-items: center;
}
import React from "react";
+ import "./styles.css";
export const App = () => {
4. devServer の起動
npm run dev
スタイルシートが適用された
5. CSS にもソースマップを付ける
デバッグ時にはバンドル前の CSS ファイルへのソースマップも必要です。
options
エントリを追加することでソースマップのオプションを設定することができます。
{
test: /\.css$/,
use: [
"style-loader",
{
loader: "css-loader",
options: {
// dev モードではソースマップを付ける
sourceMap: isDev,
},
},
],
},
Sass もバンドルする
Sass ファイルをバンドルするには sass
と sass-loader
の追加が必要です。
-
sass:
sass(scss) -> css
の変換を行います。スタイルシート版 Babel のようなものです。 - sass-loader: webpack に Sass を扱わせます。
npm i -D sass sass-loader
ローダーの適用順は sass-loader
-> css-loader
-> style-loader
となります。
{
+ // 拡張子 scss または css のファイル
+ test: /\.s?css$/,
- test: /\.css$/,
use: [
"style-loader",
{
loader: "css-loader",
options: {
sourceMap: isDev,
},
},
+ {
+ loader: "sass-loader",
+ options: {
+ sourceMap: isDev,
+ },
+ },
],
},
ソースファイルの CSS を Sass(scss) へ変換し、前項同様に App.jsx
でインポートします。
body {
margin: 0;
.container {
height: 100vh;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
}
- import "./styles.css";
+ import "./styles.scss";
画像などのアセットファイルをバンドルする
webpack v5.x では別途ローダーを必要とせずに JS から読み込まれる画像やフォントなどのアセットファイルをバンドルできるようになりました。
アセットファイルのバンドルには module.rules
で type
エントリーを用います。
module: {
rules: [
{
// 画像やフォントファイル
test: /\.(ico|png|svg|ttf|otf|eot|woff?2?)$/,
type: "asset",
},
App.jsx (クリックで展開)
import React from "react";
import reactLogo from "./logo.svg";
import "./styles.scss";
export const App = () => {
return (
<div className="container">
<img src={reactLogo} alt="logo" />
<h1>Hello.</h1>
</div>
);
};
React の react.svg をバンドル
type の値にはいくつかの選択肢がありますが、通常は以下の 3 つから選択することになります。
-
asset/inline
: アセットを JS ファイルへバンドルします。 -
asset/resource
: アセットを別ファイルとして出力します。 -
asset
: アセットをバンドルするか、別のファイルとして出力するかを自動的に選択します。
アセットを JS ファイルへ含めてしまうとバンドルサイズが大きくなりすぎる(=読み込みに時間がかかる)場合のために別ファイルとして出力するオプションが用意されています。
このオプションを選択するケースには、ブラウザのリクエスト数を増やしてでもバンドルファイルの読み込みを早めるほうがメリットが大きい場合などが該当するでしょう。
アセット類の出力先を設定する
asset/resource
を選択した場合のアセット類の出力先も output
エントリでカスタマイズすることができます。これには assetModuleFilename
を利用します。
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist"),
// "dist/asset/名前.拡張子" として出力される
assetModuleFilename: "asset/[name][ext]",
},
上の filename
エントリとは異なり、ドット .
は不要であることに注意が必要です。
アセットのファイルサイズによってバンドルの可否を分ける
アセットファイルの容量によって自動的に asset/inline
と asset/resource
をアセットごとに使い分けることもできます。これには parser.dataUrlCondition.maxSize
オプションを使います。
rules: [
{
test: /\.(ico|png|svg|ttf|otf|eot|woff?2?)$/,
// type は自動モード
type: "asset",
parser: {
dataUrlCondition: {
// 8kb 以上なら `asset/resource` する
maxSize: 1024 * 8,
},
},
},
HTML も webpack から出力する
これまでは dist
ディレクトリに置いた HTML ファイルを devServer でサーブしていましたが、これも webpack から出力することができます。
html-webpack-plugin をインストールしましょう。
npm i -D html-webpack-plugin
html-webpack-plugin はデフォルトで src/index.ejs
をテンプレートとし、それにバンドル済みの JS を <script> ~ </script>
タグとして差し込んだ HTML ファイルを出力します。
これ以外のファイル(たとえば *.html
ファイル)をテンプレートにしたい場合には template
オプションへ明示的にそのファイルを指定する必要があります。
// プラグインの読み込み
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
// "plugins" エントリーを追加
plugins: [
// プラグインのインスタンスを作成
new HtmlWebpackPlugin({
// テンプレート
template: "./src/index.html",
// <script> ~ </script> タグの挿入位置
inject: "body",
// スクリプト読み込みのタイプ
scriptLoading: "defer",
// ファビコンも <link rel="shortcut icon" ~ /> として挿入できる
favicon: "./src/favicon.ico",
}),
],
index.html
を dist
から src
フォルダへ移動させますが、<script> ~ </script>
タグや <link rel="shortcut icon" ~ />
タグはプラグインが挿入してくれるため削除します。
<body>
<div id="root"></div>
- <script src="./main.js"></script>
</body>
</html>
これにより前回のビルド結果は dist
フォルダごと削除できるようになりました。
CSS も別ファイルとして出力する
CSP (Content-Security-Policy) の設定によってはインラインスタイルの使用が禁じられているような場合があります。
CSS を JS へバンドルすることはインラインスタイルに該当するため、これもアセット類と同様に別ファイルとして出力したいケースがあるでしょう。
これを実現するプラグインが mini-css-extract-plugin です。
npm i -D mini-css-extract-plugin
webpack.config.js
で mini-css-extract-plugin を読み込み、style-loader
に代わってこのプラグインのローダーを使用します。
// プラグインの読み込み
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
{
test: /\.s?css$/,
use: [
- "style-loader",
+ MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
sourceMap: isDev,
},
},
plugins
配列にこのプラグインのインスタンスを作成しておくことを忘れないでください。
},
plugins: [
+ new MiniCssExtractPlugin(),
new HtmlWebpackPlugin({
template: "./src/index.html",
inject: "body",
scriptLoading: "defer",
}),
],
devtool: isDev ? "source-map" : undefined,
devServer: {
static: {
directory: "./dist",
},
},
};
dist
フォルダへ CSS ファイルも出力されるようになりました。
上の html-webpack-plugin
を利用しない場合、HTML ファイルへ手動で <link rel="stylesheet" ~ />
としてインポートする必要があります。
<head>
<meta charset="UTF-8" />
<title>Webpack</title>
+ <link rel="stylesheet" href="main.css" />
</head>
逆に html-webpack-plugin
を利用する場合には、自動的に <link rel="stylesheet" ~ />
タグが HTML へ挿入されます。
webpack.config.js
ここまでの webpack.config.js(クリックで開閉)
const path = require("node:path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const isDev = process.env.NODE_ENV === "development";
/** @type {import('webpack').Configuration} */
module.exports = {
mode: isDev ? "development" : "production",
resolve: {
extensions: [".js", ".json", ".jsx"],
},
entry: {
main: "./src/index.jsx",
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist"),
assetModuleFilename: "asset/[name][ext]",
},
module: {
rules: [
{
test: /\.jsx?$/,
loader: "babel-loader",
},
{
test: /\.s?css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
sourceMap: isDev,
},
},
{
loader: "sass-loader",
options: {
sourceMap: isDev,
},
},
],
},
{
test: /\.(ico|png|svg|ttf|otf|eot|woff?2?)$/,
type: "asset/resource",
},
],
},
plugins: [
new MiniCssExtractPlugin(),
new HtmlWebpackPlugin({
template: "./src/index.html",
inject: "body",
scriptLoading: "defer",
}),
],
devtool: isDev ? "source-map" : undefined,
devServer: {
static: {
directory: "./dist",
},
},
};
TypeScript をバンドルする
1. 必要なパッケージのインストール
- TypeScript 本体
npm i -D typescript
- ローダー
npm i -D ts-loader
tsconfig.json
の作成
2. TypeScript の挙動を規定する tsconfig.json
を作成します。
また、React を利用する場合には "jsx": "react"
の追記も必要です。
npx tsc --init
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"jsx": "react"
}
}
webpack.config.js
のアップデート
3. TS のバンドルには babel-loader の代わりに ts-loader を利用します。
(変更部分のみを抜粋)
module.exports = {
entry: {
// "tsx" へ変更
main: "./src/index.tsx",
},
resolve: {
// TS ファイルを追加
extensions: [".js", ".jsx", ".ts", ".tsx"],
},
module: {
rules: [
{
// "tsx" へ変更
test: /\.tsx?$/,
// "babel-loader" -> "ts-loader"
loader: "ts-loader",
},
],
},
};
webpack.config
を TypeScript で記述する
webpack.config.js
の拡張子を ts
へ変更するには以下の 2 つのパッケージが必要です。
- ts-node: TypeScript のまま Node.js スクリプトを実行できるようにするモジュール
- @types/node: TypeScript 用 Node.js の型定義ファイル
npm i -D ts-node @types/node
また、tsconfig.json
の ts-node
セクションでは "module": "CommonJS"
を指定する必要があります。
{
"compilerOptions": {
"target": "ES2020",
"module": "ES2020",
"moduleResolution": "Node",
"esModuleInterop": true,
"strict": true,
"jsx": "react"
},
"ts-node": {
"compilerOptions": {
"module": "CommonJS"
}
}
}
webpack.config.ts の例
// require 文から import 文へ
import path from "node:path";
import HtmlWebpackPlugin from "html-webpack-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin";
/** 以下 2 行は補完を効かせるためのインポート */
import "webpack-dev-server";
import { Configuration } from "webpack";
const isDev = process.env.NODE_ENV === "development";
const config: Configuration = {
mode: isDev ? "development" : "production",
resolve: {
extensions: [".js", ".jsx", ".ts", ".tsx", ".json"],
},
entry: {
main: "./src/index.tsx",
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist"),
assetModuleFilename: "asset/[name][ext]",
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader",
},
{
test: /\.s?css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
sourceMap: isDev,
importLoaders: 1,
},
},
{
loader: "sass-loader",
options: {
sourceMap: isDev,
},
},
],
},
{
test: /\.(ico|png|svg|ttf|otf|eot|woff?2?)$/,
type: "asset/resource",
},
],
},
plugins: [
new MiniCssExtractPlugin(),
new HtmlWebpackPlugin({
template: "./src/index.html",
inject: "body",
scriptLoading: "defer",
}),
],
devtool: isDev ? "inline-source-map" : undefined,
devServer: {
static: {
directory: "./dist",
},
},
};
// 設定をデフォルトエクスポート
export default config;
公式ドキュメント
Discussion
分かりやすい記事をありがとうございます。typo見つけたので報告です。
ご指摘ありがとうございます。修正いたしました。