さよならボイラープレート。s2sによる高速reduxアプリケーション構築

## まだアクションクリエイターを自分で書いているの？
reduxとflowtypeを使ってフロントエンドアプリケーションを構築していると、ボイラープレートが多く、面倒だと感じることがありませんか？
しかし、もはや、このAST時代の前には過去の悩みでしかありません。

![demo.gif (1072×586)](https://raw.githubusercontent.com/akameco/s2s-examples/master/media/demo.gif)

型を書く。それが全てです。
型を書いて、定数を書いて、アクションクリエイターを書いて、一つ変更したら全て変更して、もしくはなんらかのハックを行って型付けして、なんてものは過去のことです。
これからは、アクションクリエイターの作成に5秒以上時間をかけたら**怠惰**でありましょう。そして、これはs2sの1プラグインでしかありません。

プラグインを組み合わせると以下のようなこともできます。

![last.gif](https://qiita-image-store.s3.amazonaws.com/0/15319/70b98fdd-738b-ddd2-f8f5-9244557cd742.gif)


## s2s (Source to Source)
これを実現している仕組みを**Source to Source**(**s2s**)といいます。

**ソースコード**から**ソースコード**への**コーディングタイムコンパイル**です。

<img width="712" alt="スクリーンショット 2017-09-23 23.48.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/15319/61aba0e2-1aaa-43ef-be11-ef2c4a6105a9.png">

仕組み自体は非常に単純です。プロジェクトをs2sが監視し、条件にマッチしたファイルを引数にBabelを実行します。
Babelに代表されるトランスパイラとs2sの違いは、言葉の通り**出力がソースコードへ返ってくる**点しか違いません。

s2sは概念でありますから、先程のreduxの例のみならず、人が記述可能なものは全て自動で記述できます。例えば、コーディング時にASTに従ってテストコードを自動生成してもいいですし、flowの型定義を書きながらGraphQLのクエリを生成し続けるのもいいでしょう。何より、ボイラープレートだと思われるものは例外なく自動生成可能でしょう。ボイラープレートが忌み嫌われる時代は終わりました。自動生成されるボイラープレートこそが開発に速度とコードの明快さをもたらす福音足り得るでしょう。

また、s2sはエディタにロックインされません。AtomでもVSCodeでも好きなエディタを使うことが出来ます。ただ、実行するだけです。

## s2sの構成とConfigについて
s2sの実装はGitHubにあります。実は6ヶ月前からスクリプトとしてずっと使っていたんですが、無職ゆえ自分と知人以外使われてきませんでした。

[**akameco/s2s**<br>Source to Source](https://github.com/akameco/s2s)

さて、ライブラリとしてのs2sの機能は大きく2つあります。
1つはもう何度も登場しているs2sそのもの。
もう1つは**テンプレート展開**です。
ところでみなさん、Scaffoldやスニペットは好きですか？
ええ、もちろん大嫌いです。アタマが悪いので覚えれないんですよねコマンド。
テンプレート展開は、ファイルを生成する際にテンプレートで上書きします。特に手間がかからないわりに言葉以上に有効です。

[![https://gyazo.com/91e13497dfe9692a37d397d6db413826](https://i.gyazo.com/91e13497dfe9692a37d397d6db413826.gif)](https://gyazo.com/91e13497dfe9692a37d397d6db413826)

上記の例では、reducer.jsが生成された瞬間にテンプレートで上書きしています。また、reducer.jsが生成されたことによりs2sもトリガーし、**type/state.js**以下に**State**の型を追加し、ルートの**reducer.js**の**combineReducers**に追記します。これでStateの型とreducerがずれるなんてことはありませんね。もちろん、ファイルを削除すれば消えるので、そもそもルートのreducerやstateなどの型定義は開く必要も、意識する必要もないです。

機能の説明はこれで終わりです。

次に、設定ファイルの説明に入ります。
s2sはルートに配置された**s2s.config.js**を設定ファイルとして扱います。webpack.config.jsのようなものだと思ってください。
しかし設定する項目は非常に少ないです。

```js:s2s.config.js
const path = require('path')

const rootDir = path.resolve(process.cwd(), 'src')
const rootReducerPath = path.resolve(rootDir, 'reducer.js')


module.exports = {
  watch: './**/*.js',
  plugins: [
    {
      test: /actionTypes.js$/,
      plugin: ['s2s-action-types', { removePrefix: 'src/containers' }],
    },
    {
      test: /actionTypes.js$/,
      output: 'actions.js',
      plugin: ['s2s-action-creater'],
    },
    {
      test: /reducer.js/,
      input: rootReducerPath,
      output: rootReducerPath,
      plugin: [
        's2s-reducer-root',
        { input: 'src/containers/**/reducer.js', output: rootReducerPath },
      ],
    },
  ],
  templates: [
    { test: /reducer.js/, input: 'reducer.js' },
  ],
}
```

#### watch
監視するファイルです。globパターンで指定します。

#### plugins
プラグインの配列です。いくつかの引数を取ります。

- **test** トリガーとなるファイルを正規表現で指定します。他にオプションがなければそのままこのパスに出力されます。actionの型定義がこのパターンです。

- **plugin** バベルのプラグインを指定します。.babelrcの書き方と全く同じです。.babelrcと同様に引数を与えることも可能です。このプラグインにs2s特有の概念はありません。全ての**BabelPlugin**をそのまま使え、新し機能を追加したければ、単にBabelPluginを書くだけです。

- **output** トリガーと出力するパスが異なる場合に、このオプションを使います。相対パスを渡した場合、トリガーとなったファイルが起点の相対パスになります。root集約などは絶対パスで指定しています。

- **input** トリガーとなるファイルと異なるファイルを対象として変換を実行したい場合に指定します。reducerやStateのルート集約などに利用します。

#### templates
テンプレートの配列です。いくつかの引数を取ります。

- **test** トリガーなるパターンです。Pluginのオプションと同じです。
- **input** 配置したファイル名を指定します。デフォルトでは、ルートの**templates**以下に配置されたファイルを指定します

#### templatesDir
テンプレートの起点を変更する際に指定します。例えば、`config/templates`以下にテンプレートを配置したいときなどです。


見たとおり、設定する項目は少ないです。また、単に**Babel Plugin**を使うため一切のロックインはありません。

新しくBabel Pluginを書いてみたい場合、以下の記事が参考になります。

**Babel Pluginを作成するときに有効な情報まとめ**
https://qiita.com/akameco/items/af81882fabf2728f2567


## サンプルとreduxについて
はじめましょう。
以下のサンプルを*git clone*してください。

[**akameco/s2s:exmaple**](https://github.com/akameco/s2s)

```
$ git clone git@github.com:akameco/s2s.git
```

よくあるショッピングカートのサンプルがあります。create-react-appを使っています。

```
$ cd examples/shopping-cart
$ yarn
$ yarn start
```

s2sを起動しましょう。

```
$ yarn run s2s
```

![スクリーンショット 2017-09-24 00.30.36.png](https://qiita-image-store.s3.amazonaws.com/0/15319/dc1ad876-57c0-a8f4-7cd5-de5e20537f4a.png)

さて、実際もう説明することはありません。
好きなように変更して`s2s`の動きを確認してみてください。
例えば、App以下にActionTypes.jsを新規作成し、Addアクションを追加してみてください。

<a href="https://gyazo.com/70b365152ef53158f9720298d35db4bb"><img src="https://i.gyazo.com/70b365152ef53158f9720298d35db4bb.gif" alt="https://gyazo.com/70b365152ef53158f9720298d35db4bb" width="1078"/></a>

コマンドラインには以下のように出力されます。イベントのパスと出力先のパスが表示されます。MacならCmd+クリックで対象のファイルがエディタで開けるので便利かもしれません。

<img width="742" alt="スクリーンショット 2017-09-24 02.18.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/15319/bbe254af-8742-b3f7-69e7-31900c83f656.png">


さて、この変換を行っているのは、[**babel-plugin-s2s-action-creater**](https://github.com/akameco/s2s/tree/master/packages/babel-plugin-s2s-initial-state-creater)というプラグインです。s2sの責務は、変換の対象とプラグインを接続しイベントをトリガーするだけなので、変換の責務は全てBabelPluginにあります。このプラグインのreadmeを見てましょう。

![スクリーンショット 2017-09-24 02.24.43.png](https://qiita-image-store.s3.amazonaws.com/0/15319/6fd8b875-fe74-2ab0-0737-248546a3442c.png)

一つ一つ分解すると単純ですね。
このサンプルでは、6つのバベルプラグインを使っています。s2sはプラグイン含めて、モノレポで運用しているので、よかったら確認してみてください。

[**akameco/s2s-plugins**](https://github.com/akameco/s2s/tree/master/packages)

また、reducerのテストケースの自動生成も可能です。
詳しくは、以下のリンクを読んでください。

[**s2s: reduxにおけるreducerのテスト。あなたがテストを書く必要はないかも知れない**](https://qiita.com/akameco/items/66a2232df0e95e5bfe31)

![reducer-test.gif](https://qiita-image-store.s3.amazonaws.com/0/15319/03b48bdd-ba7e-d5aa-ecf8-02e3d6c0707f.gif)

### サンプルのreduxの構成について
話はかわりますが、この生成されるflowの型付け方法やreduxの構成は、個人的なベストプラクティスでもあります。基本的な構成はコンテナベースで、action、reducer含め全てをコンテナに閉じ、同一ディレクトリに**reducer.test.js**のような形式でテストを書きます。Actionはディレクトリ名のプレフィックスを付けます。これで完全に一意なアクションになるので、取り違えは起こりえません。またstring型ではなくstring literal型を使います。これについては以下の記事を読んでください。本来であれば書くのが面倒でありますが、s2sがある現在ボイラープレートは味方です。
**flowtypeのactionの型付け。String Literal型とString型について**
https://qiita.com/akameco/items/e7021e22da4c9e14463a

テンプレートより生成されるreducerは**T & $Shape<T>**型で型を付けます。詳しくは、以下の記事を確認してください。
**Flowtype+reduxにおけるreducerの正しい型付け**
https://qiita.com/akameco/items/fe7ba22c158a2593b077

また、reduxのミドルウェアはredux-thunkです。これだけ先に記事を書いておくのを忘れました。なぜredux-sagaを使うことを辞めたのかを含め、後でこの選択をした理由の記事を上げる予定です。

## 予想される質問とその回答

### TypeScriptのサポートは？
----
11/8 追記
デフォルトでサポートしました。設定なしで、pluginを指定すればtypescriptに対して動きます。

11/06 追記
Yeｓ. サポートしました 🎉
<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="ja" dir="ltr">s2sのTypeScript対応完了した。ts-handlerを渡すとTSに対してs2sが発動する <a href="https://t.co/giXFcYTFrb">pic.twitter.com/giXFcYTFrb</a></p>&mdash; あかめ@無職.js (@akameco) <a href="https://twitter.com/akameco/status/927225088658698240?ref_src=twsrc%5Etfw">November 5, 2017</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

が、まだプラグインがありません。
もし、TS用のプラグインを作成した方がいれば、教えて頂けると嬉しいです。
それと上記のツイートでは、`s2s-ts-handler`ですが、`s2s-handler-typescript`にリネームしました。

追記終わり

---


No. Just Use FlowType.


[Adopting Flow & TypeScript – @thejameskyle](http://thejameskyle.com/adopting-flow-and-typescript.html)


冗談はさておき、Babel6だとTypeScriptは解釈されません。TypeScriptユーザは、Babel7のリリースを待つことになります。


### Issueやプルリク
日本語だと嬉しいです。英語でもいいですが、Google翻訳アシストの謎い英文を返すマシーンとなります。

## まとめ
s2sについて、概念や使い方についてまとめました。
s2sがあなたのプロジェクトの力になることがあれば嬉しいです。
もし気にいってくだされば、スターを貰えると励みになります。

[**akameco/s2s**<br>Source to Source](https://github.com/akameco/s2s)

兎にも角にもASTの時代です。人間のやることや書くことがどんどん減っていけば最高ですね。
何かあれば、コメント欄またはツイッターにて議論しましょう。

<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="ja" dir="ltr">今この一瞬日本で一番高速にflow+reduxのアクションを書ける自信がある</p>&mdash; 無職.js (@akameco) <a href="https://twitter.com/akameco/status/910379500759801856">September 20, 2017</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>


<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="ja" dir="ltr">僕が辿りついた最高のflowtype+reduxです <a href="https://t.co/FISlpW9OpE">https://t.co/FISlpW9OpE</a> <a href="https://twitter.com/hashtag/redux?src=hash">#redux</a> <a href="https://t.co/YXujSJawsC">pic.twitter.com/YXujSJawsC</a></p>&mdash; 無職.js (@akameco) <a href="https://twitter.com/akameco/status/911566655641182208">September 23, 2017</a></blockquote>

<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="ja" dir="ltr">ほとんど理想のreduxになってきた。もう少し自動生成できる部分があるけど <a href="https://t.co/hedRGsTkIM">pic.twitter.com/hedRGsTkIM</a></p>&mdash; あかめ@無職.js (@akameco) <a href="https://twitter.com/akameco/status/916479997744889856?ref_src=twsrc%5Etfw">October 7, 2017</a></blockquote>



## お知らせ HTML5カンファレンス
本日9/24にあるHTML5カンファレンスでデモ展示をする予定です。一応、一日中いる予定ですので、気軽に声をかけてくださると嬉しいです。自分のデモの内容は一番上に貼ってあるGIFの無限リピートとこの記事へのリンクを張っておくだけなので面白みはないですが、他のメンバーが何か面白い展示をしてくれるはずですのでよろしくお願いします。


10/2 追記 デモに来てくれたみなさん。本当にありがとうございました。非常に勉強になりました。出来る限りフォローを返したつもりですが、もしフォロー返されてないなどありましたら、一声かけてくれると嬉しいです。

<a href="https://github.com/akameco/s2s">
<img width="388" alt="スクリーンショット 2017-09-24 00.05.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/15319/5ab8a503-45e9-250f-c286-5adfc5cee72c.png">
</a>

## 参考
[**akameco/s2s** - Source to Source](https://github.com/akameco/s2s)


**flowtypeのactionの型付け。String Literal型とString型について**
https://qiita.com/akameco/items/e7021e22da4c9e14463a

**Flowtype+reduxにおけるreducerの正しい型付け**
https://qiita.com/akameco/items/fe7ba22c158a2593b077

**s2s: reduxにおけるreducerのテスト。あなたがテストを書く必要はないかも知れない**
[https://qiita.com/akameco/items/66a2232df0e95e5bfe31](https://qiita.com/akameco/items/66a2232df0e95e5bfe31)