これまで require
をフックしてしまう babel-register
はなんとなく邪道ではと思って使っていなかったのですが、最近はよく flowtype を利用するようになり、いっぽうプロジェクト内の小さなツールを node で書くことが増えたこともあって、突然 register しましょうかという気持ちになりました。
いちいち型定義情報削除するために babel 通すのは面倒だし、コメント記法や別ファイル定義も flowtype の手軽さをスポイルしてしまうと感じていて、それであれば都度ビルド走ってもいいかなという。ただ、 node -r babel-register
のようにコマンドラインに現れるのをやめたかったので、require("babel-register")
で進めることにしました。
コードは以下で確認しています。
- node v10
- babel 7
- flow 0.79
※ babel 7 がリリースされました。
構成
インストールする。
$ npm init -y
$ npm install -D @babel/core @babel/register @babel/preset-flow \
@babel/preset-env flow-bin
$ npx flow init
手動実行するエントリポイントを bin/
以下に配置し、 実際のスクリプトの機能実装本体は src/
以下に置くとして、
$ tree . -I node_modules
.
├── bin
│ └── app.js
├── package-lock.json
├── package.json
└── src
└── scripts
└── app
└── index.js
4 directories, 4 files
実装
エントリポイントとなるファイルは node 準拠で書く。require("@babel/register")
以降の require で babel での動的なコンパイルが行われます。ここでは、 ../src/scripts/app.js
が babel で処理されます。
// @flow
require("@babel/register");
const { app } = require("../src/scripts/app")
app()
src 以下のファイルは babel を通す前提で型定義もそのまま書いてしまう。普通のJSとしてはエラーになりますが、 @babel/preset-flow
を通すことで型定義が無視されて素直に実行できるようになる寸法。
// @flow
type LogicResult = {
code: string
}
function logic(): LogicResult{
return {
code: "Success"
}
}
export function app(){
console.log(logic())
}
.babelrc には preset-env と preset-flow だけとりあえず設定している。
{
"presets": [
"@babel/preset-env",
"@babel/preset-flow"
]
}
.flowconfig は生成されたときのまま。
[ignore]
[include]
[libs]
[lints]
[options]
[strict]
これでいつでも node bin/app.js
できますし、flowtype
の型チェックの恩恵も受けられます。
なお、flowによるチェックはコンパイル時に自動で走るわけではないので npx flow check
手動で叩く必要があります。このあたりは typescript の方が確実にチェックされるという意味では確実かなという気も。
なお、requireをフックしてビルドしている関係上遅いです。上記くらいの小さなコードでも
$ time node bin/app.js
{ code: 'Success' }
real 0m0.329s
user 0m0.295s
sys 0m0.051s
user 0.3秒くらいかかります。あくまでも「シェルスクリプトよりちょっとボータブルな自動化スクリプトを、静的型付けで書きたい or 業務ロジックとモデル共通化したい」みたいなケースで使える構成上の小技として考えたほうがよさそう。
本番系で使うバッチとか、npmモジュールの実行エントリポイントとして書くならちゃんとビルドしたものを使いましょうということで一つ。