ES6で書くRiot - riot.config.js編

[Advent Calendar](http://qiita.com/advent-calendar/2016/riotjs)の3日目です。近年、JavaScriptでプログラムを書くのに避けて通れない「プリコンパイル」について、Riotの文脈で3本ほど書いてみたいと思います。

- [ES6で書くRiot - riot.config.js編](http://qiita.com/cognitom/items/200a96b86eafc463ed24) - ([サンプルコード](https://github.com/riot/examples/tree/gh-pages/riot-config))
- [ES6で書くRiot - Rollup編](http://qiita.com/cognitom/items/c20c22614560627062cb) - ([サンプルコード](https://github.com/riot/examples/tree/gh-pages/rollup))
- [ES6で書くRiot - Felt編](http://qiita.com/cognitom/items/fedcfe08bf5527e43bcb) - ([サンプルコード](https://github.com/cognitom/felt-example-riot/))

## はじめてのriotコマンド

Riotでは、次のような「タグ(tag)」ファイルを書いて、コンポーネントを作成します。

```html:my-tag.tag
<my-tag>
  <p>{ message }</p>
  <script>
    this.message = 'Hello Riot!'
  </script>
  <style>
    :scope { display: block; color: #f04 }
  </style>
</my-tag>
```

デスクトップに`hello-riot`というフォルダを作り、そこに`my-tag.tag`というファイル名で保存しておいてください。残念ながら、これをそのままブラウザで開くことはできませんが、心配しないで! 実行できる形に変換するための公式コマンドがあります。それが本稿の主眼である`riot`です。

### Node.jsのインストール

もし、まだNode.jsの準備ができていなければ、[こちらからダウンロード](https://nodejs.org/ja/)して、環境を整えてください。

> **メモ**: 執筆時現在で、`v6.9.1`または`v7.2.0`が利用可能で、どちらを使っても構いません。ただ、偶数バージョンのNodeだけが長期サポートの対象になるので、今の時点では6系が無難です。

さあ「黒い画面」を開きましょう。Macであれば「アプリケーション > ユーティリティ > ターミナル.app」にあります。次のようにコマンド`node -v`と打ってEnterキーを押すと、バージョンが表示されます。

> **メモ**: 以下、入力すべきコマンドの前には`$ `をつけますが、`$ `を打つ必要はありません。

```bash
$ node -v
v6.9.1
```

ここで、もし`v0.12.0`などのように表示された場合、インストールされているNodeのバージョンが古すぎます。Node.jsのインストールからやり直しましょう。以下も参考まで。

- `v0.11.x` `v0.12.x`: 古すぎます
- `v1`〜`v3`: io.jsはNodeに統合されました。`v4`以降に上げましょう
- `v4`: OK (執筆時点で`v4.6.2`が最新です)
- `v5`: もうメンテナンスされていません。`v6`に上げましょう
- `v6`: OK (執筆時点で`v6.9.1`が最新です)
- `v7`: OK

### riotコマンドのインストール

次は、`riot`コマンドのインストールです。Node.jsを入れた時点で、`npm`というコマンドが利用可能になっていますので、これを使ってインストールします。

```bash
$ npm install -g riot
```

> **メモ**: npmのサーバにアクセスするため、`npm`コマンドの多くはネット接続を前提にしています。オフラインだと使えないので注意してください。

数十秒から1分ほどで、ダウンロードとインストールが完了すると思います。

### タグファイルをコンパイル

それでは、早速、先ほどのファイルをコンパイルしてみましょう。先ほどのファイルはデスクトップに保存したので、まずはそこに移動します。

```bash
$ cd ~/Desktop/hello-riot
```

次に`riot`コマンド:

```bash
$ riot my-tag.tag 
my-tag.tag -> my-tag.js
```

`my-tag.js`というファイルが、生成されます。下記、見やすいように改行位置を調整していますが、これに近いものが生成されたはずです。

```javascript:my-tag.js
riot.tag2('my-tag',
  '<p>{ message }</p>',
  'my-tag a,[data-is="my-tag"]{ display: block; color: #f04 }',
  '',
  function(opts) { this.message = 'Hello Riot!'; }
);
```

## HTMLに組み込む

JavaScriptのファイルだけでは、ブラウザに表示できないので、起点となるHTMLファイルを作成します。次の内容を`index.html`として保存してください。

```html:index.html
<!doctype html>
<html>
  <head>
    <title>Hello Riot!</title>
  </head>
  <body>
    <my-tag></my-tag>
    <script src="https://cdn.jsdelivr.net/riot/3.0/riot.min.js"></script>
    <script src="my-tag.js"></script>
    <script> riot.mount('*') </script>
  </body>
</html>
```

いくつかポイントがあります。

1. `<my-tag></my-tag>`は、タグを「マウント」するための一種のプレースホルダです。
2. Riotのライブラリを[CDN](https://cdn.jsdelivr.net/riot/3.0/riot.min.js)から読み込んでいます。
3. 先ほどコンパイルした`my-tag.js`も`<script>`タグで読み込んでいます。
4. `riot.mount('*')`のところで、実際にタグをマウントしています。


> **メモ**: 「マウント」というのはRiotの用語で、指定部分が実際の「タグ」で置き換えることを言います。

この`index.html`をブラウザで開いてみましょう。「Hello Riot!」と表示されていれば成功です。

## 次世代標準で書く

JavaScriptは、ここ数年大きく変化しています。2015年にES6(ES2015)が勧告され、ブラウザ側の対応も進んでいます。ただ、モバイルまで含めると、足並みが揃っていないのも事実で、ES6の文法で書いてしまうと実行できない環境が、少なからずあります。

一方で、ES7(ES2017)の足音も近づいており、ブラウザの完全対応を待ってから`ES6`に移行すれば良いという問題ではありません。Web上に公開されているコードも、多くはES6(かES7)を前提に書いているため、古い文法だけを使い続けることはもう難しくなってしまいました。

### babel とか bublé とか

未対応のブラウザと、次世代標準の間のギャップを埋めるのが「トランスパイラ」と呼ばれるツールです。これらは、ES6やES7で書かれたJavaScriptのコードを、ほぼ全てのブラウザで使えるES5に変換してくれます。

[babel](https://babeljs.io/)が有名ですが、非常に重く、設定が若干面倒なのが玉に瑕です。次項以降ではbabelの代わりに、設定が簡単でしかも高速な[bublé](https://buble.surge.sh/)を使おうと思います。

### 試しに書いてみる

さきほどの`my-tag.tag`を少し書き換えます。ES6で導入された機能はいろいろありますが、ここでは定数を宣言する`const`と、文字列の中に`${変数}`を埋め込める機能を試してみます。

```html:my-tag.tag
<my-tag>
  <p>{ message }</p>
  <script>
    const name = 'Riot and Bublé'
    this.message = `Hello ${name}!`
  </script>
  <style>
    :scope { display: block; color: #f04 }
  </style>
</my-tag>
```

書き換えたら保存を忘れずに。

### bubléをインストール

下準備として、次のコマンドを実行します。いろいろ聞かれますが、毎回Enterキーを押すだけで今のところはOKです。全て答え終わると、`package.json`というファイルが作成されます。

```bash
$ npm init
...
{
  "name": "hello-riot",
  "version": "1.0.0",
  "description": "",
  "main": "my-tag.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}
```

次に`buble`をインストールします。

```bash
$ npm install -D buble
```

開発用モジュールとして利用したいので、`-D`フラグをつけました。その詳しい意味について、ここでは省略しますが、次の点だけ押さえておけばOKです。

- `-g`をつけた場合と異なり「ローカル」つまり、作業中のフォルダ内にインストールされる。(`node_modules`というフォルダが新しく出来ているはずです)
- インストール情報は、`package.json`に追記されます。

### riot.config.js

`riot`コマンドでは、`--config`オプションをつけて[設定ファイルを読み込む](http://riotjs.com/guide/compiler/#es6-config-file)ことが可能です。`buble`についても、この設定ファイルを介して呼び出します。

次の内容を、`riot.config.js`というファイル名で保存してください。

```javascript:riot.config.js
import buble from 'buble' // bubleを読み込む -- (1)

export default {
  // スクリプトのタイプとして`buble`を指定 -- (2)
  type: 'buble',
  parsers: {
    // パーサの種類として`buble`を追加 -- (3)
    js: { buble: js => buble.transform(js) }
  }
}
```

> **メモ**: (2)と(3)の部分で「タイプ」として「buble」を指定しましたが、任意の別の文字列でも構いません。

それでは、あらためて`riot`コマンドを実行します。

```bash
$ riot my-tag.tag --config riot.config.js
```

すると、スクリプト部分が`buble`によって処理され、ES5に書き換えられました。

```javascript:my-tag.js
riot.tag2('my-tag',
  '<p>{message}</p>',
  'my-tag,[data-is="my-tag"]{ display: block; color: #f04 }',
  '',
  function(opts) {
    var name = 'Riot and Bublé'
    this.message = "Hello " + name + "!"
  }
);
```

- 変換前: ```this.message = `Hello ${name}!` ```
- 変換後: `this.message = "Hello " + name + "!"`

## まとめ

ここで紹介したコードものとほぼ同じものを、[公式のExampleリポジトリ](https://github.com/riot/examples/tree/gh-pages/riot-config)にも置きました。そちらもご参考まで。

Riotで、タグファイルのコンパイルすることからはじめて、`buble`を使ってES6→ES5変換をするまでを見てきました。RiotでES6を使う方法はいくつかあるのですが、ここでのやり方を知っていれば他への応用もやりやすくなるはずです。

一方で、ES6の特徴である`import`文を使って、タグ内でモジュールを読み込む方法については触れませんでした。それをするためには、実は`buble`などの「トランスパイラ」だけでは足りず別途「バンドラ」が必要になります。その点については、次回「Rollup編」で説明したいと思います。

## APPENDIX: CSSも次世代標準に対応する

CSSについては、[cssnext](http://cssnext.io/)というモジュールを使うと、現行のブラウザに対応したCSSに変換してくれます。その場合の`riot.config.js`は次のようになります。

```javascript:riot.config.js
import buble from 'buble'
import postcss from 'postcss'
import cssnext from 'postcss-cssnext'

export default {
  type: 'buble',
  style: 'cssnext',
  parsers: {
    js: { buble: js => buble.transform(js) },
    css: { cssnext: (tagName, css) => {
      // ちょっとだけハックして、:scopeを:rootに置き換えてPostCSSに渡す
      // タグの中でCSS変数を使いやすくするため
      css = css.replace(/:scope/g, ':root')
      css = postcss([cssnext]).process(css).css
      css = css.replace(/:root/g, ':scope')
      return css
    }}
  }
}
```

タグの中で、こんなふうに次世代標準のCSSを使うことができます。

```html
<my-tag>
  ..略..
  <style>
    :scope {
      --riot-color: #f04;
    }
    h1 {
      color: var(--riot-color);
      border-bottom: 1px solid var(--riot-color);
    }
  </style>
</my-tag>
```

`riot`コマンドを実行する前に、次のコマンドで必要なモジュールをインストールしておきましょう。

```bash
$ npm install -D postcss postcss-cssnext
```

先ほどと同様に、次のコマンドでタグファイルをコンパイルできます。

```bash
$ riot my-tag.tag --config riot.config.js
```