撰寫外掛程式
外掛程式是自訂規則和自訂規則組。它們可能會支援特定方法或工具組,套用至非標準結構和功能,或用於特定使用案例。
我們建議您的自訂規則遵循我們的 規則慣例,以符合下列事項
- 名稱
- 選項
- 訊息
- 測試
- 文件
- 元資料
- 結構特定解析器
外掛程式的結構
此範例外掛程式禁止在選擇器中使用「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 個參數
- 主要選項
- 選擇性地,次要選項物件
如果你的外掛規則支援 自動修正,則 ruleFunction
也應接受第三個參數: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 提供了一些有用的工具程式。
你也可以將任何 內部工具程式 複製到你的外掛程式中。你不應該直接 import
它們,因為它們不是公開 API 的一部分,可能會在沒有警告的情況下變更或移除。
stylelint.utils.report()
將你的外掛程式中的問題新增到 Stylelint 會向使用者報告的問題清單中。
使用 stylelint.utils.report()
來確保你的外掛程式尊重已停用的範圍和 Stylelint 的其他可能的未來功能。
不要直接使用 PostCSS 的 Node#warn()
方法。
你可以指定 PostCSS 的 警告選項(例如 word
、index
、start
等)來詳細說明問題發生在哪裡。
stylelint.utils.ruleMessages()
將你的訊息調整為標準 Stylelint 規則的格式。
stylelint.utils.validateOptions()
驗證你的規則選項。
stylelint.utils.checkAgainstRule()
在你的規則中檢查 CSS 是否符合標準或自訂 Stylelint 規則。此函式為希望修改、限制或擴充現有 Stylelint 規則功能的外掛程式作者提供強大且靈活的功能。
這是一個非同步函數。您的自訂規則可能需要等到函數傳回的 Promise
已解決。
它接受一個選項物件和一個回呼,並使用指定規則的警告呼叫它。選項為
ruleName
:您呼叫的規則名稱ruleSettings
:您呼叫的規則設定root
:執行此規則的根節點result?
:用於解決和呼叫自訂規則的 PostCSS 結果context?
:您呼叫的規則的 context
使用警告從您的外掛程式規則建立一個新的警告,並使用 stylelint.utils.report()
報告。
例如,想像一下您想建立一個外掛程式,以內建的 at-rule 例外清單執行 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
。
典型的使用案例是建立比規則選項允許的更複雜的條件。例如,您的程式碼庫可能使用特殊的註解指令,以自訂特定樣式表的規則選項。您可以建立一個外掛程式,檢查這些指令,然後使用正確的選項執行適當的規則(或根本不執行它們)。
所有規則都共用一個常見的簽章。它們是一個函數,接受兩個引數:主要選項和次要選項物件。而該函數傳回一個函數,其簽章為 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
關鍵字。