babel6にmodule.exportsを追加するだけのプラグインを作った

[babel6はCommonJSに対応しないので](https://phabricator.babeljs.io/T2212)、コンパイル済みのコードに`module.exports`（requireのエントリポイント）が含まれません。これは、[transform-es2015-modules-commonjsプラグイン](https://www.npmjs.com/package/babel-plugin-transform-es2015-modules-commonjs)を使っても改善しませんでした。[issueもしょっちゅう立ってはLockされてます。](https://phabricator.babeljs.io/T2975)

[add-module-exports](https://github.com/59naga/babel-plugin-add-module-exports)プラグインというものを作成しました。これはコード末尾に`module.exports`文を加えます。

```bash
npm install babel-cli --save-dev
npm install babel-preset-es2015 --save-dev
npm install babel-plugin-add-module-exports --save-dev
```

## `0.1.*`系

`babel@5`の動作に準拠するバージョン`0.1.0`を公開しました。
このバージョンでは、`export default`文のみを使用した場合にだけ、`module.exports`文を追加します。

```js:index.js
export default 'foo'
```

上記は`module.exports`文を**追加します**。

```
babel index.js --presets es2015 --plugins add-module-exports 
# 'use strict';
#
# Object.defineProperty(exports, "__esModule", {
#   value: true
# });
# exports.default = 'foo';
# module.exports = exports['default'];
```

下記は**追加しません**。

```js:index.js
let foo= 'bar'
export default 'baz'
export {foo}
```

```bash
babel index.js --presets es2015 --plugins add-module-exports 
# 'use strict';
#
# Object.defineProperty(exports, "__esModule", {
#   value: true
# });
# var foo = 'bar';
# exports.default = 'baz';
# exports.foo = foo;
```

## `0.0.*`系

常に`module.exports`文を発行します。`Object.assign`でマージするため、**動作は破壊的です**。

```js:index.js
let foo= 'bar'
export default 'baz'
export {foo}
```

下記のように変換されるでしょう。

```bash
babel index.js --presets es2015 --plugins add-module-exports 
# 'use strict';
#
# Object.defineProperty(exports, "__esModule", {
#   value: true
# });
# var foo = 'bar';
# exports.default = 'baz';
# exports.foo = foo;
# module.exports = Object.assign(exports.default || {}, exports);
```

Object.assignを使用したくない場合は、[polyfillに置換できるプラグイン](https://github.com/babel/babel/tree/development/packages/babel-plugin-transform-object-assign)が用意されているので、これを利用して下さい。

参考
---
* [babel6での変更点　Gulp・Webpackの設定 by kamijin_fanta(Qiita)](http://qiita.com/kamijin_fanta/items/e8e5fc750b563152bbcf)
* [.babelrc / package.json](https://babeljs.io/docs/usage/babelrc/)

よもやま話
---

babel@6からプラグインの作成方法も変わったらしく、ハローワールド的な記事を探しても、[5系のものがほとんど](https://github.com/RReverser/babel-plugin-hello-world)でした。その中、頼りになるのは[公式の似た目的を持つプラグインの実装](https://github.com/babel/babel/tree/v6.1.3/packages/babel-plugin-transform-es2015-modules-commonjs)と[日本語の5系の解説記事](http://qiita.com/shuhei/items/96a852f7e0995fd42981)でした。

例えば拙作のプラグインでは、[babel-template](https://www.npmjs.com/package/babel-template)というsebmck製モジュールに依存していますが、これは文字列からASTの生成関数を返すファクトリ関数です。

```js
var babelTemplate= require('babel-template')
babelTemplate('var hoge="fuga"')()
// { type: 'VariableDeclaration',
//   start: undefined,
//   end: undefined,
//   loc: undefined,
//   declarations: 
//    [ { type: 'VariableDeclarator',
//        start: undefined,
//        end: undefined,
//        loc: undefined,
//        id: [Object],
//        init: [Object],
//        _fromTemplate: true } ],
//   kind: 'var',
//   _fromTemplate: true }
```

プラグインのエントリポイントは以下のようなオブジェクトを返すのが決まりですが、visitor内のトランスフォーム関数の第一引数`path`がAST改変用のメソッド群を持つので、これの`.pushContainer('body')`にASTの配列を渡すと、オレオレコードを追加することができました。

```js:babel-plugin-add-module-exports/lib/index.js
// Dependencies
var babelTemplate= require('babel-template')

// Public
module.exports= function(){
  return {
    visitor: {
      Program: {
        exit: function(path){
          var topNodes= []

          var code= 'module.exports=Object.assign(exports.default||{},exports);'
          var node= babelTemplate(code)()
          topNodes.push(node)

          path.pushContainer('body',topNodes)// プログラム末尾に`module.exports = Object.assign(exports.default || {}, exports);`を追加する
        },
      },
    },
  }
}
```