<commit_before>---
title: "ライブラリをES6で書いて公開する所から始めよう"
author: azu
layout: post
date : 2015-01-09T15:16
category: JavaScript
tags:
    - ES6
    - JavaScript
    - Node.js
    - library
issue: https://github.com/efcl/efcl.github.io/issues/54

---

この記事はECMAScript 6の事始めとして、ライブラリをES6で書いて公開するというところから始めるのがいいのではという内容です。

**2015-05-09: 追記**

- 6to5 は Babel へリネームされました。
- [azu/espower-babel](https://github.com/azu/espower-babel "azu/espower-babel")はテストコード以外もBabelで変換するようになりました
	- テストから`import hoge from "../lib/hoge"`ではなく`import hoge from "../src/hoge"`を参照できるようになりました

## 現在のES6 -> ES5

ECMAScript 6は現在策定の最終段階で、メジャーなブラウザの最新版は[多くの機能](http://kangax.github.io/compat-table/es6/ "ECMAScript 6 compatibility table")を既に実装しています。

- [ECMAScript 6 compatibility table](http://kangax.github.io/compat-table/es6/ "ECMAScript 6 compatibility table")

しかし、現実には最新版以外の実行環境でも動くように書くと思うので、ES6のコードをES5に変換して利用すると思います。

今すぐ、ES6でコードを書いてES5に変換するツールは色々あります。

- [Babel](http://babeljs.io/ "Babel")
- [Traceur](https://github.com/google/traceur-compiler "Traceur")
- TypeScript

少し注意したほうがいいのは、上記のツールは構文(`class`や`module`など)をサポートという意味が多いです(そうじゃないとシンタックスエラーになるので)。

一方、`Array.from`や`Promise`といったAPIは別のpolyfill(またはruntimeなどと言わわれるもの)を使いサポートしているという違いがある事には少し注意しておいたほうがいいです。

なので、こういった変換ツールを使う際は構文のサポートを求める感じで使うのがいいと思います。逆にpolyfillで対処できる機能は別にES6で書かなくても利用することができます。

polyfillについては以下を見ていくと面白いと思います。

- [Polyfill service](https://cdn.polyfill.io/v1/docs/ "Polyfill service")

簡単にまとめると、ES6の構文を使うことでコードもシンプルになりますが、それを公開するためには変換ツールを使わないといけないというのが現状だと思います(一種のAltJSみたいなもの)

## ライブラリをES6で書いてnpmで公開してみよう

じゃあ手始めにどういう時にES6で書くのがいいかというと、ライブラリとして公開するようなコードをES6で書くのがいいと思います。

具体的な例と共にES6で書いてnpmでライブラリを公開するまでを見て行きたいと思います。

扱う題材をシンプルにするためにNode.jsのライブラリとして考えます。

以下の[azu/textlint-rule-helper](https://github.com/azu/textlint-rule-helper "azu/textlint-rule-helper")というものを例にして見ていきます。(このライブラリは[textlint](http://efcl.info/2014/12/30/textlint/ "textlint")というツールのルールを書くのを補助するライブラリですが今回はどうでもいいです)

- [azu/textlint-rule-helper](https://github.com/azu/textlint-rule-helper "azu/textlint-rule-helper")

上記のリポジトリをみてもらうと、ES5のコードやgulp等の設定ファイルも存在しない事がわかると思います。

[![file tree](http://efcl.info/wp-content/uploads/2015/01/09-1420785429.png)](https://github.com/azu/textlint-rule-helper)

## ライブラリの構成

ライブラリの構成は以下のようになっています。

- `src/`: ES6で書かれたコード
- `lib/`: babelで`src/`以下のコードを変換したES5のコード
    - `lib/`ディレクトリは`.gitignore`で無視して、リポジトリには含めない
    - 変換後のソースはGitで管理しない(そうした方がPull Request時に迷わない)
    - 逆に`package.json`の`files`フィールドでは`"files" : ["lib"]`のみとする
    - npmが軽量になるし、npmで取ってきたファイルは`lib/`以下のES5のコードのみにできる
    - [細かすぎて伝わらない package.json 小ネタ三選 - t-wadaのブログ](http://t-wada.hatenablog.jp/entry/nodejs-package-json-tips "細かすぎて伝わらない package.json 小ネタ三選 - t-wadaのブログ")
- `test/`: ES6 + mocha + power-assertでテストを書く
    - [azu/espower-babel](https://github.com/azu/espower-babel "azu/espower-babel")を使うことで、`mocha --compilers js:espower-babel/guess`と指定するだけでテストもES6で書ける
- `README.md`: ドキュメント
    - ライブラリにはドキュメントは必要。いくら良いコードでもドキュメントがないならゴミ
    - JSDocでドキュメントを書き、[jsdoc-to-markdown](https://github.com/75lb/jsdoc-to-markdown "jsdoc-to-markdown")でREADMEに追加出来るように自動化する
    - [jsdoc-to-markdown](https://github.com/75lb/jsdoc-to-markdown "jsdoc-to-markdown")を使うことで、`template/`のテンプレートを使ってREADMEを生成できる
    - JSDocを変更する度にREADMEを手動で変更する必要がなくなる

ざっくり見ると[azu/textlint-rule-helper](https://github.com/azu/textlint-rule-helper "azu/textlint-rule-helper")は上記のような構成で動いています。

![tree](http://efcl.info/wp-content/uploads/2015/01/textlint-rule-helper.png)

(OmniGraffleの**アイコン**にディレクトリをD&Dするとこういう図ができる - [OmniGraffle Folder Trick — MacSparky](http://macsparky.com/blog/2010/4/21/omnigraffle-folder-trick.html "OmniGraffle Folder Trick — MacSparky"))

### `src/`

ES6で書いたコードを置く場所です。AltJSでも`src/`にAltJSのソースを置くのと同じような話です。

### `lib/`

JavaScriptのコードを置く場所です。Node.jsでは一般的に`lib/`以下にJSのソースをおいてると思います。

ここに置かれるJSは`src/`のものをES5に変換したものが配置されます。

ES6 -> ES5の変換には[Babel](http://babeljs.io/ "Babel")を使っています。`devDependencies`で入れておいて、npm run-scriptで実行するようにすればglobalに入れる必要はありません。

- [npm で依存もタスクも一元化する - Qiita](http://qiita.com/Jxck_/items/efaff21b977ddc782971 "npm で依存もタスクも一元化する - Qiita")

```
  ...
  "scripts": {
    "build": "babel src --out-dir lib  --source-maps-inline",
    "watch": "babel src --out-dir lib --watch --source-maps-inline",
    "test": "npm run build && mocha test/*.js"
  },
  "devDependencies": {
    "6to5": "^2.7.4",
    "mocha": "^2.1.0",
    "espower-6to5": "^1.0.3",
    "power-assert": "^0.10.0",
  }
  ...
```


しかし、変換したJSにPull Requestとか送られても困るのでGitリポジトリには`lib/`は含めないようにします。

`.gitignore`に以下のように書いて、`lib`ディレクトリを無視させます。

```
/lib
```

逆に、`npm publish`で公開するときには、ES5に変換されたコードだけを公開したいです。
そういう時は、`package.json`の`"files"`フィールドに、ホワイトリストで含めたいファイル or ディレクトリを指定出来ます。

`lib/`だけを含めたいので以下のように書くことが出来ます。

```json
{
  "name": "textlint-rule-helper",
  "description": "Helper for textlint rule.",
  "version": "1.1.2",
  "homepage": "https://github.com/azu/textlint-rule-helper/",
  "repository": {
    "type": "git",<commit_msg>update dependency<commit_after>---
title: "ライブラリをES6で書いて公開する所から始めよう"
author: azu
layout: post
date : 2015-01-09T15:16
category: JavaScript
tags:
    - ES6
    - JavaScript
    - Node.js
    - library
issue: https://github.com/efcl/efcl.github.io/issues/54

---

この記事はECMAScript 6の事始めとして、ライブラリをES6で書いて公開するというところから始めるのがいいのではという内容です。

**2015-05-09: 追記**

- 6to5 は Babel へリネームされました。
- [azu/espower-babel](https://github.com/azu/espower-babel "azu/espower-babel")はテストコード以外もBabelで変換するようになりました
	- テストから`import hoge from "../lib/hoge"`ではなく`import hoge from "../src/hoge"`を参照できるようになりました

## 現在のES6 -> ES5

ECMAScript 6は現在策定の最終段階で、メジャーなブラウザの最新版は[多くの機能](http://kangax.github.io/compat-table/es6/ "ECMAScript 6 compatibility table")を既に実装しています。

- [ECMAScript 6 compatibility table](http://kangax.github.io/compat-table/es6/ "ECMAScript 6 compatibility table")

しかし、現実には最新版以外の実行環境でも動くように書くと思うので、ES6のコードをES5に変換して利用すると思います。

今すぐ、ES6でコードを書いてES5に変換するツールは色々あります。

- [Babel](http://babeljs.io/ "Babel")
- [Traceur](https://github.com/google/traceur-compiler "Traceur")
- TypeScript

少し注意したほうがいいのは、上記のツールは構文(`class`や`module`など)をサポートという意味が多いです(そうじゃないとシンタックスエラーになるので)。

一方、`Array.from`や`Promise`といったAPIは別のpolyfill(またはruntimeなどと言わわれるもの)を使いサポートしているという違いがある事には少し注意しておいたほうがいいです。

なので、こういった変換ツールを使う際は構文のサポートを求める感じで使うのがいいと思います。逆にpolyfillで対処できる機能は別にES6で書かなくても利用することができます。

polyfillについては以下を見ていくと面白いと思います。

- [Polyfill service](https://cdn.polyfill.io/v1/docs/ "Polyfill service")

簡単にまとめると、ES6の構文を使うことでコードもシンプルになりますが、それを公開するためには変換ツールを使わないといけないというのが現状だと思います(一種のAltJSみたいなもの)

## ライブラリをES6で書いてnpmで公開してみよう

じゃあ手始めにどういう時にES6で書くのがいいかというと、ライブラリとして公開するようなコードをES6で書くのがいいと思います。

具体的な例と共にES6で書いてnpmでライブラリを公開するまでを見て行きたいと思います。

扱う題材をシンプルにするためにNode.jsのライブラリとして考えます。

以下の[azu/textlint-rule-helper](https://github.com/azu/textlint-rule-helper "azu/textlint-rule-helper")というものを例にして見ていきます。(このライブラリは[textlint](http://efcl.info/2014/12/30/textlint/ "textlint")というツールのルールを書くのを補助するライブラリですが今回はどうでもいいです)

- [azu/textlint-rule-helper](https://github.com/azu/textlint-rule-helper "azu/textlint-rule-helper")

上記のリポジトリをみてもらうと、ES5のコードやgulp等の設定ファイルも存在しない事がわかると思います。

[![file tree](http://efcl.info/wp-content/uploads/2015/01/09-1420785429.png)](https://github.com/azu/textlint-rule-helper)

## ライブラリの構成

ライブラリの構成は以下のようになっています。

- `src/`: ES6で書かれたコード
- `lib/`: babelで`src/`以下のコードを変換したES5のコード
    - `lib/`ディレクトリは`.gitignore`で無視して、リポジトリには含めない
    - 変換後のソースはGitで管理しない(そうした方がPull Request時に迷わない)
    - 逆に`package.json`の`files`フィールドでは`"files" : ["lib"]`のみとする
    - npmが軽量になるし、npmで取ってきたファイルは`lib/`以下のES5のコードのみにできる
    - [細かすぎて伝わらない package.json 小ネタ三選 - t-wadaのブログ](http://t-wada.hatenablog.jp/entry/nodejs-package-json-tips "細かすぎて伝わらない package.json 小ネタ三選 - t-wadaのブログ")
- `test/`: ES6 + mocha + power-assertでテストを書く
    - [azu/espower-babel](https://github.com/azu/espower-babel "azu/espower-babel")を使うことで、`mocha --compilers js:espower-babel/guess`と指定するだけでテストもES6で書ける
- `README.md`: ドキュメント
    - ライブラリにはドキュメントは必要。いくら良いコードでもドキュメントがないならゴミ
    - JSDocでドキュメントを書き、[jsdoc-to-markdown](https://github.com/75lb/jsdoc-to-markdown "jsdoc-to-markdown")でREADMEに追加出来るように自動化する
    - [jsdoc-to-markdown](https://github.com/75lb/jsdoc-to-markdown "jsdoc-to-markdown")を使うことで、`template/`のテンプレートを使ってREADMEを生成できる
    - JSDocを変更する度にREADMEを手動で変更する必要がなくなる

ざっくり見ると[azu/textlint-rule-helper](https://github.com/azu/textlint-rule-helper "azu/textlint-rule-helper")は上記のような構成で動いています。

![tree](http://efcl.info/wp-content/uploads/2015/01/textlint-rule-helper.png)

(OmniGraffleの**アイコン**にディレクトリをD&Dするとこういう図ができる - [OmniGraffle Folder Trick — MacSparky](http://macsparky.com/blog/2010/4/21/omnigraffle-folder-trick.html "OmniGraffle Folder Trick — MacSparky"))

### `src/`

ES6で書いたコードを置く場所です。AltJSでも`src/`にAltJSのソースを置くのと同じような話です。

### `lib/`

JavaScriptのコードを置く場所です。Node.jsでは一般的に`lib/`以下にJSのソースをおいてると思います。

ここに置かれるJSは`src/`のものをES5に変換したものが配置されます。

ES6 -> ES5の変換には[Babel](http://babeljs.io/ "Babel")を使っています。`devDependencies`で入れておいて、npm run-scriptで実行するようにすればglobalに入れる必要はありません。

- [npm で依存もタスクも一元化する - Qiita](http://qiita.com/Jxck_/items/efaff21b977ddc782971 "npm で依存もタスクも一元化する - Qiita")

```
  ...
  "scripts": {
    "build": "babel src --out-dir lib  --source-maps-inline",
    "watch": "babel src --out-dir lib --watch --source-maps-inline",
    "test": "npm run build && mocha test/*.js"
  },
  "devDependencies": {
    "babel": "^5.2.17",
    "espower-babel": "^3.0.0",
    "power-assert": "^0.11.0"
  }
  ...
```


しかし、変換したJSにPull Requestとか送られても困るのでGitリポジトリには`lib/`は含めないようにします。

`.gitignore`に以下のように書いて、`lib`ディレクトリを無視させます。

```
/lib
```

逆に、`npm publish`で公開するときには、ES5に変換されたコードだけを公開したいです。
そういう時は、`package.json`の`"files"`フィールドに、ホワイトリストで含めたいファイル or ディレクトリを指定出来ます。

`lib/`だけを含めたいので以下のように書くことが出来ます。

```json
{
  "name": "textlint-rule-helper",
  "description": "Helper for textlint rule.",
  "version": "1.1.2",
  "homepage": "https://github.com/azu/textlint-rule-helper/",
  "repository": {
    "type": "git",
{'fraction_non_alphanumeric': 0.15440952090326518, 'fraction_numerical': 0.026243515410436374, 'mean_word_length': 5.68843537414966, 'pattern_counts': {'":': 29, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 24, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6941273', 'n_tokens_mistral': 6152, 'n_tokens_neox': 5564, 'n_words': 394}