JSDocをassertに変換するライブラリとそれを使ったBabelプラグインを書きました。

ライブラリのjsdoc-to-assertの方は、JavaScript ASTのコメントからassertの文字列を作り出すだけのシンプルなものです。

実際に使う場合は、Babelのプラグインとしてbabel-plugin-jsdoc-to-assertを使い、コードを変換してランタイムassertを追加させます。

やっていることとしては、FlowTypeをランタイムチェックするbabel-plugin-typecheckのJSDoc版とも言えます。

すごく難しい仕組みではないので、既にやって人がいるのではと思ったのですがいなかったので作りました。

変換例

例えば次のような関数と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をコードに追加する部分は分けて実装していました。

話を戻して、JSDoc自体の型定義が結構曖昧(nullableとか定義みたいな部分が曖昧)なので、jsdoc-to-assertも実装見ると分かるようにかなり簡略化しています。

ESLintで静的にJSDocのコメントをチェックできるvalid-jsdocbabel-plugin-jsdoc-to-assertを合わせて使うようなイメージで書いています。

JSDoc単体の型定義はValidだけど、実際の利用方法と合ってるかどうかをbabel-plugin-jsdoc-to-assertでチェックするイメージです。

また、契約プログラミングに言えばjsdoc-to-assertは事前条件、つまり@paramのみのチェックをしています。 @returnsのチェックはユニットテストで保証するのがいいと思います。

正直まだエラーメッセージも全然わかりやすくないし、中途半端なチェックしかできません。 この辺を改善したい人のContributionsを待ってます!

結局はキレイなJSDocが残ればいいだけなので、 babel-plugin-jsdoc-to-assertはいつでも外しても問題ない作りです。 なので、Babelを使ってるプロジェクトにひっそりいれて色々エラーを見つけると面白いのかもしれません。

類似