今では200以上のルールがある自然言語のLintツールであるtextlintがどのように作られたかを振り返る記事です。

Maintainer Month

6月はMaintainer MonthというイベントをGitHubが主催しています。

Maintainer Month is a reminder for the ecosystem to support, celebrate, and compensate open source maintainers.
Maintainer Month

Maintainer Monthは、オープンソースのメンテナーが集まって情報共有したり、メンテナーを祝ったり、支援したりするイベントです。 メンテナーがどういうサポートを求めているかを知る、負荷が特定の箇所に集中するエコシステムはバランスが悪いのでそれを支援する方法を知るといったことを思い出すのがMaintainer Monthの目的です。

Maintainer Month関係の記事

textlintの誕生…の前に

2014年2月ごろに、JSer.infoであなたが読むべきJavaScript Promises - JSer.infoという記事を書いて、Promisesについてもっと知らないといけなさそうだなと考えました。 調べてもPromiseについてまとまったものはなかったので、ないなら書くかーと思ってJavaScript Promiseの本を書き始めました。

Promise本の最初のコミットは、2014年3月だったのでこの頃に書き始めたのだと思います。

Promise本は最終的に100ページを越える書籍になっているのですが、書いてる途中で表記揺れなどが気になってきました。

そこで、技術評論社で使われているWEB+DB PRESS用語統一ルールというWZ EDITORの表記揺れを統一ための辞書が公開されていることに注目しました。

とりあえず、この辞書を使えば表記揺れが少なくなるかもと思って、WZ EDITOR用の形式だったので、辞書をJSONに変換できるパーサを書きました。

これを使えば、日本語Lintできるんじゃないかとか言ってたので、この頃から自然言語のLintが少し思い浮かんでいたようです。

結局この時は辞書をパースしただけで、Promise本での表記揺れには使っていませんでした。

少し日にちが経って、全く別のところでJSer.infoの記事紹介文でtypoや表記揺れが多くあることに気づきました。 これを減らそうと思って、CodeMirrorの拡張としてcodemirror-spellckeckerというものを書きました。 このスペルチェックには、先ほど登場したWEB+DB PRESS用語統一ルール をベースにしたtechnical-word-rulesという独自の辞書を使っています。

このcodemirror-spellckeckerは、CodeMirrorなのでブラウザ上でしか動きません。しかし、Travis CIのようなCI上でも同じ辞書を使ってチェックしたいと考えるようになってきました。

なぜなら、この少し前にJSer.infoのデータ管理もGitHubに全て移したため、紹介記事のデータもjser/jser.info: JSer.infoデータリポジトリとへコミットされるようになり、コミットしたら辞書でCI上で表記揺れを見つけたいと思ったためです。

technical-word-rulesはただの辞書のJSONファイルです。 この辞書を使ってブラウザでも、Node.jsでも、CIでも動く汎用的な自然言語のチェックツールが欲しい!となったのがtextlintを作ろうと思ったきっかけです。

現在はニンゲンが介在しないとちょっとチェックの精度が良くない感じで完全に補助するツールになってます。 (こういうの既にプロダクトとして、プログラマブルに使えるものって存在してるのかな?)
https://efcl.info/2014/10/20/lint-tech-word/

codemirror-spellckeckerは、ただの文字列一致であったため、Markdownで書いても構文を認識する訳ではありませんでした。これによる誤検知があったので、このようなことを書いているのだと思います。

そのため、textlintを作る時には文字列一致ではなくASTをベースにして扱うことを考えていました。

textlintを作り始める

2014年12月24日にnono/mddiffというライブラリを見つけて、このライブラリがCommonMarkというパッケージを使っていることに気づきました。 CommonMarkはMarkdownをパースして、内部的にASTを持っていることを発見しました。

その日のうちに、CommonMarkを使って日本語Lintを作るアイデアを思いつきました。

この次の日にtextlintを作り始めました。 そのため、textlintのfirst commitは2014年12月25日でした。

最初は、remarkではなくCommonMarkをベースにして作っていました。(そもそもこの時はremarkはまだノードの位置をASTに持ってない状態でした。そのため、CommonMark以外にはノードの位置とASTを扱えるライブラリはなかった)

そのまま一気に開発して、2014年12月30日には最初のtextlintをリリースしています。

この時のルールは、サンプルとして書いたno-todoと先ほども出ていた独自辞書のtextlint-rule-spellcheck-tech-wordぐらいだったと思います。 textlintのプラグインの仕組みはESLintを参考にして書いていて、初期リリースはESLintのコードを結構そのまま持ってきて書き直してたきがします。

この辺の学びについては次のスライドにまとめてありました。

ESLintは結構モノリシックな作りであったため、textlintはそれをベースにモジュールを意識して書き直していた気がします。 これが、その後のTypeScript移行やブラウザで動かすためにKernel分離などに役立ったと思います。

2015年はひたすらtextlintの安定化と色々なルールを書いていました。 RedPenを参考にしたり、色々日本語の論文とか参考にしてルール書いてた気がします。

2016年になるともっとtextlintを開発するために、新しく本を書くことにしました。 JavaScript Plugin Architectureという書籍はtextlintの開発を進めるために書いた書籍で、ESLintを含むJavaScriptのプラグインアーキテクチャについて書いています。

これと同じようなことをjsprimerというJavaScript入門書を書くときにやっていた気がします。 やっぱり、実際に大きな文章があるとツールの開発が進みます。

また、この頃は思いついたアイデアをひたすらtextlintのルールに落としていた気がします。

このような形でtextlintの開発は進み、2022年ではtextlintのルールは200以上あります。

textlintの今後

今年は、去年末に書いていたRFCやNode.js ECMAScript Modules対応など結構大きな変更が必要になりそうです。

既にRFCの一つであるRFC: Improve error location #835 は実装していてリリースされています。 この変更は、今までtextlintのエラー位置は”点”でしたが、エラーの”範囲”を報告できるようになるという変更です。(ルール側もpaddingの対応が必要です。)

この変更は@textlint/editorを作っていて、エラー位置をエディタ上で正確に出すには、点だけでは不十分となっているためです。 RFC: textlint new message format · Discussion #833 · textlint/textlintには他にもProposalがあって、ローカライズするために使えそうなMessageIdの仕組み、エラーとドキュメントを紐づける仕組み、エラーではなくサジェストの仕組みなどがあります。

この辺はやっぱりユースケースがあることで進むので、ユースケースを持ってる人は意見をください。

また、@textlint/editorで、.textlintrcのロード周りの処理を再実装したりしてるので、これをtextlint本体に持ってきて今のよくないConfig.tsを全部書き直すなどやりたいことは色々あります。(手が足りないので、興味ある人は手をあげてください)

また、ESM対応はとりあえずルールをESMで書いて読み込めるようにするため、ルールのロードをimport()でやるような変更を入れるつもりです。 ただし、default exportをBabelで変換したようなCJSをimport()で読み込むとほんとひどいことになるので、どうにかしないといけません。(.default.defaultという危ないプロパティを参照しないといけない…) 全部named exportにするのが一番いいのですが、既存のルールとの互換性があって大変です。

また、ルールのオプションのスキーマを書けるようにするという長年のIssueもどうにかしたいです。 BabelでTSからスキーマを生成するか、ESLintのmetaのように手書きでJSON Schemaを書くかという部分で止まっています。 これも、textlintのルールからオプションをGUIで設定するなどの利用方法がありますが、やっぱりユースケースがある人を中心に進めるのが良いです。

textlintは、日本語が多いですが、英語や中国語などでも使われています。

textlintは文章向けに作り始めたものではありますが、最近はプロダクトの表記揺れとかに使っているケースも増えているようです。

いつの間にかGitHubのgithub/super-linterにも含まれていました。 これが、Thank you to our maintainers | The GitHub Blogに選ばれた理由なのかはよくわかっていません。

まだやりたいことは色々ありますが、やっぱり物事を進めるのはユースケースなので、ユースケースを持ってる人は積極的に意見ください。 Twitter#textlint のようなハッシュタグでもいいし、Issueにコメントしたりでもいいし、Gitterは日本語でも話せます。

支援の方法は色々あるので、textlintはいつでもContributionを募集しています。

オープンソースにContributeする方法として、バグ報告/修正、機能追加、ドキュメントを書く、IssueやDiscussionのトリアージ、デザイン、マーケティングなど色々な関わり方があります。 その関わり方の一つとして、金銭的な支援をするという選択肢は、Contributorが選べるようになっているのが望ましいと思っています。
GitHub Sponsorsの募集を始めてから2年が経ったので振り返る | Web Scratch

GitHub Sponsorsでの支援は次のページからできます。GitHub Sponsorsは個人だけではなく会社単位でもできるようになっています。