跳至主要內容

撰寫外掛程式

外掛程式是自訂規則和自訂規則組。它們可能會支援特定方法或工具組,套用至非標準結構和功能,或用於特定使用案例。

我們建議您的自訂規則遵循我們的 規則慣例,以符合下列事項

  • 名稱
  • 選項
  • 訊息
  • 測試
  • 文件
  • 元資料
  • 結構特定解析器

外掛程式的結構

此範例外掛程式禁止在選擇器中使用「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...
};
};

測試

你可以使用

兩者都提供一個 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 的 警告選項(例如 wordindexstart 等)來詳細說明問題發生在哪裡。

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.jsonpeerDependencies 鍵中(而非 dependencies 鍵中)表達你的外掛可以搭配哪些 Stylelint 版本使用。這是為了確保不會意外安裝不同版本的 Stylelint。

例如,表達你的外掛可以搭配 Stylelint 版本 14 和 15 使用

{
"peerDependencies": {
"stylelint": "^14.0.0 || ^15.0.0"
}
}

外掛套件

要讓單一模組提供多個規則,請匯出外掛物件陣列(而非單一物件)。

分享外掛和外掛套件

在你的 package.json 中使用 stylelint-plugin 關鍵字。