webpackでモジュール解決しながらディレクトリ構成を保ったまま書き出す

webpackって超便利ですよね。
違うファイルで作ったモジュールを１つにまとめてることができて、bundle.jsみたいなファイルにまとめることが出来て。。。

**でも。。。。**

僕みたいなまだまだレガシーエンジニアには１つのファイルにまとめてSPAライクにすることなんて出来ないわけです。

ぶっちゃけ共通のApp.jsと各ページ1ファイル読み込むパターンで十分なんです。
初期ロードで数10MBのjsを読み込むのって我慢できないんです。

なので今回はwebpackを使って複数のディレクトリにまたがったファイルを依存解決も行いながら別ディレクトリに同じ構成で書き出すのをやってみたいと思います。

## 目指す姿

applicationのルートディレクトリにfrontendsディレクトリを作成し、PC用の各ページのjsファイル、mobile各ページ用のjsファイル、共通で使えそうなmodulesファイルに分けました。

```
assets
frontends
├── App.es6
├── App.mobile.es6
├── mobile
│   └── users
|       ├── setting.es6
|       └── show.es6
├── modules
│   ├── _Dropdown.es6
│   ├── _Follow.es6
│   ├── _Tab.es6
│   └── _XhrMethods.es6
├── pc
│   └── users
|       ├── setting.es6
|       └── show.es6
└── validations
    ├── _Contact.es6
    ├── _Password.edit.es6
    ├── _Password.edit.from.email.es6
    └── _Send.email.es6
```

もちろん各ファイルの中では`import`しまくってます！

```js
// pc/users/show.es6
import Tab from '../../modules/_Tab.es6'
import DropDown from '../../modules/_DropDown.es6'

const tab = new Tab({active: 'hogehoge',...})
const dropDown = new DropDown({el: 'jsDropdown'})

// more
```

実際に使ってるのは`App.es6, App.mobile.es6, pc/, mobile/`なので、そのファイルをそのまま`dist/javascripts`に書き出したいと思います。

```
dist/javascripts
├── App.es6.js
├── App.mobile.es6.js
├── mobile
│   └── users
|       ├── setting.es6.js
|       └── show.es6.js
└── pc
    └── users
        ├── setting.es6.js
        └── show.es6.js
```

## よくあるwebpackで複数ファイルを出力する方法
実際によくあるのが、`webpack.config.js`の`entries`にhashを指定すること複数のファイルの出力に対応するということです。

例: 複数ファイルを指定

```js
module.exports = {
  entry: {
    pc_users_show: 'frontends/users/show.es6',
    pc_users_setting: 'frontends/users/setting.es6',
    // more
  },
  output: {
    path: '/dist/javascripts',
    filename: '[name].js'
  },
  // その他の設定
}

```

こうしてwebpackwを実行すると`/dist`に`entry`で指定したkeyの名前でファイルが主力されます。

```
dist/javascripts
├── pc_users_show.es6.js
└── pc_users_setting.es6.js
```

ここで問題なのが**output.pathが１つのみなのでディレクトリごとに書き出すなどの処理ができない**ということです。

## webpackで階層ごとにファイルを書き出す
**output.path**ではディレクトリが１つしか指定できませんが、entryのkeyをいじることで階層ごとに書き出すことが可能です。

```js
module.exports = {
  entry: {
    pc/users/show: 'frontends/users/show.es6', //keyを階層化して指定する
    pc/users/setting: 'frontends/users/setting.es6',
    // more
  },
  output: {
    path: '/dist/javascripts',
    filename: '[name].js'
  },
  // その他の設定
}

```
↓

```
dist/javascripts
└── pc
    └── users
        ├── setting.es6.js
        └── show.es6.js
```

こうすることで**階層化しつつ、各モジュール依存を解決した状態で書き出す**ことは出来ました(๑•̀ㅂ•́)و✧

ただ、ファイルが増えるたびに全てのファイルをentryのkeyの追加していくのは面倒くさいです。
なので、`glob`を使用してファイル名を取得してkeyも一緒に作るようにしちゃいましょう！

## globを使用してwebpackでいい感じに階層化してモジュール依存解決して書き出す

```js
const path    = require('path'),
      glob    = require('glob'),
      _       = require('lodash');


const jsBasePath = path.resolve(__dirname, 'frontends'),
      jsDistPath = path.resolve(__dirname, 'dists/javascripts')

// filenameを取得するメソッドを追記
// 後から使う
String.prototype.filename = function(){
  return this.match(".+/(.+?)([\?#;].*)?$")[1];
}

// jsBasePath配下の.es6のpathも含めたファイルを取得する
// _から始まるファイルはmoduleなので書き出す必要はないのでいらない
var targets = _.filter(glob.sync(`${jsBasePath}/**/*.es6`), (item) => {
  return !item.filename().match(/^_/)
});

// entryに入れるhash
var entries = {};

// pathも含めたfilenameからpathとfilenameでhashを作る
targets.forEach(value => {
  var re = new RegExp(`${jsBasePath}/`);
  var key = value.replace(re, '');

  // 確認用に取得したファイル名を出す
  console.log('--------------------------')
  console.log(key) 
  console.log(value.filename()) 
  entries[key] = value;
});

module.exports = {
  entry: entries, //作成したhashをset
  output: {
    path: jsDistPath,
    filename: '[name].js'
  },
  module: {
    loaders: [
      {
        test: /\.es6$/,
        loader: 'babel-loader',
        exclude: /node_modules/,
        query: {
          presets: ['es2015']
        }
      }
    ]
  }
}

```

こうすることで階層構造を保ったままファイルを書き出すことが出来ました。

```
Hash: a94765d83d77487ff60d
Version: webpack 1.13.3
Time: 18715ms
                              Asset     Size  Chunks             Chunk Names
                         App.es6.js   596 kB       0  [emitted]  App.es6
        mobile/users/setting.es6.js  7.86 kB       1  [emitted]  mobile/users/setting.es6
           mobile/users/show.es6.js   592 kB       2  [emitted]  mobile/users/show.es6
                  App.mobile.es6.js   583 kB       3  [emitted]  App.mobile.es6
            pc/users/setting.es6.js  7.86 kB       4  [emitted]  pc/users/setting.es6
               pc/users/show.es6.js   593 kB       5  [emitted]  pc/users/show.es6
    + 386 hidden modules
[BS] Reloading Browsers...
```

## 終わりに
みなさんお気付きの通り、こちら**超ゴリゴリ**の実装な気がしてたまりません(´；ω；｀)
何かいい方法があればご教授ください。。。