カスタムルールをつくる
APIドキュメントを参照しながらカスタムルールを作成できますが、コマンドを利用することで楽に作成ができます。
npx @markuplint/create-rule
出力された質問に答えてください。
? What purpose do you create the rule for? …
❯ Add the rule to this project
  Create the rule and publish it as a package
最初の質問で、「ルールをこのプロジェクトに追加する("Add the rule to this project")」か、「ルールを作成してパッケージとして公開する("Create the rule and publish it as a package")」のどちらかを回答してください。
プロジェクトへ追加する
ディレクトリ名を聞かれるので答えます。ルール名も答えてください。
TypeScriptかJavaScriptのどちらかの言語を選び、テストを実施するかどうかを決めてください。
すると、以下のファイルが作成されます。
テストはVitest形式で書かれます。適宜書き換えてください。
最終的には、設定ファイルに指定して適用します。
{
  "plugins": ["./[dir-name]/index.js"], // ソースがTypeScriptの場合、別途トランスパイルが必要です。
  "rules": {
    "[dir-name]/[rule-name]": true
  }
}
デフォルトでは、プラグイン名はサンプルコードで[dir-name]と示した部分がディレクトリ名になります。必要であれば変更します。
import { createPlugin } from '@markuplint/ml-core';
import { ruleName } from './rules/ruleName';
export default createPlugin({
  name: '[dir-name]', // 👈 必要であれば変更してください。
  create(setting) {
    return {
      rules: {
        ruleName: ruleName(setting),
      },
    };
  },
});
プラグインをnpmパッケージとして作成する
プラグイン名を聞かれるので答えます。ルール名も答えてください。
TypeScriptかJavaScriptのどちらかの言語を選び、テストを実施するかどうかを決めてください。
最終的に以下のファイルが作成されます。
基本的な評価方法
documentオブジェクトから対象ノードを抽出します。それを評価してからreport関数に渡します。documentオブジェクトはMarkuplint固有のメソッドであるwalkOnメソッドなどを持ちます。またネイティブのDOM API(querySelectorメソッドなど)を持っているので、用途に応じて使い分けることができます。
createRule({
  async verify({ document, report }) {
    // Walkスタイル
    await document.walkOn('Element', el => {
      if (el.localName === 'div') {
        report({
          scope: el,
          message: 'The div element is found',
        });
      }
    });
    // DOM探索スタイル
    const el = document.querySelector('div');
    if (el) {
      report({
        scope: el,
        message: 'The div element is found',
      });
    }
  },
});
report関数に違反情報を渡すには、2つの方法があります。ひとつは、前述したようにノードを渡す方法。そしてもうひとつは、行と列の番号と、範囲内の文字列を渡す方法です。
report({
  scope: node, // ノード(要素、属性、テキストノードなど)を設定します
  message: 'Warning message',
});
report({
  line: 20,
  col: 10,
  raw: 'string in range',
  message: 'Warning message',
});
メッセージの多言語化
translate関数(tという別名があります)は、メッセージを翻訳します。
createRule({
  async verify({ document, report, translate, t }) {
    const noTitle = !document.querySelector('title');
    if (noTitle) {
      report({
        line: 1,
        col: 1,
        raw: '',
        message: translate('missing {0}', t('the "{0*}" {1}', 'title', 'element')),
      });
    }
  },
});
Missing the "title" element
「title」要素がありません
必要に応じて、@markuplint/i18n APIの詳細をご覧ください。