まだアクションクリエイターを自分で書いているの?
reduxとflowtypeを使ってフロントエンドアプリケーションを構築していると、ボイラープレートが多く、面倒だと感じることがありませんか?
しかし、もはや、このAST時代の前には過去の悩みでしかありません。
型を書く。それが全てです。
型を書いて、定数を書いて、アクションクリエイターを書いて、一つ変更したら全て変更して、もしくはなんらかのハックを行って型付けして、なんてものは過去のことです。
これからは、アクションクリエイターの作成に5秒以上時間をかけたら怠惰でありましょう。そして、これはs2sの1プラグインでしかありません。
プラグインを組み合わせると以下のようなこともできます。
s2s (Source to Source)
これを実現している仕組みをSource to Source(s2s)といいます。
ソースコードからソースコードへのコーディングタイムコンパイルです。
仕組み自体は非常に単純です。プロジェクトをs2sが監視し、条件にマッチしたファイルを引数にBabelを実行します。
Babelに代表されるトランスパイラとs2sの違いは、言葉の通り出力がソースコードへ返ってくる点しか違いません。
s2sは概念でありますから、先程のreduxの例のみならず、人が記述可能なものは全て自動で記述できます。例えば、コーディング時にASTに従ってテストコードを自動生成してもいいですし、flowの型定義を書きながらGraphQLのクエリを生成し続けるのもいいでしょう。何より、ボイラープレートだと思われるものは例外なく自動生成可能でしょう。ボイラープレートが忌み嫌われる時代は終わりました。自動生成されるボイラープレートこそが開発に速度とコードの明快さをもたらす福音足り得るでしょう。
また、s2sはエディタにロックインされません。AtomでもVSCodeでも好きなエディタを使うことが出来ます。ただ、実行するだけです。
s2sの構成とConfigについて
s2sの実装はGitHubにあります。実は6ヶ月前からスクリプトとしてずっと使っていたんですが、無職ゆえ自分と知人以外使われてきませんでした。
さて、ライブラリとしてのs2sの機能は大きく2つあります。
1つはもう何度も登場しているs2sそのもの。
もう1つはテンプレート展開です。
ところでみなさん、Scaffoldやスニペットは好きですか?
ええ、もちろん大嫌いです。アタマが悪いので覚えれないんですよねコマンド。
テンプレート展開は、ファイルを生成する際にテンプレートで上書きします。特に手間がかからないわりに言葉以上に有効です。
上記の例では、reducer.jsが生成された瞬間にテンプレートで上書きしています。また、reducer.jsが生成されたことによりs2sもトリガーし、type/state.js以下にStateの型を追加し、ルートのreducer.jsのcombineReducersに追記します。これでStateの型とreducerがずれるなんてことはありませんね。もちろん、ファイルを削除すれば消えるので、そもそもルートのreducerやstateなどの型定義は開く必要も、意識する必要もないです。
機能の説明はこれで終わりです。
次に、設定ファイルの説明に入ります。
s2sはルートに配置されたs2s.config.jsを設定ファイルとして扱います。webpack.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してください。
$ git clone git@github.com:akameco/s2s.git
よくあるショッピングカートのサンプルがあります。create-react-appを使っています。
$ cd examples/shopping-cart
$ yarn
$ yarn start
s2sを起動しましょう。
$ yarn run s2s
さて、実際もう説明することはありません。
好きなように変更してs2s
の動きを確認してみてください。
例えば、App以下にActionTypes.jsを新規作成し、Addアクションを追加してみてください。
コマンドラインには以下のように出力されます。イベントのパスと出力先のパスが表示されます。MacならCmd+クリックで対象のファイルがエディタで開けるので便利かもしれません。
さて、この変換を行っているのは、babel-plugin-s2s-action-createrというプラグインです。s2sの責務は、変換の対象とプラグインを接続しイベントをトリガーするだけなので、変換の責務は全てBabelPluginにあります。このプラグインのreadmeを見てましょう。
一つ一つ分解すると単純ですね。
このサンプルでは、6つのバベルプラグインを使っています。s2sはプラグイン含めて、モノレポで運用しているので、よかったら確認してみてください。
また、reducerのテストケースの自動生成も可能です。
詳しくは、以下のリンクを読んでください。
s2s: reduxにおけるreducerのテスト。あなたがテストを書く必要はないかも知れない
サンプルの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**型で型を付けます。詳しくは、以下の記事を確認してください。
Flowtype+reduxにおけるreducerの正しい型付け
https://qiita.com/akameco/items/fe7ba22c158a2593b077
また、reduxのミドルウェアはredux-thunkです。これだけ先に記事を書いておくのを忘れました。なぜredux-sagaを使うことを辞めたのかを含め、後でこの選択をした理由の記事を上げる予定です。
予想される質問とその回答
TypeScriptのサポートは?
11/8 追記
デフォルトでサポートしました。設定なしで、pluginを指定すればtypescriptに対して動きます。
11/06 追記
Yes. サポートしました 🎉
s2sのTypeScript対応完了した。ts-handlerを渡すとTSに対してs2sが発動する pic.twitter.com/giXFcYTFrb
— あかめ@無職.js (@akameco) November 5, 2017
が、まだプラグインがありません。
もし、TS用のプラグインを作成した方がいれば、教えて頂けると嬉しいです。
それと上記のツイートでは、s2s-ts-handler
ですが、s2s-handler-typescript
にリネームしました。
追記終わり
No. Just Use FlowType.
Adopting Flow & TypeScript – @thejameskyle
冗談はさておき、Babel6だとTypeScriptは解釈されません。TypeScriptユーザは、Babel7のリリースを待つことになります。
Issueやプルリク
日本語だと嬉しいです。英語でもいいですが、Google翻訳アシストの謎い英文を返すマシーンとなります。
まとめ
s2sについて、概念や使い方についてまとめました。
s2sがあなたのプロジェクトの力になることがあれば嬉しいです。
もし気にいってくだされば、スターを貰えると励みになります。
兎にも角にもASTの時代です。人間のやることや書くことがどんどん減っていけば最高ですね。
何かあれば、コメント欄またはツイッターにて議論しましょう。
今この一瞬日本で一番高速にflow+reduxのアクションを書ける自信がある
— 無職.js (@akameco) September 20, 2017
僕が辿りついた最高のflowtype+reduxです https://t.co/FISlpW9OpE #redux pic.twitter.com/YXujSJawsC
— 無職.js (@akameco) September 23, 2017
ほとんど理想のreduxになってきた。もう少し自動生成できる部分があるけど pic.twitter.com/hedRGsTkIM
— あかめ@無職.js (@akameco) October 7, 2017
お知らせ HTML5カンファレンス
本日9/24にあるHTML5カンファレンスでデモ展示をする予定です。一応、一日中いる予定ですので、気軽に声をかけてくださると嬉しいです。自分のデモの内容は一番上に貼ってあるGIFの無限リピートとこの記事へのリンクを張っておくだけなので面白みはないですが、他のメンバーが何か面白い展示をしてくれるはずですのでよろしくお願いします。
10/2 追記 デモに来てくれたみなさん。本当にありがとうございました。非常に勉強になりました。出来る限りフォローを返したつもりですが、もしフォロー返されてないなどありましたら、一声かけてくれると嬉しいです。
参考
akameco/s2s - Source to Source
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