Stylelintselector-combinator-allowed-list规则完全指南:用白名单约束选择器组合器
【免费下载链接】stylelintA mighty CSS linter that helps you avoid errors and enforce conventions.项目地址: https://gitcode.com/gh_mirrors/st/stylelint
selector-combinator-allowed-list是 Stylelint 内置的"选择器组合器白名单"规则,用于限定样式表中允许出现的组合器(如>、+、~或后代空格),从而从源头约束选择器的书写习惯与可维护性。本文以该规则的官方文档(lib/rules/selector-combinator-allowed-list/README.md)为主体,结合仓库中的规则实现、工具函数与测试用例,讲解它的行为特性、配置方法、底层原理以及与selector-combinator-disallowed-list的搭配使用,帮助你把它准确落地到实际项目中。
规则概述:它检查什么
selector-combinator-allowed-list的功能一句话概括:指定一个允许使用的组合器列表(allowed list)。凡是出现在选择器中的组合器,只要不在该列表中,就会被报告为违规。
CSS 中的组合器主要包括:
| 组合器 | 写法 | 含义 |
|---|---|---|
| 后代组合器 | 空格() | 匹配后代元素,如a b |
| 子组合器 | > | 匹配直接子元素,如a > b |
| 相邻兄弟组合器 | + | 匹配紧邻的下一个兄弟,如a + b |
| 通用兄弟组合器 | ~ | 匹配后续所有兄弟,如a ~ b |
| 列组合器 | \|\| | 匹配列(Selectors 4 中定义,兼容性有限) |
| 引用组合器(reference) | /for/等 | 用于 ID 引用语法,属非标准或特定场景 |
规则文档用如下代码片段直观标注了检查目标:
a + b {} /** ↑ * This combinator */箭头所指的+就是本规则检查的对象。例如配置只允许>和空格时,a + b {}与a ~ b {}会被报告,而a > b {}、a b {}不会。
三条关键行为特性
除了"白名单过滤"这一核心逻辑,规则文档明确声明了三项行为约定,理解它们才能避免配置踩坑。
1. 后代组合器的空白会被规范化为单个空格
This rule normalizes the whitespace descendant combinator to be a single space.
选择器中的后代组合器本质上是"空白",但书写形式可能是一个空格、多个空格、制表符(Tab)甚至换行。规则在比较之前,会先把任意形式的空白统一规范为单个空格再与配置项比对。这意味着:
- 配置
" "(一个空格)时,a b、a b、a\tb、a\nb都算作使用了后代组合器,统一按"空格"处理; - 反过来,如果白名单里不含空格,那么任何形式的后代组合器(包括换行分隔)都会被视为违规。
该行为由 normalizeCombinator.mjs 实现,实现只有一行正则替换:
export default function normalizeCombinator(value) { return value.replace(/\s+/g, ' '); }2. 引用组合器(reference combinators)被忽略
This rule ignores reference combinators e.g.
/for/.
形如/for/、/deep/这样被/包裹的引用组合器(见 W3C Selectors Level 4 的 idref combinators 规范),不会被本规则检查。也就是说,即使白名单中没有它们,也不会被报告。
判定逻辑位于 isStandardSyntaxCombinator.mjs:只要组合器节点的value以/开头或以/结尾,就直接判定为非标准组合器并跳过:
// Ignore reference combinators like `/deep/` if (node.value.startsWith('/') || node.value.endsWith('/')) { return false; }3. 支持 1 个消息参数:被禁止的组合器
规则文档注明:
This rule supports 1 message argument: the disallowed combinator.
即使用自定义message时,可以通过消息参数把"被拒绝的组合器"注入到提示文案中。结合 docs/user-guide/configure.md 中关于message次要选项的说明,可以这样配置:
/** @type {import('stylelint').Config} */ export default { rules: { "selector-combinator-allowed-list": [ [">", " "], { message: (combinator) => `组合器 "${combinator}" 不在允许列表中,请改用 ">" 或后代空格` } ] } };如果配置文件是 JSON(不支持函数),可以使用printf风格的占位符:
{ "rules": { "selector-combinator-allowed-list": [ [">", " "], { "message": "Disallowed combinator \"%s\"" } ] } }消息参数的值来自源码中messages.rejected的定义(lib/rules/selector-combinator-allowed-list/index.mjs):
const messages = ruleMessages(ruleName, { rejected: (combinator) => `Disallowed combinator "${combinator}"`, });注意:注入的是规范化后的组合器值(即空白已被替换为单个空格),所以触发a\nb {}时消息中的参数是" "而不是原始换行。
选项配置:Array<string>
规则的唯一主要选项(primary option)是一个字符串数组,枚举所有允许出现的组合器:
["array", "of", "combinators"]例如,只允许子组合器和后代组合器:
{ "rules": { "selector-combinator-allowed-list": [">", " "] } }被视为问题的写法(Problems)
a + b {}a ~ b {}因为+与~都不在白名单[">", " "]中。
不被视为问题的写法(Not considered problems)
a > b {}a b {}a b {}最后一个例子值得注意:a与b分处两行,但它是合法的后代组合器写法,经空白规范化后等价于单个空格,因此在白名单允许范围内,不构成问题。
该规则还设置了rule.primaryOptionArray = true(index.mjs),并从 lib/rules/index.mjs 的规则注册表中可以看到它与selector-combinator-disallowed-list成对存在。
源码级原理:一条完整的检查链路
阅读 index.mjs 的实现,可以完整还原规则的执行流程,核心链路如下:
- 选项校验:
validateOptions校验主要选项,要求数组内每个元素都是字符串(possible: [isString])。校验失败则直接返回,不产生任何报告。 - 遍历规则节点:
root.walkRules遍历样式树中的每一条 rule。 - 过滤非标准规则:调用 isStandardSyntaxRule.mjs,跳过 Less 的
&:extend规则以及包含插值(如 SCSS/Stylus 插值语法)等非标准选择器的规则。 - 解析选择器:通过 parseSelector.mjs 使用
postcss-selector-parser对选择器做 AST 解析;解析失败时记录parseError并跳过。获取原始选择器文本的是 getRuleSelector.mjs,它会优先使用raws.selector.raw(保留源码中的原始空白),保证换行、Tab 等信息不丢失。 - 遍历组合器:
walkCombinators逐个取出选择器中的组合器节点。 - 过滤非标准组合器:调用
isStandardSyntaxCombinator,除忽略引用组合器外,还忽略位于容器首部或尾部的组合器节点(这些位置不构成真实的关系连接)。 - 规范化并比对:
normalizeCombinator(value)将空白压成单个空格,然后判断primary.includes(normalizedValue)——不在白名单中即触发报告。 - 精确定位报告:报告位置使用
combinatorNode.sourceIndex作为起点,终点通过index + (raws?.value || value).length计算(index.mjs),确保编辑器中的高亮范围准确落在组合器字符本身。
从代码结构可以推断,规则的报告发生在 rule 节点上,并携带精确的index/endIndex,这与测试断言中的行列号(如line: 1, column: 3)完全对应。
与selector-combinator-disallowed-list的对照使用
本规则有一个语义完全相反的孪生规则 selector-combinator-disallowed-list(指定禁止的组合器列表)。两者的源码结构几乎一致,唯一差异在第 47 行附近的判定逻辑:allowed-list 在primary.includes(normalizedValue)为true时放行、为false时报告;disallowed-list 则恰好相反,命中列表即报告。
| 对比项 | selector-combinator-allowed-list | selector-combinator-disallowed-list |
|---|---|---|
| 语义 | 允许列表,未列出即违规 | 禁止列表,列出即违规 |
| 示例配置 | [">", " "] | [">", " "] |
a > b {} | 不报告 | 报告 |
a b {} | 不报告 | 报告 |
a + b {} | 报告 | 不报告 |
| 空白规范化 | 相同 | 相同 |
| 忽略引用组合器 | 相同 | 相同 |
两条规则的默认消息文案也都是Disallowed combinator "…",均支持 1 个消息参数。选择哪一条取决于你的风格策略:希望"只许用某几种"就选 allowed-list;希望"明令禁用某几种"(例如禁止深嵌套常用的>或禁止~依赖)就选 disallowed-list。
实战配置建议
基础接入(配置文件)
Stylelint 从当前目录向上查找stylelint.config.js(也支持.mjs、.cjs、.ts,详见 docs/user-guide/configure.md)。最小可用配置如下:
/** @type {import('stylelint').Config} */ export default { rules: { "selector-combinator-allowed-list": [">", " "] } };常用组合器取值参考
- 后代组合器:配置值始终写单个空格
" ",因为任何空白都会被规范化; - 子组合器:
">"; - 相邻兄弟:
"+"; - 通用兄弟:
"~"; - 列组合器:
"||"(如需支持)。
调整严重级别
借助severity次要选项,可以把违规从默认的"error"降级为"warning",适合渐进式推行阶段:
{ "rules": { "selector-combinator-allowed-list": [ [">", " "], { "severity": "warning" } ] } }局部豁免
与其他规则一致,该规则也支持stylelint-disable系列注释。测试用例(tests/index.mjs)验证了stylelint-disable-next-line的豁免能力:
/* stylelint-disable-next-line selector-combinator-allowed-list */ a + b {}测试用例印证
仓库为该规则提供了完整的测试覆盖(lib/rules/selector-combinator-allowed-list/tests/index.mjs),可以用这些用例来验证你对规则行为的理解:
配置['>', ' ']时,接受(accept)的写法包括:
a {}(无组合器)、a, b {}(逗号分隔,非组合器)a /for/ b {}(引用组合器被忽略)a > b {}、a:not(b > c) {}(组合器出现在:not()内同样会被检查)a b {}、a\tb {}、a\nb {}(各种空白形式的后代组合器)- 带
stylelint-disable-next-line注释的a + b {}
拒绝(reject)的写法包括:
a ~ b {},报错Disallowed combinator "~",位置1:3至1:4a:not(b ~ c) {},位置1:9至1:10(证明伪类内嵌套选择器也会被递归检查)- 多行
a,\nb + c {},第二行2:3处报告+
另一个测试用例使用字符串配置config: '~'验证了反向场景:a b {}、a\tb {}、a\n\tb {}全部因后代空格不在白名单中而报告Disallowed combinator " "。这说明尽管规则文档以数组为示例形式,选项校验对"单个字符串"同样兼容,配置时应优先使用数组以保持一致性。
小结
selector-combinator-allowed-list是 Stylelint 组合器管理家族中的"白名单"成员,与selector-combinator-disallowed-list互补。它通过空白规范化、引用组合器豁免、消息参数支持与精确的源码定位,让团队能够以声明式配置统一选择器书写风格。若要深入其实现细节,建议从 规则实现、空白规范化工具、标准组合器判定 三个文件入手,并结合 测试用例 验证边界行为。
【免费下载链接】stylelintA mighty CSS linter that helps you avoid errors and enforce conventions.项目地址: https://gitcode.com/gh_mirrors/st/stylelint
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考