CUBE SUGAR CONTAINER

技術系のこと書きます。

textlint でシンプルなプリセットを自作する

textlint 1 は自然言語向けの Linter のひとつ。 前回は勉強のために textlint のシンプルなルールを自作した。

blog.amedama.jp

今回は、それに引き続いてシンプルなプリセットを自作してみる。 プリセットは、複数のルールを束ねてチェックする内容を表現するときに実装する。 ただし、簡単のために含めるルールはひとつだけにする。

使った環境は次のとおり。

$ sw_vers
ProductName:        macOS
ProductVersion:     26.3.1
BuildVersion:       25D2128
$ uname -srm      
Darwin 25.3.0 arm64
$ npm --version
11.11.0
$ node --version
v25.8.1

もくじ

下準備

下準備として Node.js をインストールしておく。

$ brew install node

パッケージを作成する

ルールと同様、プリセットの場合もパッケージ名は textlint-rule- のプリセットをつける。 今回は textlint-rule-preset-internet-rojinkai というパッケージ名にした。

$ mkdir -p textlint-rule-preset-internet-rojinkai
$ cd textlint-rule-preset-internet-rojinkai

パッケージのメタデータを記述した最低限の package.json を用意する。 今回はソースコードは src 以下のディレクトリに設置する。

{
  "name": "textlint-rule-preset-internet-rojinkai",
  "version": "0.0.1",
  "description": "hantoshi ROM re",
  "type": "module",
  "main": "src/index.js",
  "scripts": {
    "test": "textlint-scripts test"
  }
}

必要なパッケージを追加する。

$ npm install --save-dev textlint textlint-scripts textlint-tester

ルールを組み込む

続いて、ルールを配置するディレクトリを用意する。

$ mkdir -p src/rules

個別のルールを用意する。 今回は、前回作成したのと全く同じルールにしている。 内容は文章に「ぬるぽ」や「ヌルポ」があるとエラーになるもの。 これを src/rules/no-null-pointer-exception.js に設置する。

// ルールに対応する処理を記述した関数を export する
export default function (context, options = {}) {
    // 処理に必要なオブジェクトは関数の引数として得られる
    const { Syntax, getSource, RuleError, report, locator } = context;
    return {
        // AST のオブジェクト毎にチェックする処理が書ける
        // 文字列の中身を見るだけなら Syntax.Str で良い
        [Syntax.Str](node) {
            // ノードに含まれるテキストを得る
            const text = getSource(node);
            // 特定の文字列が含まれるかチェックする (見つかったら違反)
            const matches = text.matchAll(/ぬるぽ|ヌルポ/g);
            // 特定の文字列が見つかった場合のループ
            for (const match of matches) {
                // テキストの中で見つかった場所を求める
                const index = match.index ?? 0;
                const length = match[0].length;
                const matchRange = [index, index + length];
                // ルールに違反したことを RuleError で伝える
                const ruleError = new RuleError("ガッ!", {
                    padding: locator.range(matchRange),
                });
                // report() 関数を使って違反の詳細を報告する
                report(node, ruleError);
            }
        },
    };
}

続いて、上記を組み込んだエントリポイントとして src/index.js を作成する。 ここで rulesrulesConfig を含むオブジェクトを export する。 rules がルールで、rulesConfig がルールを動作させる設定になる。 rulesConfigtrue を指定するとデフォルトでルールが有効になる。

import noNullPointerException from "./rules/no-null-pointer-exception";

const preset = {
    rules: {
        "no-null-pointer-exception": noNullPointerException,
    },
    rulesConfig: {
        "no-null-pointer-exception": true,
    }
};

export default preset;

ここではパッケージ内のルールを読み込んで使っている。 もちろん外部のパッケージからインポートしたルールを組み込んでも良い。

テストを書く

続いて、ルールをテストするディレクトリを用意する。

$ mkdir -p test/rules

先ほどプリセットに組み込んだルールをテストするコードを用意する。 以下を test/rules/no-null-pointer-exception.test.js として作成する。

import TextLintTester from "textlint-tester";

// テストする対象を import する
import rule from "../../src/rules/no-null-pointer-exception";

// TextLintTester を使ってテストが書ける
const tester = new TextLintTester();

tester.run("no-null-pointer-exception", rule, {
    // 違反していないパターン
    valid: [
        "こんにちは",
        "NullPointerException",
    ],
    // 違反しているパターン
    invalid: [
        {
            // 違反しているテキスト
            text: "ぬるぽ",
            // 違反したときのメッセージと場所
            errors: [
                {
                    message: "ガッ!",
                    range: [0, 3]
                }
            ]
        },
        {
            text: "ヌルポ",
            errors: [
                {
                    message: "ガッ!",
                    range: [0, 3],
                }
            ]
        }
    ]
});

npm run test コマンドを実行してテストを実行する。

$ npm run test

> textlint-rule-preset-internet-rojinkai@0.0.1 test
> textlint-scripts test



  no-null-pointer-exception
    ✔ ぬるぽ
    ✔ ヌルポ
    ✔ こんにちは
    ✔ NullPointerException


  4 passing (8ms)

テストがパスして、ちゃんとルールが動作していることが確認できた。

コマンドラインから動かしてみる

続いて、実際にコマンドラインからプリセットを使ってみる。

チェックする対象のファイルを用意する。

$ cat << EOF > /tmp/nullpo.md 
# 「NullPointerExceptionを「ぬるぽ」と呼ぶスレ

 ∧_∧
( ´∀`)< ぬるぽ
EOF

パッケージをリンクして使えるようにする。

$ npm link
$ npm link textlint-rule-preset-internet-rojinkai

textlint コマンドに --preset オプションを使って今回作成したプリセットを読み込む。 プリセット名を指定するとき textlint-rule- をつける必要はない。

$ ./node_modules/.bin/textlint --preset preset-internet-rojinkai -f pretty-error /tmp/nullpo.md
internet-rojinkai/no-null-pointer-exception: ガッ!
/tmp/nullpo.md:1:26
                                    v
    0. 
    1. # 「NullPointerExceptionを「ぬるぽ」と呼ぶスレ
    2. 
                                    ^

internet-rojinkai/no-null-pointer-exception: ガッ!
/tmp/nullpo.md:4:9
                     v
    3.  ∧_∧
    4. ( ´∀`)< ぬるぽ
    5. 
                     ^

✖ 2 problems (2 errors)

ちゃんとルールに基づいてエラーになっている。

まとめ

今回は勉強のために textlint のシンプルなプリセットを自作してみた。