メインコンテンツまでスキップ

カスタムルールをつくる

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")」のどちらかを回答してください。

プロジェクトへ追加する

ディレクトリ名を聞かるので答えます。ルール名も答えてください。

TypeScriptJavaScriptのどちらかの言語を選び、テストを実施するかどうかを決めてください。

すると、以下のファイルが作成されます。

  • 📂 [cwd]
    • 📂 [dir-name]
      • 📄 index.ts # もしくは index.js
      • 📂 rules
        • 📄 [rule-name].ts # もしくは [rule-name].js
        • 📄 [rule-name].spec.ts # もしくは [rule-name].spec.js (任意)
備考

テストVitest形式で書かれます。適宜書き換えてください。

最終的には、設定ファイルに指定して適用します。

{
"plugins": ["./[dir-name]/index.js"], // ソースがTypeScriptの場合、別途トランスパイルが必要です。
"rules": {
"[dir-name]/[rule-name]": true
}
}
注記

デフォルトでは、プラグイン名はサンプルコードで[dir-name]と示した部分がディレクトリ名になります。必要であれば変更します。

./[dir-name]/index.ts
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パッケージとして作成する

プラグイン名を聞かれるので答えます。ルール名も答えてください。

TypeScriptJavaScriptのどちらかの言語を選び、テストを実施するかどうかを決めてください。

最終的に以下のファイルが作成されます。

  • 📂 [cwd]
    • 📄 README.md
    • 📄 package.json
    • 📄 tsconfig.json # TypeScriptを選択したときのみ
    • 📂 src
      • 📄 index.ts # もしくは index.js
      • 📂 rules
        • 📄 [rule-name].ts # もしくは [rule-name].js
        • 📄 [rule-name].spec.ts # もしくは [rule-name].spec.js (任意)

基本的な評価方法

documentオブジェクトから対象ノードを抽出します。それを評価してからreport関数に渡します。documentオブジェクトはMarkuplint固有のメソッドであるwalkOnメソッドなどを持ちます。またネイティブのDOM APIquerySelectorメソッドなど)を持っているので、用途に応じて使い分けることができます。

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の詳細をご覧ください。

備考

現在、辞書データには英語の他に日本語しかありません。他の言語の翻訳への貢献も期待しています。