インデックス
要件
以下のようなディレクトリ構成があり、それぞれのプロジェクト毎にJavaScriptをコンパイルして、それぞれのプロジェクトディレクトリにコンパイルしたファイルを格納したいといった案件がありました。Webpackでプロジェクトをコンパイルする時、今までは一つのディレクトリにのみ全てのプロジェクトを書き出していたので、ディレクトリ単位に書き出す方法を調べました。また、すべてのプロジェクトではなく、特定のプロジェクトだけビルドする方法も合わせて調べた結果を記載します。
.
├── package.json
├── src
│ ├── common <- プロジェクト共通で使うファイル
│ └── projects
│ ├── foo <- 単一プロジェクト
│ ├── fuga <- 単一プロジェクト
│ ├── hoge <- 単一プロジェクト
│ └── pages <- スペシャルプロジェクト
│ ├── pageOne <- スペシャルプロジェクトに関連するプロジェクト
│ ├── pageThree <- スペシャルプロジェクトに関連するプロジェクト
│ └── pageTwo <- スペシャルプロジェクトに関連するプロジェクト
└── webpack.config.js
通常であれば、webpack.config.js
のoutput
に以下のような設定をすると複数ページのアプリケーションとしてコンパイルしてくれます(参考)。
module.exports = {
//...
entry: {
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js'
},
output: {
filename: '[name].js',
path: __dirname + '/dist'
}
};
ディレクトリ毎にアプリケーションをコンパイル
今回は一つのディレクトリにまとめて書き出すのではなく、プロジェクトディレクトリ毎にそれぞれ書き出したい訳です。
│ └── projects
│ ├── foo
│ │ ├── app.js
│ │ ├── dist <- ここに書き出して管理したい
│ ├── fuga
│ │ ├── app.js
│ │ └── dist <- ここに書き出して管理したい
│ ├── hoge
│ │ ├── app.js
│ │ └── dist <- ここに書き出して管理したい
そのため、少しトリッキーかもしれませんが、entry
とoutput
の設定を変更し、
複数ディレクトリに書き出しできるような設定に変更しました。今回は各プロジェクトに distというフォルダを作成し、その中にコンパイル後のファイルを書き出します。イメージとしてはこんな感じです。
module.exports = {
//...
entry : {
'./src/projects/path-to-project/dist/hashfilename.js': './src/projects/path-to-project/app.js',
},
output: {
path: __dirname,
filename: '[name]'
}
};
output.path
は書き出す先のパスを指定します。今回は書き出す先のディレクトリをルート直下に指定し、ファイル名にパスを含めた書き出し先を指定しました。outputで使っている[name]
はWebpack側で定義されている設定でentryのkeyとなっている部分を置換します。なので上記の設定だと ./src/projects/path-to-project/dist/hashfilename.js が[name]
に入ることになります。
余談ですが、プロジェクト単位で作成されるdistディレクトリをgit
の管理から外す場合は.gitignore
に以下のように記述すると良いです。
/**/dist/
プロジェクト共通のライブラリを設定
それぞれのプロジェクトで共通して使いたいクラスは以下の設定で使用可能になります。
module.exports = {
//...
resolve: {
module: [
path.resolve(__dirname, './src/common'),
];
}
};
こうすると./src/common
が名前空間になり、例えば./src/common/Common.js
のファイルをプロジェクトで使いたい場合、import文は以下の書式になります。
import Common from 'Common';
また、./src/common/particle/flower.js
の場合はそのままサブディレクトリを繋げれば良いです。
import Common from 'particle/flower';
コンパイルするターゲットを決めてビルドする
複数プロジェクトのビルドに対応しておけば、1つのプロジェクトだけビルドしたい場合や、2つのプロジェクトだけビルドしたい場合など、特定のプロジェクトに絞ってビルドすることも可能になります。
方法はnpm run build
とする際に、webpack.config.js
に環境変数(env)としてビルドターゲットのプロジェクト名を渡します。こんな感じです。
npm run build -- --env.target="hoge"
このコマンドには2つの意味が含まれているのですが、--env.target="hoge"
の部分がwebpack.config.jsに環境変数(env)を渡している部分となります。
また、webpack.config.jsで環境変数(env)を受け取る場合、エクスポートを関数に変更する必要があります。
https://webpack.js.org/guides/environment-variables/
To use the env variable, you must convert module.exports to a function:
webpack --env.NODE_ENV=local --env.production --progress
const path = require('path');
module.exports = env => {
// Use env.<YOUR VARIABLE> here:
console.log('NODE_ENV: ', env.NODE_ENV); // 'local'
console.log('Production: ', env.production); // true
return {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
};
私の場合はenv
にtarget
プロパティを追加し、target
にプロジェクトディレクトリがあれば、そのプロジェクトのみビルドするようにしました。なお、プロジェクトのファイルは全て統一してapp.js
しています。
// NOTE: Use function in order to pass environment variables.
// @see https://webpack.js.org/guides/environment-variables/
module.exports = (env, {watch} ) => {
let entry = {};
if (env && env.target) {
// NOTE: Divide target string into array by comma.
const targetFiles = env.target.split(',').map(s => s.trim())
for (file of targetFiles) {
// NOTE: ここでプロジェクト毎にentryを作成しています(割愛)
const filePath = './' + join(targetDir, 'app.js');
const anEntry = createEntry(filePath);
Object.assign(entry, anEntry);
}
} else {
// NOTE: 全てのエントリー情報
entry = createAllEntry();
}
return {
//...
entry: entry,
output: {
path: __dirname,
filename: '[name]'
},
}
})
ここでもうひとつnpm run build -- --env.target="hoge"
実行時のbuildの後の--
についてですが、これのダブルハイフン以降に定義する変数を実行時のファイルに変数として渡すという意味です。例えば、以下のnpmパッケージがあり、
{
script:
{
server: "node index.js"
}
}
以下のコマンドを実行すると、index.js
に環境変数「--port=8080」が渡されます。
npm run server -- --port=8080
これは、通常のnode
コマンドを実行するときと同じ意味になります。
node server.js --port=1337
npmコマンドに渡す変数と、スクリプトに渡す変数の2つの違いがあるということですね。
Conclusion
今回は複数プロジェクトをビルドする時にプロジェクト毎にディレクトリを指定してビルドする方法と、プロジェクトの一部だけビルドする方法を調査しました。部分ビルドについてはプロジェクトが膨大になった時にビルド時間を短縮するために有効そうです。この部分ビルドはwatch
オプションをつけた時にも適用されるので、開発していくときに便利かもしれません。
今回の検証で使ったプロジェクトはGihubに置いてありますので、何かの参考になれば幸いです。