编写插件
插件是自定义规则和自定义规则集。它们可能支持特定的方法或工具集,适用于非标准结构和功能,或用于特定用例。
我们建议您的自定义规则遵循我们的 规则约定,用于
- 名称
- 选项
- 消息
- 测试
- 文档
- 元数据
- 特定于结构的解析器
插件的结构
此示例插件禁止在选择器中使用“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
}
]
});
或者,您将在 精选 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?
:您正在调用的规则的 上下文
使用警告从您的插件规则创建新的警告,并使用 stylelint.utils.report()
报告。
例如,假设您想创建一个插件,该插件运行 at-rule-no-unknown
,并使用您选择的预处理器提供的 at-rules 内置异常列表
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
关键字。