プラグインの作成
プラグインは、カスタムルールとカスタムルールのセットです。特定の方法論やツールセットをサポートしたり、非標準の構成要素や機能に適用したり、特定のユースケースで使用したりすることができます。
カスタムルールは、当社のルールの規約に従うことをお勧めします。
- 名前
- オプション
- メッセージ
- テスト
- ドキュメント
- メタデータ
- 構成要素固有のパーサー
プラグインの構成
このプラグインの例では、セレクター内の単語「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` のように名前空間を使用する必要があります。これにより、組み込みのルールと衝突することがなくなります。プラグインが単一のルールしか提供しない場合、または適切な名前空間が思いつかない場合は、`plugin/my-rule`を使用できます。 *ユーザーは設定で使用する必要があるため、プラグインのルール名(および名前空間)をドキュメント化する必要があります。*
プラグインが他のルールと共に適切に設定されるようにするには、`stylelint.createPlugin(ruleName, ruleFunction)`を使用します。
プラグインルールを標準的な設定形式で動作させるには、`ruleFunction`は2つの引数を受け入れる必要があります。
- 主要なオプション
- オプションで、2番目のオプションオブジェクト
プラグインルールが自動修正をサポートしている場合、`ruleFunction`は3番目の引数`context`も受け入れる必要があります。
`ruleFunction`は、本質的に小さなPostCSSプラグインである関数を返す必要があります。2つの引数を受け入れます。
- PostCSSルート(解析されたAST)
- PostCSS LazyResult
PostCSS APIについて学ぶ必要があります。
非同期ルール
`Promise`を処理するために、プラグインを*非同期関数*として記述できます。
const ruleFunction = (primary, secondaryOptions) => {
return async (root, result) => {
// validate options...
// load disallowed words asynchronously
const disallowedWords = await import("./disallowed-words.js");
// traverse AST nodes...
// report a warning if a problem word is detected...
};
};
テスト
どちらかを使用できます。
stylelint-test-rule-node
(`node: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を直接使用できます。
例:
import stylelint from "stylelint";
const { lint } = stylelint;
const config = {
plugins: ["./index.js"],
rules: {
"foo-org/selector-no-foo": true
}
};
it("warns", async () => {
const {
results: [{ warnings, parseErrors }]
} = await lint({
files: ["fixtures/test.css"],
config
});
expect(parseErrors).toHaveLength(0);
expect(warnings).toHaveLength(1);
const [{ text, line, column }] = warnings;
expect(text).toBe('Unexpected "foo" within selector ".foo"');
expect(line).toBe(1);
expect(column).toBe(1);
});
it("doesn't warn", async () => {
const {
results: [{ warnings, parseErrors }]
} = await lint({
code: ".foo {}",
config
});
expect(parseErrors).toHaveLength(0);
expect(warnings).toHaveLength(0);
});
stylelint.utils
Stylelintはいくつかの便利なユーティリティを公開しています。
内部ユーティリティのいずれかをプラグインにコピーすることもできます。公開APIの一部ではないため、直接`import`しないでください。予告なしに変更または削除される可能性があります。
stylelint.utils.report()
プラグインからの問題を、Stylelintがユーザーに報告する問題のリストに追加します。
プラグインが、無効化された範囲やStylelintのその他の将来的な機能を尊重するようにするには、`stylelint.utils.report()`を使用します。
PostCSSのNode#warn()
メソッドを直接使用しないでください。
問題が発生した場所の詳細を指定するために、PostCSSの警告オプション(例:`word`、`index`、`start`など)を指定できます。
stylelint.utils.ruleMessages()
メッセージを標準のStylelintルールの形式に合わせて調整します。
stylelint.utils.validateOptions()
ルールのオプションを検証します。
stylelint.utils.checkAgainstRule()
独自のルール内で、標準またはカスタムのStylelintルールに対してCSSをチェックします。この関数は、既存のStylelintルールの機能を変更、制約、または拡張したいプラグイン作成者にとって、強力で柔軟性のある機能を提供します。
これは非同期関数です。カスタムルールは、関数が返す`Promise`が解決されるまで待機する必要がある場合があります。
オプションオブジェクトと、指定されたルールからの警告を使用して呼び出されるコールバックを受け入れます。オプションは次のとおりです。
ruleName
:呼び出しているルールの名前ruleSettings
:呼び出しているルールの設定root
:このルールを実行するルートノードresult?
:カスタムルールを解決して呼び出すためのPostCSS結果context?
:呼び出しているルールのコンテキスト
警告を使用して、stylelint.utils.report()
で報告する、*プラグインルールからの*新しい警告を作成します。
たとえば、プリプロセッサで提供されるatルールの組み込み例外リストを使用してat-rule-no-unknown
を実行するプラグインを作成したいとします。
const {
utils: { checkAgainstRule, report }
} = stylelint;
const allowableAtRules = [
/* .. */
];
const ruleName = "your-own/at-rule-no-unknown";
const myPluginRule = (primary, secondaryOptions, context) => {
return async (root, result) => {
const ignoreAtRules = allowableAtRules.concat(
secondaryOptions?.ignoreAtRules ?? []
);
const defaultedSecondaryOptions = { ...secondaryOptions, ignoreAtRules };
await checkAgainstRule(
{
ruleName: "at-rule-no-unknown",
ruleSettings: [primary, defaultedSecondaryOptions],
root,
result,
context
},
(warning) => {
report({
ruleName,
result,
message: warning.text,
node: warning.node,
start: { line: warning.line, column: warning.column },
end: { line: warning.endLine, column: warning.endColumn }
});
}
);
};
};
stylelint.rules
すべてのルール関数は、stylelint.rules
オブジェクトで使用できます。これにより、特定のニーズに合わせて既存のルールの上に構築できます。
stylelint.rules
オブジェクトのすべての値は、ルール関数を解決する`Promise`です。
一般的なユースケースは、ルールのオプションが許可するよりも複雑な条件を組み込むことです。たとえば、コードベースで特別なコメントディレクティブを使用して、特定のスタイルシートのルールオプションをカスタマイズする場合があります。これらのディレクティブをチェックし、適切なオプションでルールを実行する(またはまったく実行しない)プラグインを構築できます。
すべてのルールは共通のシグネチャを共有します。それらは、主要なオプションと2番目のオプションオブジェクトを受け入れる関数です。そして、その関数は、PostCSSルートと結果を引数として期待するPostCSSプラグインのシグネチャを持つ関数を返します。
スタイルシートのどこかに特別なディレクティブ`@@check-declaration-no-important`がある場合にのみ`declaration-no-important`を実行するプラグインの例を次に示します。
createPlugin(ruleName, (primary) => {
const rulePromise = stylelint.rules["declaration-no-important"];
const ruleRunnner = rulePromise.then((rule) => rule(primary));
return async (root, result) => {
if (!root.toString().includes("@@check-declaration-no-important")) {
return;
}
(await ruleRunnner)(root, result);
};
});
主要なオプション配列を許可
プラグインが主要なオプションとして配列を受け入れることができる場合、ルール関数に`primaryOptionArray = true`プロパティを設定して指定する必要があります。詳細については、"ルールの作成"ドキュメントを参照してください。
ピア依存関係
プラグインの`package.json`の`peerDependencies`キー内(`dependencies`キー内ではない)に、プラグインで使用できるStylelintのバージョンを指定する必要があります。これは、異なるバージョンのStylelintが予期せずインストールされるのを防ぐためです。
たとえば、プラグインがStylelintバージョン14と15で使用できることを示すには、次のようになります。
{
"peerDependencies": {
"stylelint": "^14.0.0 || ^15.0.0"
}
}
プラグインパック
単一のモジュールで複数のルールを提供するには、(単一のオブジェクトではなく)プラグインオブジェクトの配列をエクスポートします。
プラグインとプラグインパックの共有
`package.json`内で`stylelint-plugin`キーワードを使用します。