Skip to main content

编写规则

¥Writing rules

请帮助我们创建、增强和调试我们的规则!

¥Please help us create, enhance, and debug our rules!

添加规则

¥Add a rule

你应该为 贡献代码 做好准备。

¥You should get yourself ready to contribute code.

定义规则

¥Define the rule

规则必须是:

¥A rule must be:

  • 仅适用于标准 CSS 语法

    ¥for standard CSS syntax only

  • 一般有用;不拘泥于特殊的模式

    ¥generally useful; not tied to idiosyncratic patterns

并有一个:

¥And have a:

  • 明确的完成状态

    ¥unambiguous finished state

  • 单一目的

    ¥singular purpose

它的名字分为两部分:

¥Its name is split into two parts:

  • 规则适用的 thing,例如 at-rule

    ¥the thing the rule applies to, e.g. at-rule

  • 规则正在检查什么,例如 disallowed-list

    ¥what the rule is checking, e.g. disallowed-list

除非它适用于整个源代码,否则就没有第一部分。

¥Unless it applies to the whole source, then there is no first part.

编写测试

¥Write tests

你应该为以下所有模式添加测试用例:

¥You should add test cases for all patterns that are:

  • 考虑的问题

    ¥considered problems

  • 不考虑的问题

    ¥not considered problems

你应该使用:

¥You should use:

  • 真实的 CSS,避免使用省略号

    ¥realistic CSS, avoiding the use of ellipses

  • 尽可能少的代码,例如 如果定位选择器,则使用空规则

    ¥the minimum amount of code possible, e.g. use an empty rule if targeting selectors

  • {} 表示空规则,而不是 { }

    ¥{} for empty rules, rather than { }

  • 默认为 a 类型选择器

    ¥the a type selector by default

  • 默认情况下 @media at 规则

    ¥the @media at-rules by default

  • 默认为 color 属性

    ¥the color property by default

  • 默认值 red

    ¥the red value by default

  • 默认 (min-)width 媒体功能

    ¥the (min-)width media feature by default

  • foo、bar 和 baz 表示名称,例如 .foo#bar--baz

    ¥foo, bar and baz for names, e.g. .foo, #bar, --baz

你应该:

¥You should:

  • 在测试中改变列和行的位置

    ¥vary column and line positions across your tests

  • 至少包括一项有 2 个警告的测试

    ¥include at least one test that has 2 warnings

  • isStandardSyntax* 实用程序中测试非标准语法,而不是在规则本身中测试

    ¥test non-standard syntax in the isStandardSyntax* utilities, not in the rule itself

经常被忽视的边缘情况

¥Commonly overlooked edge-cases

你应该问自己你的规则如何处理:

¥You should ask yourself how does your rule handle:

  • 变量(例如 var(--custom-property))?

    ¥variables (e.g. var(--custom-property))?

  • CSS 字符串(例如 content: "anything goes";)?

    ¥CSS strings (e.g. content: "anything goes";)?

  • CSS 注释(例如 /* anything goes */)?

    ¥CSS comments (e.g. /* anything goes */)?

  • 空函数(例如 var())?

    ¥empty functions (e.g. var())?

  • url() 函数,包括数据 URI(例如 url(anything/goes.jpg))?

    ¥url() functions, including data URIs (e.g. url(anything/goes.jpg))?

  • 浏览器前缀(例如 @-webkit-keyframes name {})?

    ¥vendor prefixes (e.g. @-webkit-keyframes name {})?

  • 区分大小写(例如 @KEYFRAMES name {})?

    ¥case sensitivity (e.g. @KEYFRAMES name {})?

  • 伪类与伪元素的组合(例如 a:hover::before)?

    ¥a pseudo-class combined with a pseudo-element (e.g. a:hover::before)?

  • 嵌套(例如,你是否解析 & a {},或按原样检查它?)?

    ¥nesting (e.g. do you resolve & a {}, or check it as is?)?

  • 空格和标点符号(例如比较 rgb(0,0,0)rgb(0, 0, 0))?

    ¥whitespace and punctuation (e.g. comparing rgb(0,0,0) with rgb(0, 0, 0))?

编写规则

¥Write the rule

编写规则时,你应该:

¥When writing the rule, you should:

  • 默认情况下使规则严格

    ¥make the rule strict by default

  • 添加辅助 ignore 选项以使规则更加宽松

    ¥add secondary ignore options to make the rule more permissive

  • 不包含特定于 SCSS 等语言扩展的代码

    ¥not include code specific to language extensions like SCSS

你应该利用:

¥You should make use of the:

  • PostCSS API

  • 构造特定的解析器

    ¥construct-specific parsers

  • 效用函数

    ¥utility functions

PostCSS API

使用 PostCSS API 导航和分析 CSS 语法树。我们建议使用 walk 迭代器(例如 walkDecls),而不是使用 forEach 来循环节点。

¥Use the PostCSS API to navigate and analyze the CSS syntax tree. We recommend using the walk iterators (e.g. walkDecls), rather than using forEach to loop through the nodes.

在节点上使用数组方法时,例如 findsomefilter 等,在尝试访问其他属性之前,你应该显式检查节点的 type 属性。例如:

¥When using array methods on nodes, e.g. find, some, filter etc, you should explicitly check the type property of the node before attempting to access other properties. For example:

const hasProperty = nodes.find(
({ type, prop }) => type === "decl" && prop === propertyName
);

PostCSS AST 访问原始字符串时,使用 node.raws 而不是 node.raw()

¥Use node.raws instead of node.raw() when accessing raw strings from the PostCSS AST.

特定于构造的解析器

¥Construct-specific parsers

根据规则,我们还建议使用:

¥Depending on the rule, we also recommend using:

使用这些解析器而不是正则表达式或 indexOf 搜索有显着的好处(即使它们并不总是最高效的方法)。

¥There are significant benefits to using these parsers instead of regular expressions or indexOf searches (even if they aren't always the most performant method).

实用函数

¥Utility functions

Stylelint 有 效用函数 在现有规则中使用,并且可能对你也有用。请仔细查看这些内容,以便了解可用的内容。(如果你有一个你认为可能普遍有用的新功能,让我们将其添加到列表中!)。

¥Stylelint has utility functions that are used in existing rules and might prove useful to you, as well. Please look through those so that you know what's available. (And if you have a new function that you think might prove generally helpful, let's add it to the list!).

使用:

¥Use the:

  • validateOptions() 实用程序警告用户无效选项

    ¥validateOptions() utility to warn users about invalid options

  • isStandardSyntax* 实用程序忽略非标准语法

    ¥isStandardSyntax* utilities to ignore non-standard syntax

添加选项

¥Add options

每个规则都可以接受一个主要选项和一个可选的辅助选项。

¥Each rule can accept a primary and an optional secondary option.

仅当规则解决了所请求的用例时才向规则添加选项,以避免未使用的功能污染工具。

¥Only add an option to a rule if it addresses a requested use case to avoid polluting the tool with unused features.

基本的

¥Primary

每条规则都必须有一个主要选项。例如,在:

¥Every rule must have a primary option. For example, in:

  • "font-weight-notation": "numeric",首选是 "numeric"

    ¥"font-weight-notation": "numeric", the primary option is "numeric"

  • "selector-max-type": [2, { "ignoreTypes": ["custom"] }],首选是 2

    ¥"selector-max-type": [2, { "ignoreTypes": ["custom"] }], the primary option is 2

规则的命名是为了鼓励明确的主要选项。例如,font-weight-notation: "numeric"|"named-where-possible" 而不是 font-weight-numeric: "always"|"never"。因为 font-weight-named: "never" 意味着始终是数字,而 font-weight-notation: "numeric" 则使其明确。

¥Rules are named to encourage explicit primary options. For example, font-weight-notation: "numeric"|"named-where-possible" rather than font-weight-numeric: "always"|"never". As font-weight-named: "never" implies always numeric, whereas font-weight-notation: "numeric" makes it explicit.

中学

¥Secondary

有些规则需要额外的灵活性来解决边缘情况。这些可以使用可选的辅助选项对象。例如,在:

¥Some rules require extra flexibility to address edge cases. These can use an optional secondary options object. For example, in:

  • "font-weight-notation": "numeric" 没有辅助选项对象

    ¥"font-weight-notation": "numeric" there is no secondary options object

  • "selector-max-type": [2, { "ignore": ["descendant] }],次要选项对象是 { "ignore": ["descendant] }

    ¥"selector-max-type": [2, { "ignore": ["descendant] }], the secondary options object is { "ignore": ["descendant] }

最典型的辅助选项是 "ignore": []"except": []

¥The most typical secondary options are "ignore": [] and "except": [].

关键字 "ignore""except"

¥Keyword "ignore" and "except"

"ignore""except" 选项接受一组预定义的关键字选项,例如 ["relative", "first-nested", "descendant"]

¥The "ignore" and "except" options accept an array of predefined keyword options, e.g. ["relative", "first-nested", "descendant"]:

  • "ignore" 跳过特定模式

    ¥"ignore" skips-over a particular pattern

  • "except" 反转特定模式的主要选项

    ¥"except" inverts the primary option for a particular pattern

用户自定义 "ignore*"

¥User-defined "ignore*"

某些规则接受用户定义的要忽略的事项列表。这采用 "ignore<Things>": [] 的形式,例如 "ignoreAtRules": []

¥Some rules accept a user-defined list of things to ignore. This takes the form of "ignore<Things>": [], e.g. "ignoreAtRules": [].

ignore* 选项允许用户在配置级别忽略非标准语法。例如:

¥The ignore* options let users ignore non-standard syntax at the configuration level. For example, the:

  • CSS 模块中引入的 :global:local 伪类

    ¥:global and :local pseudo-classes introduced in CSS Modules

  • SCSS 中引入的 @debug@extend at 规则

    ¥@debug and @extend at-rules introduced in SCSS

方法论和语言扩展来得快去得也快,这种方法可以确保我们的代码库不会充斥着过时的代码。

¥Methodologies and language extensions come and go quickly, and this approach ensures our codebase does not become littered with code for obsolete things.

如果你的规则可以接受数组作为其主要选项,则必须通过在规则函数上设置属性 primaryOptionArray = true 来指定它。例如:

¥If your rule can accept an array as its primary option, you must designate this by setting the property primaryOptionArray = true on your rule function. For example:

function rule(primary, secondary) {
return (root, result) => {
/* .. */
};
}

rule.primaryOptionArray = true;

module.exports = rule;

这里有一个警告:如果你的规则接受主选项数组,则它也不能接受主选项对象。只要有可能,如果你希望你的规则接受主要选项数组,你应该使数组成为唯一的可能性,而不是允许各种数据结构。

¥There is one caveat here: If your rule accepts a primary option array, it cannot also accept a primary option object. Whenever possible, if you want your rule to accept a primary option array, you should make an array the only possibility, instead of allowing for various data structures.

添加问题消息

¥Add problem messages

以以下形式添加问题消息:

¥Add problem messages in form of:

  • "预期的[某事][在某些情况下]"

    ¥"Expected [something] [in some context]"

  • "意想不到的[某事][在某些情况下]"

    ¥"Unexpected [something] [in some context]"

如果规则具有自动修复功能,请使用:

¥If the rule has autofix use:

  • '预计 "[未修复]" 为 "[固定的]"' 用于短字符串

    ¥'Expected "[unfixed]" to be "[fixed]"' for short strings

  • '预计 "[基本的]"...长字符串的符号'

    ¥'Expected "[primary]" ... notation' for long strings

添加自动修复

¥Add autofix

根据规则,可以通过使用 PostCSS API 改变 PostCSS AST(抽象语法树)来自动修复规则的问题。

¥Depending on the rule, it might be possible to automatically fix the rule's problems by mutating the PostCSS AST (Abstract Syntax Tree) using the PostCSS API.

meta.fixable = true 设置为规则:

¥Set meta.fixable = true to the rule:

const meta = {
url: /* .. */,
+ fixable: true,
};

fix 回调传递给 report 实用程序

¥Pass fix callback to the report utility:

function rule(primary, secondary) {
return (root, result) => {
/* .. */

+ const fix = () => { /* put your mutations here */ };

report({
result,
ruleName,
message,
node,
+ fix
});
};
}

上下文

¥Context

context 是一个可以具有三个属性的对象:

¥context is an object which could have three properties:

  • configurationComment(字符串):配置注释的前缀字符串,例如 /* stylelint-disable */

    ¥configurationComment(string): String that prefixes configuration comments like /* stylelint-disable */.

  • fix(布尔值):如果 true,你的规则可以应用自动修复。

    ¥fix(boolean): If true, your rule can apply autofixes.

  • newline(字符串):当前 linted 文件中使用的行结束符。

    ¥newline(string): Line-ending used in current linted file.

[!WARNING] 限制基于 context.fix 属性的修复应用的惯例已被弃用,取而代之的是推荐正确处理 配置注释fix 回调。

¥[!WARNING] The convention of restricting the appliance of fixes based on the context.fix property is deprecated in favour of recommending the fix callback which properly handles configuration comments.

如果 context.fixtrue,则使用 PostCSS API 更改 root,并在调用 report() 之前提前返回。

¥If context.fix is true, then change root using PostCSS API and return early before report() is called.

function rule(primary, secondary, context) {
return (root, result) => {
if (context.fix) {
// Apply fixes using PostCSS API
return; // Return and don't report a problem
}

report(/* .. */);
};
}

编写自述文件

¥Write the README

每条规则都附有以下格式的自述文件:

¥Each rule is accompanied by a README in the following format:

  1. 规则名称。

    ¥Rule name.

  2. 单行描述。

    ¥Single-line description.

  3. 原型代码示例。

    ¥Prototypical code example.

  4. 扩展描述(如有必要)。

    ¥Expanded description (if necessary).

  5. 选项。

    ¥Options.

  6. 被视为问题的示例模式(对于每个选项值)。

    ¥Example patterns that are considered problems (for each option value).

  7. 不被视为问题的示例模式(对于每个选项值)。

    ¥Example patterns that are not considered problems (for each option value).

  8. 可选选项(如果适用)。

    ¥Optional options (if applicable).

单行描述的形式为:

¥The single-line description is in the form of:

  • "不允许 ..." 对应 no 规则

    ¥"Disallow ..." for no rules

  • "限制 ..." 对应 max 规则

    ¥"Limit ..." for max rules

  • "要求 ..." 表示接受 "always""never" 选项的规则

    ¥"Require ..." for rules that accept "always" and "never" options

  • "指定 ..." 代表其他一切

    ¥"Specify ..." for everything else

你应该:

¥You should:

  • 从测试中选取示例

    ¥pick examples from the tests

  • 仅在示例和选项中使用标准 CSS 语法

    ¥only use standard CSS syntax in examples and options

  • 添加尽可能少的示例来传达规则的意图,而不是显示边缘情况

    ¥add the fewest examples possible to communicate the intent of the rule, rather than show edge cases

  • css 代码围栏之前使用 <!-- prettier-ignore -->

    ¥use <!-- prettier-ignore --> before css code fences

  • 使用 "这条规则" 来引用规则,例如 "该规则忽略了..."

    ¥use "this rule" to refer to the rule, e.g. "This rule ignores ..."

  • 将原型代码示例中的箭头与高亮的结构的开头对齐

    ¥align the arrows within the prototypical code example with the beginning of the highlighted construct

  • 将原型代码示例中的文本尽可能向左对齐

    ¥align the text within the prototypical code example as far to the left as possible

例如:

¥For example:

 @media screen and (min-width: 768px) {}
/** ↑ ↑

* These names and values */

查看其他规则的自述文件以收集更传统的模式。

¥Look at the READMEs of other rules to glean more conventional patterns.

连接规则

¥Wire up the rule

最后一步是在以下位置添加对新规则的引用:

¥The final step is to add references to the new rule in the following places:

向规则添加选项

¥Add an option to a rule

你应该:

¥You should:

  1. 准备好 贡献代码

    ¥Get ready to contribute code.

  2. 添加新的单元测试来测试该选项。

    ¥Add new unit tests to test the option.

  3. 更改规则的验证以允许新选项。

    ¥Change the rule's validation to allow for the new option.

  4. 向规则添加(尽可能少的)逻辑以使测试通过。

    ¥Add (as little as possible) logic to the rule to make the tests pass.

  5. 添加有关新选项的文档。

    ¥Add documentation about the new option.

修复规则中的错误

¥Fix a bug in a rule

你应该:

¥You should:

  1. 准备好 贡献代码

    ¥Get ready to contribute code.

  2. 编写失败的单元测试来举例说明该错误。

    ¥Write failing unit tests that exemplify the bug.

  3. 修改规则直到这些新测试通过。

    ¥Fiddle with the rule until those new tests pass.

弃用规则

¥Deprecate a rule

废弃规则并不经常发生。当你这样做时,你必须:

¥Deprecating rules doesn't happen very often. When you do, you must:

  1. 添加适当的元数据以将规则标记为已弃用,如下所示:rule.meta = { deprecated: true }

    ¥Add the appropriate metadata to mark the rule as deprecated like so: rule.meta = { deprecated: true }.

  2. stylelintType 设置为 'deprecation'

    ¥Set the stylelintType to 'deprecation'.

  3. 可选择将 stylelintReference 设置为指向规则文档特定版本的链接,以便始终保持可访问。

    ¥Optionally set stylelintReference to a link that points to a specific version of the rule's document so that it always remains accessible.

例如:

¥For example:

result.warn(
`"your-namespace/old-rule" has been deprecated and will be removed in 7.0. Use "your-namespace/new-rule" instead.`,
{
stylelintType: "deprecation",
stylelintReference:
"https://github.com/your-org/your-stylelint-plugin/blob/v6.3.0/src/rules/old-rule/README.md"
}
);

提高规则的性能

¥Improve the performance of a rule

你可以使用任何有效配置对任何给定规则运行基准测试:

¥You can run a benchmark on any given rule with any valid config using:

npm run benchmark-rule -- ruleName ruleOptions [config]

如果 ruleOptions 参数不是字符串或布尔值,则它必须是用引号引起来的有效 JSON。

¥If the ruleOptions argument is anything other than a string or a boolean, it must be valid JSON wrapped in quotation marks.

npm run benchmark-rule -- value-keyword-case lower
npm run benchmark-rule -- value-keyword-case '["lower", {"camelCaseSvgKeywords": true}]'

如果指定了 config 参数,则将应用相同的过程:

¥If the config argument is specified, the same procedure would apply:

npm run benchmark-rule -- value-keyword-case '["lower", {"camelCaseSvgKeywords": true}]' '{"fix": true}'

该脚本加载 Bootstrap 的 CSS(从其 CDN)并通过配置的规则运行它。

¥The script loads Bootstrap's CSS (from its CDN) and runs it through the configured rule.

它最终会打印一些简单的统计数据,如下所示:

¥It will end up printing some simple stats like this:

Warnings: 1441
Mean: 74.17598357142856 ms
Deviation: 16.63969674310928 ms

在编写新规则或重构现有规则时,请使用这些测量来确定代码的效率。

¥When writing new rules or refactoring existing rules, use these measurements to determine the efficiency of your code.

Stylelint 规则可以多次重复其核心逻辑(例如,检查庞大 CSS 代码库中每个声明的每个值节点)。因此,值得关注性能并尽我们所能来改进它!

¥A Stylelint rule can repeat its core logic many, many times (e.g. checking every value node of every declaration in a vast CSS codebase). So it's worth paying attention to performance and doing what we can to improve it!

如果你想要一个快速的小项目,那么提高规则的性能是一个很好的贡献方式。尝试选择一个规则,看看是否可以采取任何措施来加快速度。

¥Improving the performance of a rule is a great way to contribute if you want a quick little project. Try picking a rule and seeing if there's anything you can do to speed it up.

确保在拉取请求中包含基准测量!

¥Make sure you include benchmark measurements in your pull request!