编写插件
¥Writing plugins
插件是自定义规则和自定义规则集。它们可能支持特定的方法或工具集,适用于非标准构造和功能,或者适用于特定用例。
¥Plugins are custom rules and sets of custom rules. They might support a particular methodology or toolset, apply to non-standard constructs and features, or be for specific use cases.
我们建议你的自定义规则遵守我们的 规则约定:
¥We recommend your custom rules adhere to our rule conventions for:
-
名字
¥names
-
选项
¥options
-
消息
¥messages
-
测试
¥tests
-
文档
¥docs
-
metadata
-
构造特定的解析器
¥construct-specific parsers
插件的剖析
¥The anatomy of a plugin
此示例插件不允许在选择器中使用单词 "foo":
¥This example plugin disallows the word "foo" in selectors:
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);
[!NOTE] The
@type
JSDoc annotation enables Typescript to autocomplete and type-check.
用法是:
¥The usage would be:
{
"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
。你应该记录插件的规则名称(和命名空间),因为用户需要在其配置中使用它们。
¥Your plugin's rule name must be namespaced, e.g. your-namespace/your-rule-name
, to ensure it never clashes with the built-in rules. If your plugin provides only a single rule or you can't think of a good namespace, you can use plugin/my-rule
. You should document your plugin's rule name (and namespace) because users need to use them in their config.
使用 stylelint.createPlugin(ruleName, ruleFunction)
确保你的插件与其他规则一起正确设置。
¥Use stylelint.createPlugin(ruleName, ruleFunction)
to ensure that your plugin is set up properly alongside other rules.
为了使你的插件规则能够与 标准配置格式、ruleFunction
一起使用,应接受 2 个参数:
¥For your plugin rule to work with the standard configuration format, ruleFunction
should accept 2 arguments:
-
主要选项
¥the primary option
-
可选的,辅助选项对象
¥optionally, a secondary options object
如果你的插件规则支持 autofixing,那么 ruleFunction
还应该接受第三个参数:context
。
¥If your plugin rule supports autofixing, then ruleFunction
should also accept a third argument: context
.
ruleFunction
应该返回一个本质上有点 PostCSS 插件 的函数。它需要 2 个参数:
¥ruleFunction
should return a function that is essentially a little PostCSS plugin. It takes 2 arguments:
-
PostCSS 根(已解析的 AST)
¥the PostCSS Root (the parsed AST)
-
PostCSS LazyResult
你必须 了解 PostCSS API。
¥You'll have to learn about the PostCSS API.
异步规则
¥Asynchronous rules
你可以将插件编写为异步函数来处理 Promise
。
¥You can write your plugin as an async function to deal with 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...
};
};
测试
¥Testing
You can use either:
-
stylelint-test-rule-node
(node:test
based) -
jest-preset-stylelint
(Jest based)
Both expose a testRule
function that you can use to efficiently test your plugin using a schema.
例如:
¥For example:
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
}
]
});
Alternatively, you'll find more testing options in 很棒的 Stylelint.
¥Alternatively, you'll find more testing options in Awesome Stylelint.
If your plugin involves more than just checking syntax, you can use Stylelint directly.
例如:
¥For example:
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 公开了一些有用的实用程序。
¥Stylelint exposes some useful utilities.
也欢迎你将任何 内部工具 复制到你的插件中。你不应该直接对它们进行 import
,因为它们不是公共 API 的一部分,并且可能会在没有警告的情况下更改或删除。
¥You're also welcome to copy any of the internal utils into your plugin. You should not import
them directly, as they are not part of the public API and may change or be removed without warning.
stylelint.utils.report()
将插件中的问题添加到 Stylelint 将向用户报告的问题列表中。
¥Adds problems from your plugin to the list of problems that Stylelint will report to the user.
使用 stylelint.utils.report()
确保你的插件尊重禁用范围和 Stylelint 未来可能的其他功能。
¥Use stylelint.utils.report()
to ensure your plugin respects disabled ranges and other possible future features of Stylelint.
[!IMPORTANT] Do not use PostCSS's
Node#warn()
method directly.
你可以指定 PostCSS 的 警告选项(例如 word
、index
、start
等)来详细说明问题发生的位置。
¥You can specify PostCSS's warning options (e.g., word
, index
, start
, etc.) to detail where a problem occurs.
stylelint.utils.ruleMessages()
根据标准 Stylelint 规则的格式定制你的消息。
¥Tailors your messages to the format of standard Stylelint rules.
stylelint.utils.validateOptions()
验证你的规则的选项。
¥Validates the options for your rule.
stylelint.utils.checkAgainstRule()
根据你自己的规则中的标准或自定义 Stylelint 规则检查 CSS。此函数为 希望修改、限制或扩展现有 Stylelint 规则功能的插件作者提供了强大的功能和灵活性。
¥Checks CSS against a standard or custom Stylelint rule within your own rule. This function provides power and flexibility for plugins authors who wish to modify, constrain, or extend the functionality of existing Stylelint rules.
[!NOTE] This is an async function.你的自定义规则可能需要等待函数返回的
Promise
得到解析。¥[!NOTE] This is an async function. Your custom rule may need to wait until a
Promise
returned by the function is resolved.
它接受一个选项对象和一个回调,该回调是通过来自指定规则的警告来调用的。选项有:
¥It accepts an options object and a callback that is invoked with warnings from the specified rule. The options are:
-
ruleName
:你正在调用的规则的名称¥
ruleName
: the name of the rule you are invoking -
ruleSettings
:你调用的规则的设置¥
ruleSettings
: settings for the rule you are invoking -
root
:运行此规则的根节点¥
root
: the root node to run this rule against -
result?
:用于解析和调用自定义规则的 PostCSS 结果¥
result?
: the PostCSS result for resolving and invoking custom rules -
context?
:你正在调用的规则的 context¥
context?
: the context for the rule you are invoking
使用警告从你使用 stylelint.utils.report()
报告的插件规则创建新警告。
¥Use the warning to create a new warning from your plugin rule that you report with stylelint.utils.report()
.
例如,假设你想要创建一个运行 at-rule-no-unknown
的插件,并带有由你选择的预处理器提供的 at 规则的内置例外列表:
¥For example, imagine you want to create a plugin that runs at-rule-no-unknown
with a built-in list of exceptions for at-rules provided by your preprocessor-of-choice:
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
对象中使用。这使你可以在现有规则的基础上构建以满足你的特定需求。
¥All of the rule functions are available at the stylelint.rules
object. This allows you to build on top of existing rules for your particular needs.
[!NOTE] Every value in the
stylelint.rules
object is aPromise
resolving a rule function.