babel-preset-env を試す

2017年3月29日 0 開発 , , ,

npm として配布するものは純粋な Node 機能のみで構成したいため脱 Babelしたが、Web フロントエンドや Electron では最新の ECMAScript 機能を利用したい。

というわけで、これまでは Babel + babel-preset-latest で JavaScript を変換してきた。しかし latest だと Web ブラウザーや Electron が最新規格に対応しても個別に変換を無効化するのが難しい。

例えば ES2015 Classes は大半の Web ブラウザーが対応済みにも関わらず

var Sample = function () {
  function Sample() {
    _classCallCheck(this, Sample);
  }
}

のように変換されてしまう。一方、機能単位で変換を無効にできるとしても Web ブラウザー毎の対応状況を調べるのは実に面倒。ECMAScript 6 compatibility table をマメにチェックしながら下限となる環境を決める必要がある。

こうした悩みを解決するツールとして babel-preset-env が提供されている。用途を latest と比較すると以下のようになる。

preset 用途
latest 最新 ECMAScript を常時 ES5 相当に変換。
env 最新 ECMAScript を指定された環境に基づき最小限 ES5 に変換。

対象環境を考えるのが面倒で変換コストや品質を気にしないなら latest、なるべく無駄な変換をなくしたいなら env を採用することになるだろう。

試してみる

babel-preset-env を試してみる。

Web フロントエンドや Electron だと Babel 単体よりも Browserify + babelifywebpack + babel-loader のように bundler と組み合わせて利用する機会が多い。しかし bundler 部分も含めると Babel が変換した結果に限定してチェックするのが難しいため実行には babel-cli を採用することにした。

変換対象として ES2015 と ES2016 の代表的な機能を使用した 2 種類の JavaScript を定義。まずは sample.js

// ES2015
export default class Sample {
  message (text) {
    console.log(text)
  }

  async asyncFunc () {
    const wait = (n) => {
      return new Promise((resolve) => setTimeout(() => resolve(n), n))
    }

    await wait(1000)
    console.log('finish!!')
  }

  static func (text = 'sample') {
    console.log(text)
  }
}

// ES2016: Exponentiation Operator
export function pow (a = 0, b = 0) {
  return a ** b
}

// ES2016: Array.prototype.includes
export function includes (arr = [], value) {
  return Array.isArray(arr) ? arr.includes(value) : false
}

sample.js を参照する index.js

import Sample, { pow, includes } from './sample.js'

{
  const sample = new Sample()
  sample.message('Message1')
}

Sample.func('Message2')

console.log(pow(2, 3))
console.log(includes([1, 2, 3], 2))

これらを babel-preset-env の README に掲載されている Examples と現時点で最新の Electron v1.6 向け設定で変換する npm-scripts を定義した。Babel の設定も含めて package.json で完結しているため長いけれど全掲載する。

{
  "name": "using-babel-preset-env",
  "version": "1.0.0",
  "description": "",
  "author": "akabeko",
  "license": "MIT",
  "main": "index.js",
  "keywords": [
    "babel-preset-env"
  ],
  "repository": {
    "type": "git",
    "url": "https://github.com/akabekobeko/examples-web-app.git"
  },
  "babel": {
    "env": {
      "default": {
        "presets": [["env"]]
      },
      "chrome52": {
        "presets": [
          ["env", {
            "targets": {
              "chrome": 52
            }
          }]
        ]
      },
      "chrome52webpack_loose": {
        "presets": [
          ["env", {
            "targets": {
              "chrome": 52
            },
            "modules": false,
            "loose": true
          }]
        ]
      },
      "browserslist": {
        "presets": [
          ["env", {
            "targets": {
              "chrome": 52,
              "browsers": ["last 2 versions", "safari 7"]
            }
          }]
        ]
      },
      "node_current": {
        "presets": [
          ["env", {
            "targets": {
              "node": "current"
            }
          }]
        ]
      },
      "debug_output": {
        "presets": [
          ["env", {
            "targets": {
              "safari": 10
            },
            "modules": false,
            "useBuiltIns": true,
            "debug": true
          }]
        ]
      },
      "include_exclude_plugins_buildin": {
        "presets": [
          ["env", {
            "targets": {
              "browsers": ["last 2 versions", "safari >= 7"]
            },
            "include": ["transform-es2015-arrow-functions", "es6.map"],
            "exclude": ["transform-regenerator", "es6.set"]
          }]
        ]
      },
      "electron": {
        "presets": [
          ["env", {
            "targets": {
              "electron": 1.6
            }
          }]
        ]
      }
    }
  },
  "scripts": {
    "start": "run-s build",
    "build:default": "cross-env NODE_ENV=default babel ./src --out-dir ./dist/default",
    "build:chrome52": "cross-env NODE_ENV=chrome52 babel ./src --out-dir ./dist/chrome52",
    "build:chrome52webpack_loose": "cross-env NODE_ENV=chrome52webpack_loose babel ./src --out-dir ./dist/chrome52webpack_loose",
    "build:browserslist": "cross-env NODE_ENV=browserslist babel ./src --out-dir ./dist/browserslist",
    "build:node_current": "cross-env NODE_ENV=node_current babel ./src --out-dir ./dist/node_current",
    "build:debug_output": "cross-env NODE_ENV=debug_output babel ./src --out-dir ./dist/debug_output",
    "build:include_exclude_plugins_buildin": "cross-env NODE_ENV=include_exclude_plugins_buildin babel ./src --out-dir ./dist/include_exclude_plugins_buildin",
    "build:electron": "cross-env NODE_ENV=electron babel ./src --out-dir ./dist/electron",
    "build": "run-s build:*"
  },
  "devDependencies": {
    "babel-cli": "^6.24.0",
    "babel-preset-env": "^1.2.2",
    "cross-env": "^3.2.4",
    "npm-run-all": "^4.0.2"
  }
}

Babel の設定は NODE_ENV 単位に定義できることを利用して cross-env により分岐している。npm start を実行すると dist/ 配下に設定ごとの変換結果が出力される。普通に指定するなら presets 配下の内容を Babel 設定のルートに記述すればよい。例えば .babelrc に Electron 用の設定を定義するなら以下のようにする。

{
  "presets": [
    ["env", {
      "targets": {
        "electron": 1.6
      }
    }]
  ]
}

サンプルの出力結果を比較すると、例えば default なら全変換され chrome52webpack_loose だと async/await 以外はそのままであることが分かる。また chrome52 では async/await 変換があり electron だと v1.6.0 から Chromium 56.0.2924.87 を採用しているため async/await はそのままに Modules は共通して変換されていた。

babel-preset-env の README によれば、変換の基準は前述の ECMAScript 6 compatibility table を基準として判断しているのだという。つまり機能と対象環境の組み合わせ管理を babel-preset-env に丸投げできるわけだ。本当に ECMAScript 6 compatibility table や Electron のバージョンをチェックしているのか?と targets にデタラメな値を設定してみたら実行時にエラーとなった。Electron に関しては 1.0 以降が対象で最新の 1.6 系まで指定可能。

ただし targets のバージョンは JSON の number になるため小数点第一位までしか対応しておらず 1.6.2 のように semver 形式は受け付けていない。また "node": "current" は可能だが "electron": "current" を指定したらエラーになった。Electron も browsers のように相対値で指定したいものだ。

とはいえ babel-preset-latest を使用しつつ「将来 ES2015 変換をやめたくなったらどうしよう?」などと考えていたので、そのような処理を動作環境の指定へ抽象化して変換してくれる babel-preset-env は実にありがたい。

babel-preset-env 設定により想定している動作環境が明示される点もメリット。これはアプリの仕様を外部へ説明するのに役立つ。また設定を Git リポジトリなどで管理しているなら動作環境の変遷も記録される。例えば IE サポートを打ち切るとして、その履歴をきちんと残せるのだ。

最後に動作確認で使用したサンプル プロジェクトを公開しておく。

JavaScript Standard Style を試す

2017年2月17日 0 開発 , ,

話題の JavaScript Standard Style を試してみた。

背景

以前、以下の記事とはてブで JavaScript Standard Style を知った。

はてブではセミコロンの省略に抵抗感のある人が多く、私もそうだった。しかし同はてブで id:mysticatea さんが指摘されているように ESLint の no-unexpected-multiline でセミコロン省略時に問題のおきるコードを検出できる。また、

  • 昨年末に Swift 入門してセミコロンのないコードに慣れた
  • Electron の JavaScript コードがセミコロンなしルールで読みやすかった

という理由もあり、セミコロンなしも案外よいものじゃないかと考えるようになった。

私のコーディング スタイルは世間の標準からみると独特で、これは過去に在籍していたプロジェクトのルールを踏襲している。主な特徴としては

  • 括弧の内側にスペースを入れる
  • ifwhile などのキーワードと関数名の後にはスペースを入れない

というもの。C 言語や JavaScript でよく見られる K&R 系だとこんな感じのコードも

function isArray (arg) {
  if (Array.isArray) {
    return Array.isArray(arg);
  }

  return Object.prototype.toString.call(arg) === '[object Array]';
}

私のスタイルだとこうなる。

function isArray( arg ) {
  if( Array.isArray ) {
    return Array.isArray( arg );
  }

  return Object.prototype.toString.call( arg ) === '[object Array]';
}

これはこれで気に入っていたのだが GitHub で OSS を運用にするようになり、第三者からの PR は一般的な方のスタイルでくるため扱いに困っていた。スタイルの違いを理由に断るとか直すのも面倒なので今はそのまま merge しているけれど、そもそも自分の好みより世に迎合するほうがよいんじゃないか?と思い始めた。

あと Xcode の Editor におけるコーディング スタイル設定が貧弱というのもある。他の IDE だと私のスタイルを再現するのに十分な設定があるためそうしてきた。しかし Xcode の Text Editing は驚くほど設定がない。まともにいじれるのは Indentation ぐらいである。

これまでは仕方なく根性で手動整形してきたが、Swift 入門を機にあきらめた。iOS で SQLite – FMDB の使い方 2017のサンプルでは Xcode の提示するスニペットそのままに書いている。

この経験を経て、自分のスタイルへ固執することをやめることにした。プラットフォーム標準があればそれに従い、IDE や Editor、Linter の補助を最大限に享受する方針へ転換する。

というわけで、まずは公私ともに書く機会の多い JavaScript のコーディング スタイルから変更してみる。

JavaScript Standard Style

JavaScript のコーディング スタイルとしては

あたりが有名どころらしい。どれを選ぶか迷ったが Electron のようなセミコロンなしスタイルを採用している JavaScript Standard Style にしてみた。Standard と銘打つ度胸と GitHub の star 数も判断材料である。セミコロン以外はよく見るスタイルなので、ここを受け入れられるかが重要。

JavaScript Standard Style への準拠にあたり、それを保証する仕組みがほしいので ESLint を利用。今回は akabekobeko/npm-wpxml2md プロジェクトで試す。

feross/eslint-config-standard を参考にプロジェクトのローカルに必要な npm をインストール。

$ npm i -D eslint-config-standard eslint-plugin-standard eslint-plugin-promise

次にプロジェクトのルートで .eslintrc を定義。

{
  "extends": "standard",
  "env": {
    "mocha": true
  },
  "rules": {
    "no-multi-spaces": 0,
    "yoda": 0
  }
}

JavaScript Standard Style を使用するだけなら "extends": "standard" だけでよい。しかし mocha で書いたユニット テストも対象にしたいのと、

  • 連続した複数行の変数宣言などで縦位置をスペースで揃えたい
  • if 文で不等号による範囲チェックを if (0 <= value && value < max) のように書きたい

のでそれらの設定を追加した。JavaScript Standard Style はスタイルに準拠していることを示す証として

Standard - JavaScript Style Guide

というバッヂを提供している。ルール緩和した場合でもこれをつけてよいものか迷ったけれど緩和は極小なので README へ掲示することにした。第三者が README をながめたとき、基本となるコーディング スタイルを視認できるのはよいことだ。

私は JavaScript のコーディングに Atom を使用しており、ESLint によるリアル タイムなチェックのため

を採用している。これまで linter-eslint はグローバルにインストールした ESLint とプラグインを使用して設定も ~/.eslintrc を参照するようにしていたが、このプラグインはプロジェクトのローカルに ESLint と .eslintrc を検出するとそちらを優先してくれる。

そのため既存プロジェクトは現行のスタイルを維持しつつ、個別に JavaScript Standard Style を採用する運用が可能である。いきなりグローバルを書き換えてもよいけど、少しずつ移行するほうが安全だろう。

スタイルのチェックは基本的に Atom 上で確認 & 修正するのだがファイル単位で個別に作業していると抜けも出やすいため、一括チェック可能な仕組みも用意する。私は npm-scripts に

{
  "scripts": {
    "eslint": "eslint ./src"
  }
}

を定義して

$ npm run eslint

を実行している。これは AltJS/AltCSS の transpile のようにバックグラウンドでファイル変更の検出と自動チェックさせるほうがよいのかもしれない。

セミコロンなき世界

旧スタイルから JavaScript Standard Style へこのように書き換えてみた。これらの中で比較的、短めのコードを引用する。

#!/usr/bin/env node

'use strict'

const CLI = require('./cli.js').CLI
const WpXml2Md = require('../lib/index.js')

/**
 * Entry point of the CLI.
 *
 * @param {Array.<String>} argv   Arguments of the command line.
 * @param {WritableStream} stdout Standard output.
 *
 * @return {Promise} Promise object.
 */
function main (argv, stdout) {
  return new Promise((resolve, reject) => {
    const options = CLI.parseArgv(argv)
    if (options.help) {
      CLI.printHelp(stdout)
      return resolve()
    }

    if (options.version) {
      CLI.printVersion(stdout)
      return resolve()
    }

    if (!(options.input)) {
      return reject(new Error('"-i" or "--input" has not been specified. This parameter is required.'))
    }

    if (!(options.output)) {
      return reject(new Error('"-o" or "--output" has not been specified. This parameter is required.'))
    }

    return WpXml2Md(options.input, options.output, {
      noGFM: options.noGFM,
      noMELink: options.noMELink,
      report: options.report
    })
  })
}

main(process.argv.slice(2), process.stdout)
.then()
.catch((err) => {
  console.error(err)
})

実にスッキリ。見慣れるまでは JavaScript に見えないかもしれない。

これまで C 言語系の構文をもつプログラミング言語に慣れ親しんできたためセミコロン入力は手癖になっていたけど、いざ不要になるとこれがどれだけ負担だったかを認識させられる。

はじめは、ほんの 1 文字だしプログラミングでは書くより考える時間のほうが長いのだから気にするほどのことか?と考えていた。しかし ; + EnterEnter に置き換わることは、実際に体験してみると実に大きい。正確に構文の末尾へセミコロンを置くことと、単に改行するだけというのはかなり違う。セミコロンなし派が一定数いる意味を身をもって知った。

なおセミコロン省略により起き得る問題は前述のように no-unexpected-multiline が検出してくれる。実際の eslint-config-standard/eslintrc.json でも "no-unexpected-multiline": "error" と設定されているため安心だ。

所感

JavaScript Standard Style 導入の所感をまとめる。

  • 一般的な JavaScript と自身のコードを交互にながめても違和感をおぼえにくくなった
  • Atom のスニペットをそのまま利用できるようになった
  • 括弧のスペースを詰めてもそれなりに読める
  • まともな Editor なら構文強調のおかげで括弧とそれ以外を区別しやすいので困らない
  • セミコロンなしはスッキリしてかなり読みやすい
  • セミコロンを入力するのがどれだけ手間だったか実感できる

結論。JavaScript Standard Style は素晴らしかった。今後、他のプロジェクトでも採用する予定。

npm 開発で脱 Babel してみる

2017年1月25日 0 開発 , , ,

自作 npm の開発で脱 Babel したときの対応と問題点まとめ。

  • 2017/1/31 訂正
    power-assert の作者、t_wada さんより power-assert は babel-register を通すなどしないと assert 置換が働かず素の assert になってしまうという指摘があったのでユニットテスト関連の記述を訂正

脱 Babel を決めた背景

私はいくつか自作 npm を公開している。これらは ES2015 以降の機能と構文を利用して npm publish の際に Babel で ES5 相当へ transpile している。この運用で特に問題も起きていない。またプリセットに babel-preset-latest を採用することで ES 関係の規格追従を Babel 任せにできる安心感もあり、ずっとこのままでいいと思っていた。

ある日、職場で Node アプリを開発している人から「Babel 依存は怖くないですか?」という質問があった。Babel にバグがあったら調査や修正は困難だし、使わなくてよいならばそうするに越したことはないのでは?と。

これまで C++、C#、Java などコンパイル前提の言語で開発した経験から、よほどのことがない限りコンパイル結果は信頼に足ると判断していた。また、コンパイルされたマシン語や中間言語を人間が直に記述するのは非常にキツイ。そのため脱コンパイラーという選択肢は現実的ではないと認識している。

一方、JavaScript における transpile は高級言語どうしの変換となる。その気になれば人間が書けるのだ。

transpiler への慣れから、この事実をすっかり忘れていた。Node であれば Web フロントエンドと異なり動作環境の分岐は少なく、package.json の engines にて対象環境を限定することも可能である。ならば新しい Node を前提として脱 Babel を検討してみるのもよさそうと考えはじめた。

そんな折、npm-run-all が v4.0.0 で Babel による transpile を廃止。Node は v4 時点で ES Modules を除く大半の ES2015 機能が実装されているため、この範囲で足りるなら transpile せずにそのままリリース可能だ。その前例として普段利用しているツールが脱 Babel したのはインパクトある。

これらを踏まえ、まずは自作 npm のうちダウンロード数の少なくニッチな wpxml2md から脱 Babel を試してみることにした。

脱 Babel への道のり

脱 Babel 対応で実施したことを書く。

Node 環境の明示的な指定

脱 Babel における前提条件として動作環境とする Node のバージョンを決める。npm-run-all は Node v4 を下限としているようだが、wpxml2md では v6 としておく。

開発と検証コストを考慮して自作 npm の動作環境は「最新 + 最新 LTS」としている。2017/1 時点の最新 Node は v7 系、LTS は v6 と v4 があるため対象は「v7 + v6」となる。これまでは Babel による transpile で v4 以下でも動作していたのだが、これを廃止することで明示的な下限の指定が必要となった。これは package.json の engines プロパティに記述する。

{
  "engines": {
    "node": ">= 6"
  }
}

ちなみに

node v6, v7

というバッヂを用意していて前から README へ掲載していた。今回の対応により、ようやくこれが本来の意味をあらわすようになった。

Babel の transpile を廃止

これまでは Babel の transpile を前提として以下のような Babel 設定と npm-scripts を利用していた。

{
  "babel": {
    "presets": [
      "latest"
    ],
    "env": {
      "development": {
        "presets": [
          "power-assert"
        ]
      }
    }
  },
  "scripts": {
    "test": "mocha --timeout 50000 --compilers js:babel-register test/**/*.test.js",
    "watch": "babel src --out-dir ./ --watch",
    "start": "npm run watch",
    "build": "babel src --out-dir ./",
    "prepublish": "npm run build"
  }
}
script 内容
test 予約された npm run のタスク。 mocha と power-assert によるユニット テスト。
watch 開発用。ファイル監視による自動 transpile を実行。
start 予約された npm run のタスク。watch を呼び出すだけ。
build リリース用。現時点のソース コードで transpile を実行。
prepublish 予約された npm publish 時に呼び出されるタスク。build を呼び出しているため npm として公開されるイメージは transpile されたものになる。なお prepublish は現在 deprecated になっていて prepare へ修正すべきなのだが直し忘れていた。

これが脱 Babel によりこうなる。

{
  "babel": {
    "env": {
      "development": {
        "presets": [
          "power-assert"
        ]
      }
    }
  },
  "scripts": {
    "test": "mocha --timeout 50000 --compilers js:babel-register test/**/*.test.js"
  }
}

transpile 不要となるため関連するタスクが消え、ユニット テストだけが残った。ただし power-assert を利用しているなら標準 assert を置換するための transpile が必要なので、このための設定は維持する。

env.development.presetspower-assert だけ指定することで、Babel 依存がユニット テストに限定されていることが明示されるだろう。必要な Babel 関連の npm も babel-registerbabel-preset-power-assert だけになる。

ES Moduels を CommonJS 化する

Node の ES Modules 対応については以下が詳しい。

なお現時点の最新 Node である v7 においても ES Modules には対応していないため、export/import は CommonJS の exports/require へ修正する必要がある。例えば

export const Options = {
};

export default class CLI {
}

const Options = {
};

class CLI {
}

module.exports = {
  Options: Options,
  CLI: CLI
};

とする。読み込む側は

import CLI from `cli.js`;
import { Options } from `cli.js`;

const CLI = require( `cli.js` ).CLI;
const Options = require( `cli.js` ).Options;

とする。ひとつのモジュールから exportdefault export で複数のインターフェースを公開していると面倒である。しかし ES Modules で書いていたなら import はソース コード冒頭に集約されているから悩む余地なく機会的な作業になる。なんなら正規表現でまとめて変換できる。

ユニット テストについても同様に対応すること。

余談。

CommonJS にした後でも将来の ES Modules 移行を容易にするため require はソース コード冒頭へ書く習慣をつけたほうがよいかもしれない。その場合、読み込み先を camelCase で命名しているとローカル変数と競合する可能性が高くなるため PascalCase にしたくなり、私はそうしている。例えば fsFs と命名している。

Node と CommonJS だとスコープを意識して require を使い分け、なるべく関数ローカルで宣言する派が多数な感じなので ES Modules 対応されたときが気になる。私のようにするか、それともよりよい慣習となるのか?実に楽しみだ。

ESDoc 対応

npm のコード ドキュメント生成に ESDoc を採用している場合、そのままでは CommonJS を解釈できないので対応が必要になる。CommonJS は

上記 issue で紹介されている esdoc-node により対応できる。ESDoc にプラグイン機能があることを初めて知った。これを指定することでコードが ESDoc に解釈される前処理を実行できるらしい。例えば esdoc-node は CommonJS を ES Modules に変換して ESDoc に渡す。

さっそく使ってみよう。まず esdoc-node をプロジェクトに追加。

$ npm install -D esdoc-node

次にこれを ESDoc 設定へ追加する。私は ESDoc の設定を package.json に定義しているので

{
  "esdoc": {
    "source": "./src",
    "destination": "./esdoc",
    "test": {
      "type": "mocha",
      "source": "./test"
    },
    "plugins": [
      { "name": "esdoc-node" }
    ]
  }
}

このようにした。設定後、ESDoc を実行して CommonJS なコードも正しく解析されることを確認。

ただしローカル実行ではなく ESDoc Hosting Service は CommonJS やプラグインに対応していないようだ。試しに wpxml2md の API Document を生成 してみたのだが、2017/1/25 時点では esdoc-node 未指定の状態と同様に CommonJS の絡むものが解釈できていない。

この点について要望 issue を登録してみたのだけど、もしプラグイン対応する場合

  • Hosting Service 側でプラグインを網羅しておき選択実行する
    • プラグインを中央管理する仕組みがないと網羅できない
    • プラグインのバージョンはどうするのか?常に最新?
  • プロジェクトの ESDoc 設定と package.json から動的にプラグインを決定する
    • プラグインのインストールはどうする?
    • ドキュメント生成とごにプラグインをインストールすると Hosting Service の負荷が大きい
    • CI 系サービスのように VM や Docker コンテナで対応するとしても負荷は大きい

といった問題が予想されるため難しそうだ。しかし要望があることだけは記録しておきたかったので issue 登録することにした。

別の方法として ESDoc 本体が esdoc-node 処理を取り込むという選択肢もある。ただ、いずれ Node が ES Modules へ正式に対応するとしたら CommonJS は過渡期の存在である。

  • そのために対応コストを割くのか?
  • ESDoc と名乗るツールとして ECMAScript とは直に関係しない CommonJS へ対応することは設計思想として望ましくないのでは?

という考えもあるだろう。どのような対応、または非対応のままになったとしても ESDoc 作者の意向を尊重したい。

ESDoc Hosting Service を利用しない場合、対象プロジェクトが GitHub で管理されているならそのリポジトリに対して GitHub Pages を使う手もある。ローカルの ESDoc + esdoc-node で出力したものを自前でアップロードするか、そういうタスクを npm-scripts に定義して CI サービス経由で生成から公開まで自動化するなどの方法が考えられる。

本記事を書いた直後に ESDoc 作者の @h13i32maru さんから見解が。

ESDoc Hosting Service 的にはセキュリティや負荷を考慮し、ESDoc 公式プラグインのみサポートしているとのこと。ただ Node の ES Modules 対応は相当に先となりそうなので、この長い過渡期に Node かつ脱 Babel したい開発者としてどうか?という悩ましい課題がある。

まとめ

一部、問題もあったが transpile 不要となったことで Node のバージョンだけを意識して開発すればよい。記述したコードはそのまま実行されることが保証される。Babel 由来の潜在的なトラブルを考えなくてよいため精神的に楽。

あと npm をユニット テストではなく普通のプログラムとして走らせて検証してみたい場合、いちいち transpile しなくて済むのも助かる。

検証はテストに定義すべきでは?という意見もあるだろうけどリポジトリの examples フォルダに npm として参照したときのサンプルを配置しているとき、その動作検証で npm publish する前の現行コードを試したくなったりする。その場合、transpile なしだとビルド系タスクを実行せず動かせてよい。

以上を踏まえ、他の npm についても気が向いたら脱 Babel してゆく予定。