MarkdownのコードブロックをESLintでチェックするtextlintルール
今、js-primerを書いていて、この本ではいろんなものをテストできる状態にして開発しています。
詳しくはCONTRIBUTING.mdに書いていますが、今のところ次のようなテストが常に回っています。(CONTRIBUTINGもお待ちしています)
- GitBookのビルドテスト
- textlintによる文章のLint
- ESLintによるコードのLint
- textlint + ESLintによるMarkdown中のインラインコードブロックのLint
- Markdown中のインラインコードブロックへのDocTest
- Mochaによる
*-test.js
ファイルのユニットテスト *-example.js
がJavaScriptとして実行できるかのテスト*-invalid.js
がJavaScriptとして実行できないかのテスト
JavaScriptを学ぶ本なので、大量のサンプルコードがでてくるのですが、そのサンプルコードが常に正しくある必要があります。 そのため、いろんな方向からテストして間違っているサンプルコードを含めないようにしています(逆に間違っていることをテストする仕組みもあります)
サンプルコードは大きく分けると、文章中にコードブロックで直接書くインラインコードと外部ファイルとして作成して読み込むコードがあります。
インラインコードは、ESLintでスタイルのチェックやdoctestを行い実行結果のテストをしています。
以前話した方法ではESLintのeslint-plugin-markdownを使い、インラインコードをESLintでチェックしていました。
普通なら正しいコードしか出てこないので問題ないのですが、入門書という位置づけであるため次のような間違ったコードも解説として登場します。
そして、一度`const`で宣言された変数には再代入できなくなります。
そのため、次のコードでは`bookTitle`を上書きしようとして`TypeError`となります。
```js
const bookTitle = "JavaScriptの本";
bookTitle = "上書き"; // TypeError: invalid assignment to const `bookTitle'
```
一般に変数への再代入は「変数の値は最初に定義した値と常に同じである」という参照透過性を壊すため、
バグを発生させやすい要因として知られています。
via 変数と宣言 · JavaScriptの入門書 #jsprimer
eslint-plugin-markdownではjs
がついたコードブロックが自動でチェックされてしまうため、このコードブロック自体を無視して欲しいという書き方ができません。
(コードの中でESLintを eslint-disable
はできるが表示されてしまう)
そのため、Markdownのレベルでコードブロックを無視できるような仕組みが必要になりました。
textlint-rule-eslint
textlintは7.0からフィルタールールという機能を持っていて、このフィルタールールであるtextlint-filter-rule-commentsを使えば次のような形で無視する領域を作ることができます。
<!-- textlint-disable -->
この部分はtextlintのチェックでエラーがあっても無視される
<!-- textlint-enable -->
つまり、textlintのルールとしてESLintを動かすことができれば、textlintとしてコードブロックを無視する = ESLintのチェックしないコードブロックを作れるのでtextlint-rule-eslintを作りました。 (プラグインのアーキテクチャとかも似てるので10分ぐらいでできた)
これでインラインコードと外部ファイルのJavaScriptにESLintを適応することができるので、インデントがずれてないかやSyntaxがおかしくないかなどのチェックをすることができるようになっています。 (逆にベストプラクティス的なルールは引っかかりまくるので外しています…)
おまけ
後、最近入れた面白いテストとしてdoctestライクなものをテストとして入れています。
*-example.js
のJavaScriptファイルとMarkdownのインラインコードブロックを対象にDocTestが実行されています。
次のように// => 値
というコメントを書いた部分に対してDocTestが実行されます。
let a = 42;
console.log(42); // => 42
これにより、サンプルコードのコメントに書いた評価結果と実際の出力が一致するかをテストしてる感じです。
色々テストを入れているのは、いい文章といいコードは同時に書くのが難しいというのもあります。 そのため、とにかく機械的に落とせる部分は落とせるようにしています。
色々コードを考えて書いた結果、文章がダメだとCIが落ちる
— azu (@azu_re) July 3, 2016
読みやすい文章と読みやすいコードは同時に書くのが難しい "[WIP] 条件分岐の実装 by azu · Pull Request #69 · asciidwango/js-primer" https://t.co/UJ41KV5Ttk
— azu (@azu_re) July 3, 2016
後、機械的にチェックする利点の話は以下の文章を読むと面白いかもしれません。
ツールによる検査の利点 文書執筆の指南書で解説されている問題点を RedPen で発見する - Qiita
リポジトリをWatchしていると分かるかもしれませんが、書く前にIssues · asciidwango/js-primerにどういう方針にするか、どういうサンプルコードがあると話がし易いかなどの設計をすることが多いです。 (なので適当にコメントしてくれると参考になります)
書きながら考えることも多いのですが、その場合はごちゃごちゃした文章ができて大体CIが落ち始めます。そこで機械的なチェックやPull Requestを出して自分でレビューする(PRも勝手にコメントくれると嬉しい)と少しはまともになります。
そういうことを繰り返しを行うことで文章やコードの質を上げる方法を取っています。(textlintを入れたところで文章の質が上がるわけではないです)
まあ、一発でテストが通った文章とか不安になるのと同じ感覚です。
お知らせ欄
JavaScript Primerの書籍版がAmazonで購入できます。
JavaScriptに関する最新情報は週一でJSer.infoを更新しています。
GitHub Sponsorsでの支援を募集しています。