JSDocをランタイムassertに変換するBabelプラグインを書いた
JSDocをassertに変換するライブラリとそれを使ったBabelプラグインを書きました。
- azu/babel-plugin-jsdoc-to-assert: Babel plugin for jsdoc-to-assert.
- azu/jsdoc-to-assert: JSDoc to assert
ライブラリのjsdoc-to-assertの方は、JavaScript ASTのコメントからassert
の文字列を作り出すだけのシンプルなものです。
実際に使う場合は、Babelのプラグインとしてbabel-plugin-jsdoc-to-assertを使い、コードを変換してランタイムassertを追加させます。
やっていることとしては、FlowTypeをランタイムチェックするbabel-plugin-typecheckのJSDoc版とも言えます。
- babel-plugin-typecheck を使って flowtype 文法で書かれたJSをランタイムチェックする - Qiita
- codemix/babel-plugin-typecheck: Static and runtime type checking for JavaScript in the form of a Babel plugin.
すごく難しい仕組みではないので、既にやって人がいるのではと思ったのですがいなかったので作りました。
変換例
例えば次のような関数とJSDocがあったとします。
/**
* @param {number} a - this is a param.
* @param {string} b - this is a param.
* @param {string[]} c - this is a param.
* @param {boolean} [d] - this is a optional.
*/
function myFunc(a, b, c, d) {
//...
}
これをbabel-plugin-jsdoc-to-assertでは次のように変換してassert処理を追加しています。
/**
* @param {number} a - this is a param.
* @param {string} b - this is a param.
* @param {string[]} c - this is a param.
* @param {boolean} [d] - this is a optional.
*/
function myFunc(param, b, c) {
console.assert(typeof a === 'number');
console.assert(typeof b === 'string');
console.assert(Array.isArray(c));
}
みて分かるようにかなりシンプルな変換がされます。(実際はもう少しメッセージを追加しますが、最小限です)
assert
ライブラリの依存を解決するのが面倒だったので、console.assert
を使っていますが一部ブラウザは例外を投げずにログを出すだけらしいです。(Nodeは例外を投げてくれる)
d
はオプショナルな引数なので、今のところチェックしていません。
また、string[]
のチェックが配列かどうかぐらいとなっていたり、まだまだ色々チェック不足な部分はあります。
Pull request 待ってます!
使い方
インストール
npm install babel-plugin-jsdoc-to-assert
設定
Babelプラグインなので、Babelと共に利用するために.babelrc
を設定します。
{
"plugins": [
"jsdoc-to-assert"
]
}
とシンプルに設定できますが、実際には開発中のみ変換して欲しいと思うので、env
オプションを使うことで開発中のみ変換できます。
{
"presets": [
"es2015"
],
"env": {
"development": {
"plugins": [
"jsdoc-to-assert"
]
}
}
}
jsdoc-to-assert
の変換は含めずにビルドする場合はproduction
を設定してビルドすれば、適応されずにすみます。
NODE_ENV=production babel src --out-dir lib --source-maps
仕組み
jsdoc-to-assertはコメント(leadingComments Node)をassert
などのコード片に変換するだけです。
function a(){}
や const a = function(){}
、 class A{ method(){} }
などJSDocを書ける場所は沢山あります。
これらどの関数宣言に対してコード変換をするかはbabel-plugin-jsdoc-to-assert側に実装しています。
で書いていますが、BabelのASTはあんまり互換性がないので、babel-plugin-jsdoc-to-assertの部分は複数実装が存在してもおかしくない領域です。
以前、似たようなツールでa; // => "string"
のようなコメントをassertに変換するライブラリも書いていましたが、この場合もコメントをassertに変換する部分と変換したassertをコードに追加する部分は分けて実装していました。
- azu/comment-to-assert: convert single line comment to assert.
- power-assertを使ったDoctestツール power-doctestを書き直した | Web Scratch
話を戻して、JSDoc自体の型定義が結構曖昧(nullableとか定義みたいな部分が曖昧)なので、jsdoc-to-assertも実装見ると分かるようにかなり簡略化しています。
ESLintで静的にJSDocのコメントをチェックできるvalid-jsdocとbabel-plugin-jsdoc-to-assertを合わせて使うようなイメージで書いています。
JSDoc単体の型定義はValidだけど、実際の利用方法と合ってるかどうかをbabel-plugin-jsdoc-to-assertでチェックするイメージです。
また、契約プログラミングに言えばjsdoc-to-assertは事前条件、つまり@param
のみのチェックをしています。
@returns
のチェックはユニットテストで保証するのがいいと思います。
正直まだエラーメッセージも全然わかりやすくないし、中途半端なチェックしかできません。 この辺を改善したい人のContributionsを待ってます!
- azu/babel-plugin-jsdoc-to-assert: Babel plugin for jsdoc-to-assert.
- azu/jsdoc-to-assert: JSDoc to assert
結局はキレイなJSDocが残ればいいだけなので、 babel-plugin-jsdoc-to-assertはいつでも外しても問題ない作りです。 なので、Babelを使ってるプロジェクトにひっそりいれて色々エラーを見つけると面白いのかもしれません。
類似
お知らせ欄
JavaScript Primerの書籍版がAmazonで購入できます。
JavaScriptに関する最新情報は週一でJSer.infoを更新しています。
GitHub Sponsorsでの支援を募集しています。