跳至主要内容

编写插件

插件是自定义规则和自定义规则集。它们可能支持特定的方法或工具集,适用于非标准结构和功能,或用于特定用例。

我们建议您的自定义规则遵循我们的 规则约定,用于

  • 名称
  • 选项
  • 消息
  • 测试
  • 文档
  • 元数据
  • 特定于结构的解析器

插件的结构

此示例插件禁止在选择器中使用“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
}
]
});

或者,您将在 精选 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?:您正在调用的规则的 上下文

使用警告从您的插件规则创建新的警告,并使用 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 关键字。