Webpackで複数プロジェクトの一括コンパイルと部分コンパイル


インデックス
* [ディレクトリ毎にアプリケーションをコンパイル](#ディレクトリ毎にアプリケーションをコンパイル)
* [プロジェクト共通のライブラリを設定](#プロジェクト共通のライブラリを設定)
* [コンパイルするターゲットを決めてビルドする](#コンパイルするターゲットを決めてビルドする)


**要件**
以下のようなディレクトリ構成があり、それぞれのプロジェクト毎にJavaScriptをコンパイルして、それぞれのプロジェクトディレクトリにコンパイルしたファイルを格納したいといった案件がありました。Webpackでプロジェクトをコンパイルする時、今までは一つのディレクトリにのみ全てのプロジェクトを書き出していたので、ディレクトリ単位に書き出す方法を調べました。また、すべてのプロジェクトではなく、特定のプロジェクトだけビルドする方法も合わせて調べた結果を記載します。

```bash:ディレクトリ構造
.
├── package.json
├── src
│   ├── common                <- プロジェクト共通で使うファイル
│   └── projects
│       ├── foo　　　　　　　　　<- 単一プロジェクト
│       ├── fuga              <- 単一プロジェクト
│       ├── hoge              <- 単一プロジェクト
│       └── pages             <- スペシャルプロジェクト
│           ├── pageOne　　　　<- スペシャルプロジェクトに関連するプロジェクト　
│           ├── pageThree     <- スペシャルプロジェクトに関連するプロジェクト 
│           └── pageTwo       <- スペシャルプロジェクトに関連するプロジェクト　
└── webpack.config.js
```

通常であれば、```webpack.config.js```の```output```に以下のような設定をすると複数ページのアプリケーションとしてコンパイルしてくれます([参考](https://webpack.js.org/concepts/entry-points/#multi-page-application))。


```js:特定のディレクトリに別アプリケーションとして書き出し
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'
  }
};

```

## ディレクトリ毎にアプリケーションをコンパイル

今回は一つのディレクトリにまとめて書き出すのではなく、プロジェクトディレクトリ毎にそれぞれ書き出したい訳です。

```bash:それぞれのプロジェクトディレクトリに書き出し
│   └── projects
│       ├── foo
│       │   ├── app.js
│       │   ├── dist          <- ここに書き出して管理したい
│       ├── fuga
│       │   ├── app.js
│       │   └── dist          <- ここに書き出して管理したい
│       ├── hoge
│       │   ├── app.js
│       │   └── dist          <- ここに書き出して管理したい
```

そのため、少しトリッキーかもしれませんが、```entry``` と```output```の設定を変更し、
複数ディレクトリに書き出しできるような設定に変更しました。今回は各プロジェクトに **dist**というフォルダを作成し、その中にコンパイル後のファイルを書き出します。イメージとしてはこんな感じです。

```js:イメージ
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```に以下のように記述すると良いです。

```git:.gitignoreの設定
/**/dist/
```


## プロジェクト共通のライブラリを設定
それぞれのプロジェクトで共通して使いたいクラスは以下の設定で使用可能になります。

```js:ライブラリパスの設定
module.exports = {
  //...
  resolve: {
    module: [
      path.resolve(__dirname, './src/common'), 
    ];
  }
};
```

こうすると```./src/common``` が名前空間になり、例えば```./src/common/Common.js```のファイルをプロジェクトで使いたい場合、**import**文は以下の書式になります。

```js
import Common from 'Common';
```

また、```./src/common/particle/flower.js```の場合はそのままサブディレクトリを繋げれば良いです。

```js
import Common from 'particle/flower';
```

## コンパイルするターゲットを決めてビルドする
複数プロジェクトのビルドに対応しておけば、1つのプロジェクトだけビルドしたい場合や、2つのプロジェクトだけビルドしたい場合など、特定のプロジェクトに絞ってビルドすることも可能になります。
方法は```npm run build```とする際に、```webpack.config.js```に環境変数(env)としてビルドターゲットのプロジェクト名を渡します。こんな感じです。

```bash
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:

```bash:環境変数(env)の渡し方
webpack --env.NODE_ENV=local --env.production --progress
```

```js:環境変数(env)の受け取り方
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```しています。


```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パッケージがあり、

```js:package.json
 {
 	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](https://github.com/mitsuyacider/webpack-multi-exportation)に置いてありますので、何かの参考になれば幸いです。