【Node.js】npx webpack で何が起こっているか調べてみた
はじめに
こんにちは、SHIFT のアジャイルサービス部に所属している Matsuura です。
現在はサービス改革部のテックブースト(以下,TB)プロジェクトに参画し開発の基礎について学んでいます。
今回は、package.jsonファイルのscriptsセクションで定義したyarn devコマンドでnpx webpackを実行したときの挙動についてまとめました。
コマンドの実行
yarn では、yarn run <scripts>コマンド、または run を省略しyarn <scripts>コマンドを使用することで、簡単に実行することができます。
実行すると scripts で定義されたコマンドが実行されます。
ディレクトリ構造と各種ファイルの解説
ディレクトリの構成は以下の通りです。
├ src/
| ├ lib/
| | ├ moduleA.js
| | └ moduleB.js
| └ index.js
├ package.json
└ webpack.config.js
package.json とは
package.json ファイルは、Node.js プロジェクトの中心的な設定ファイルであり、プロジェクトの依存関係やバージョン管理を簡単に行うためのツールです。 構成は以下のようになっています。
scripts セクション
scripts セクションではプロジェクトで使用するコマンドとその名前を定義することができます。 これにより、コマンドラインでの操作が簡略化され、プロジェクトの一貫性が保たれます。
webpack.config.js とは
Webpack とは Javascript 向けのモジュールバンドラーです。複数の JavaScript ファイルやその他のアセット(CSS、画像、フォントなど)を 1 つまたは複数のファイルにまとめて(バンドル)出力します。この出力されたファイルをバンドルファイルといいます。これにより、ブラウザからのリクエスト数を減らしファイル転送の効率を向上させることができます。
この webpack の設定ファイルをwebpack.config.jsといい、以下のような項目が記載されています。
npx webpack の実行
それでは実際の動きについてみていきます。
今回は webpack について扱い、package.json 内の以下のような設定としました。
{
"scripts": {
"dev": "npx webpack --mode development --watch"
}
}
入力
$ yarn dev
出力
dist ディレクトリと、その配下にファイルが作成されます。
└- dist
| ├ index.js
| └ index.js.map
├ src/
| ├ lib/
| | ├ moduleA.js
| | └ moduleB.js
| └ index.js
├ package.json
└ webpack.config.js
ここでプロジェクトのビルドプロセスがどのように進行したのかを見ていこうと思います。
npx webpack の挙動
npx によって webpack コマンドが実行され Webpack が起動
npx (node package executer)
Node.js のプロジェクト内のスクリプトやコマンドを実行するためのツールです。以下の順でパッケージを探し実行します。
1. ローカルにインストールされたパッケージ
yarn add < パッケージ名 >でインストールされた、プロジェクト内の node_modules ディレクトリ配下のパッケージを使用します。
2. グローバルにインストールされたパッケージ
yarn global add < パッケージ名 >でインストールされた、システムの任意の場所から参照できるディレクトリ配下のパッケージを使用します。yarn global list でインストール先とパケージの一覧を確認できます。
3. パッケージを一時的にインストール
ローカルにもグローバルにもパッケージが見つからない場合、npx はそのパッケージを一時的にインストールして実行します。実行後は削除するため開発環境に変更を加えることなく実行が可能です。
ルートディレクトリ内にある webpack.config.js ファイルの設定の読み込み
// webpack.config.jsの例
const path = require("path");
const nodeExternals = require("webpack-node-externals");
const ESLintPlugin = require("eslint-webpack-plugin");
module.exports = {
mode: process.env.NODE_ENV === "production" ? "production" : "development",
name: "server",
target: "node",
devtool: "source-map",
entry: {
index: "./src/index.js",
},
output: {
path: path.join(__dirname, "dist"),
filename: "[name].js",
},
module: {
rules: [
{
test: /\.(js|ts)$/,
exclude: /node_modules/,
use: { loader: "babel-loader" },
},
],
},
plugins: [new ESLintPlugin({ exclude: "node_modules" })],
};
3. 依存関係の解析
Webpack はエントリーポイントから始めて、すべての依存関係を再帰的に解析します。
例:./src/index.js から始めて、モジュールなどの依存関係を解析
各モジュールはローダーを通じて処理されます。ローダーは特定のファイルタイプを変換するためのツールです。
例:Babel では ES6 を ES5 に変換
4. バンドルの生成
Webpack はすべてのモジュールを 1 つまたは複数のバンドルファイルにまとめます。webpack では、複数のエントリーポイントをサポートしており、それぞれのエントリーポイントに対して個別のバンドルファイルを生成することができます。
5. プラグインの実行
webpack.config.js に指定されたプラグインが実行されます。
例:ESLint が Webpack のビルドプロセス中に実行され、コードの品質をチェックする
6. ビルドの完了
最終的に、指定された出力ディレクトリにバンドルファイルが生成されます.
mode
webpack をコンパイルする方法を切り替えることができるオプションで、development、production、noneの 3 種類があります。
コマンドライン上で--mode==<オプション名> で指定するか、webpack.config.js の mode を設定することで指定できます。コマンドラインから指定した場合はそちらが優先されます。
mode を指定すると DefinePlugin の process.env.NODE_ENV(環境変数)が設定されます。
DefinePlugin は、Webpack のプラグインの一つで、環境変数の設定やビルド時に異なる設定を適用するために使用されます。modeをすると webpack は内部的に(node-modules/webpack/lib/WebpackOptionsApply.js 内で) DefinePlugin を自動的に設定し、process.env.NODE_ENV がdevelopmentに設定されます。
また、webpack.config.js の plugins で以下のように明示的に指定することもできます。
module.exports = {
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('development')
})
],
( 省略 )
}
各オプションは以下の特徴を持ちます。`development` と `production` のデフォルトの設定は `webpack/lib/config/defaults.js`に記載されています。設定を変更したい場合は `webpack.config.js` で指定します。
development:開発モード
DefinePlugin の process.env.NODE_ENVを development に設定します。
ソースマップが生成されます。
ソースマップファイル(.mapファイル)とは、トランスパイルされたコードを元のソースコードの対応関係を記録したファイルで、以下の要素を持ちます。これにより、デバッグ時にトランスパイルされたコードではなく、元のソースコードを参照することができるようになります。
// index.js.map
{
"version":3,
"file":"index.js",
"mappings":";;;;;;;;;AAAA;AACA;AACA;;AAEA,CAAC;AACD,GAAG,( 省略 )",
"sources":["webpack://webpack-boilerplate/../../node_modules/moment/locale/af.js",( 省略 )]
}
ソースマッピングの生成方法はwebpack.config.jsのdevtool で設定することができ、開発モードではデフォルトで devtool: 'eval' と設定されています。この eval は特にビルド速度が速いオプションです。他の設定はこちら から確認できます。
キャッシュが有効化されています。これにより、変更がない部分の再ビルドを避けることができ、ビルド速度が向上します。webpack.config.jsのcacheで設定できます。詳細はこちら から確認できます。
production:本番モード
DefinePlugin で process.env.NODE_ENV を production に設定します。
devtoolが設定されておらず、ソースマップが生成されません。
コードが圧縮されます。ファイルサイズが小さくなり、ロード時間が短縮されます。
使用されていないコード(デッドコード)が削除され、パフォーマンスが向上します。
none
すべてのデフォルトの最適化オプション(コードの圧縮、モジュールの結合、ソースマップ設定、プラグインなど)を無効にします。
--watch
ファイルの変更を監視し、変更があるたびに自動的に再コンパイルを行うための機能です。
実行中に発生したエラー
以下のようなエラーが起こる場合があります。
Watchpack Error (watcher): Error: ENOSPC: System limit for number of file watchers reached, watch 'フォルダ名'
このエラーは、システムが監視できるファイルの数の上限に達したことを示しています。解決するためにはシステムのファイル監視リソースの上限を増やす必要があります。
解決策
現在のファイル監視リソースの上限を確認する
cat /proc/sys/fs/inotify/max_user_watches
2. ファイル監視リソースの上限を増やす
一時的に上限を増やす
sudo sysctl fs.inotify.max_user_watches=524288
永続的に上限を増やす
echo "fs.inotify.max_user_watches=524288" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
おわりに
この記事では npx webpack の実行プロセスを詳しく見てきました。package.json の設定から始まり、Webpack の動作、依存関係の解析、バンドルの生成までの流れを理解することで、プロジェクトのビルドプロセスがどのように進行するかを把握できました。
これからも開発の基礎をしっかりと学び、実践に活かしていきたいと思います。
参考リンク
お問合せはお気軽に
https://service.shiftinc.jp/contact/
SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/
SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/
SHIFTの導入事例
https://service.shiftinc.jp/case/
お役立ち資料はこちら
https://service.shiftinc.jp/resources/
SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/
PHOTO:UnsplashのZachary Fetters