stylelint開発ガイドープラグイン
内容の目的
本内容は、stylelint開発ガイドープラグイン を翻訳し、個人活用のために整理しています。
プラグインの作成
プラグインとは、カスタムルールまたはその集合体のことです。特定のメソドロジーやツールセットをサポートしたり、非標準構文・機能を対象としたり、特定のユースケース向けに設計されることがあります。
カスタムルールは、以下のような Stylelint のルール規約 に準拠することを推奨します:
- 名前付け
- オプション
- メッセージ
- テスト
- ドキュメント
- メタデータ
- 構文特有のパーサー
プラグインの構造
次のサンプルは、セレクタに "foo" を含むことを禁止するプラグインです:
import stylelint from "stylelint";
const {
createPlugin,
utils: { report, ruleMessages, validateOptions }
} = stylelint;
const ruleName = "foo-org/selector-no-foo";
const messages = ruleMessages(ruleName, {
rejected: (selector) => `Unexpected "foo" within selector "${selector}"`
});
const meta = {
url: "https://github.com/foo-org/stylelint-selector-no-foo/blob/main/README.md"
};
/** @type {import('stylelint').Rule} */
const ruleFunction = (primary, secondaryOptions, context) => {
return (root, result) => {
const validOptions = validateOptions(result, ruleName, {
actual: primary,
possible: [true]
});
if (!validOptions) return;
root.walkRules((ruleNode) => {
const { selector } = ruleNode;
if (!selector.includes("foo")) return;
report({
result,
ruleName,
message: messages.rejected(selector),
node: ruleNode,
word: selector
});
});
};
};
ruleFunction.ruleName = ruleName;
ruleFunction.messages = messages;
ruleFunction.meta = meta;
export default createPlugin(ruleName, ruleFunction);
@type
の JSDoc アノテーションを使うことで、TypeScript の補完と型チェックが可能になります。
使用例:
{
"plugins": ["@foo-org/stylelint-selector-no-foo"],
"rules": {
"foo-org/selector-no-foo": true
}
}
$ echo '.foo {}' | stylelint --stdin-filename=test.css
test.css
1:1 ✖ Unexpected "foo" within selector ".foo" foo-org/selector-no-foo
1 problem (1 error, 0 warnings)
プラグインのルール名は、 your-namespace/your-rule-name
のように名前空間付きである必要があります。そうすることで、組み込みルールとの衝突を避けられます。ルールが1つだけであったり適切な名前空間が思いつかない場合は、plugin/my-rule
を使うこともできます。設定ファイルに記載するため、ルール名と名前空間は必ずドキュメントに記載しましょう。
stylelint.createPlugin(ruleName, ruleFunction)
を使用して、他のルールと一貫した方法でプラグインを構築できます。
標準の設定フォーマットに対応するためには、ruleFunction
は以下の2つの引数を受け取る必要があります:
- プライマリオプション
- (任意で)セカンダリオプションのオブジェクト
オートフィックスに対応する場合は、meta.fixable = true
を設定してください。
ruleFunction
は、2つの引数(PostCSS の Root ノードと LazyResult)を受け取る関数を返す必要があります。これは実質的に PostCSS プラグイン として動作します。
PostCSS API について学んでおくと良いでしょう。
非同期ルール
Promise を扱う場合、非同期関数としてルールを書くことができます:
const ruleFunction = (primary, secondaryOptions) => {
return async (root, result) => {
// オプションを検証...
// 禁止語句を非同期で読み込む
const disallowedWords = await import("./disallowed-words.js");
// AST ノードを走査...
// 問題がある語句を検出した場合は警告を出す...
};
};
テスト
以下のどちらかを使用できます:
-
stylelint-test-rule-node
(Node.js のtest
ベース) -
jest-preset-stylelint
(Jest ベース)
どちらも testRule
関数を提供しており、ルールのスキーマに基づいて効率的にテストできます。
例:
import { testRule } from "stylelint-test-rule-node";
import plugin from "./index.js";
const {
rule: { messages, ruleName }
} = plugin;
testRule({
plugins: [plugin],
ruleName,
config: true,
fix: true,
accept: [
{ code: ".a {}" },
{ code: ".b {}" }
],
reject: [
{
code: ".foo {}",
fixed: ".safe {}",
message: messages.rejected(".foo"),
line: 1,
column: 1,
endLine: 1,
endColumn: 8
}
]
});
より多くのテスト手法は Awesome Stylelint にあります。
より高度な解析や構文以上の検査を行う場合は、stylelint.lint()
を直接使うこともできます。
stylelint.utils
Stylelint は便利なユーティリティ関数も提供しています。内部ユーティリティをコピーして使っても構いませんが、直接インポートは非推奨です。将来変更や削除の可能性があります。
stylelint.utils.report()
ルールが検出した問題を、Stylelint の出力に追加します。無効範囲(disabled ranges)なども考慮されるため、必ずこの関数を使いましょう。
stylelint.utils.ruleMessages()
Stylelint 標準ルールのフォーマットに合うようにメッセージを整形します。
stylelint.utils.validateOptions()
ルールのオプションが妥当かどうかを検証します。
stylelint.utils.checkAgainstRule()
自作ルール内で、他の Stylelint ルールを呼び出して CSS をチェックできます。既存ルールの機能を上書き・拡張したい場合に有効です。
非同期関数である点に注意してください。
stylelint.rules
すべての組み込みルールは stylelint.rules
経由でアクセス可能です。これにより、既存ルールの上に独自ロジックを組み立てることができます。
プライマリオプションに配列を許容する
ルールが配列を受け入れる場合、ruleFunction.primaryOptionArray = true
を設定する必要があります。
ピア依存関係
プラグインの package.json
には peerDependencies
として Stylelint のバージョンを明示する必要があります。
{
"peerDependencies": {
"stylelint": "^14.0.0 || ^15.0.0"
}
}
複数ルールを提供するプラグインパック
1 つのモジュールで複数のルールを提供する場合は、プラグインオブジェクトの配列をエクスポートしてください。
プラグインと構成の共有
- プラグインは
stylelint-plugin
キーワードをpackage.json
に記載してください。 - プラグイン構成(ルールセット)を共有する場合は
stylelint-config
を使用します。
Discussion