Stylelint `layer-name-pattern` 规则详解:为 CSS 级联层(Cascade Layers)命名建立统一规范
2026/9/23 19:49:51 网站建设 项目流程

Stylelintlayer-name-pattern规则详解:为 CSS 级联层(Cascade Layers)命名建立统一规范

【免费下载链接】stylelintA mighty CSS linter that helps you avoid errors and enforce conventions.项目地址: https://gitcode.com/gh_mirrors/st/stylelint

layer-name-pattern是 Stylelint 内置的一条命名约定类规则,用于为 CSS 级联层(Cascade Layers)的名称指定统一的命名模式(pattern)。当项目使用@layer@import ... layer(...)组织样式层级时,通过该规则可以强制层名遵循团队约定的正则规范(例如统一的小写连字符风格),从而保证代码风格一致、提升可维护性。读完本文,你将掌握该规则的完整配置方式、正则模式编写技巧、两类被检测语法(@layer@importlayer()函数)的边界行为,以及它背后的源码实现原理与测试用例验证。

规则概述

layer-name-pattern要求 CSS 中**层名(layer name)**必须匹配配置的正则模式。这里的"层名"指以下两种语法中出现的名字:

  • @layer foo {}@layer foo;中紧跟在@layer后面的名称;
  • @import "foo.css" layer(bar);layer()函数括号内的名称。
@layer foo {} /** ↑ * This layer name */

该规则在 Stylelint 内置规则索引 lib/rules/index.mjs 中注册,规则名为layer-name-pattern,其元信息定义在 lib/rules/layer-name-pattern/index.mjs 中。

注意:本规则只负责命名格式检查,不校验层名是否合法(例如保留关键字或 CSS 标识符规则),只校验其是否与配置的正则匹配。

选项(Options)

string

配置一个不带/包围符的正则字符串,作为string类型的单一主选项。

例如,要求层名必须以小写字母开头,且只能包含小写字母、数字、点(.)和连字符(-):

{ "rules": { "layer-name-pattern": "^[a-z][a-z0-9.-]*$" } }

会被视为问题的写法(violation)

@layer Foo;
@layer foo.Bar {}
@layer foo, Bar {}
@import "foo.css" layer(Bar);

以上四种写法分别对应:大写开头的@layer声明、包含大写字母的点分嵌套层名、多名称列表中的大写层名、以及@importlayer()函数中出现不符合模式的层名。

不会被视为问题的写法(pass)

@layer foo;
@layer foo.bar {}
@layer foo, bar {}
@import "foo.css" layer(bar);

这些写法全部满足^[a-z][a-z0-9.-]*$:小写开头、全小写、允许点号和连字符。

高级:直接配置正则对象

从源码 lib/rules/layer-name-pattern/index.mjs 可以看出,规则的主选项校验同时接受isRegExpisString两种类型。因此在使用 JS 格式的配置文件(如.stylelintrc.mjsstylelint.config.mjs)时,可以直接传入RegExp对象,省去字符串转义:

export default { rules: { 'layer-name-pattern': /^[a-z][a-z0-9-]*$/, }, };

该测试用例同样被 lib/rules/layer-name-pattern/tests/index.mjs 中的config: /^[a-z][a-z0-9-]*$/所验证。若传入的是字符串,规则内部会先执行new RegExp(primary)完成转换(见 index.mjs)。

自定义提示消息(message二次选项)

本规则支持 message 二次选项,并且带有2 个 message 参数:第 1 个是实际的层名(name),第 2 个是配置的模式(pattern)。这意味这你既可以用%s占位符,也可以在 JS 配置中使用函数动态拼接消息:

export default { rules: { 'layer-name-pattern': [ '^[a-z][a-z0-9.-]*$', { message: (name, pattern) => `Layer name "${name}" must match pattern "${pattern}"`, }, ], }, };

规则的默认消息定义在 index.mjs:

expected: (name, pattern) => `Expected "${name}" to match pattern "${pattern}"`,

注意:这里消息参数是在字符串/正则被转换为模式后messages.expected统一格式化输出的,测试快照中的消息形如Expected "Foo" to match pattern "/^[a-z][a-z0-9-]*$/"(见 测试文件)。

检测范围与边界行为

与部分命名类规则只检查单一语法不同,该规则的检测范围覆盖两条路径,对应源码中的两次遍历(index.mjs):

1.@layer规则

规则通过root.walkAtRules(atRuleRegexes.layerName, ...)遍历所有@layer开头的 at-rule,其中atRuleRegexes.layerName定义在 lib/utils/regexes.mjs,即/^layer$/i(大小写不敏感)。随后使用postcss-value-parser@layer的参数做词法解析,逐个检查其中的"单词"节点。

  • 空的@layer {}没有参数,规则会直接跳过(if (!params) return;),不会产生问题——这在测试的 accept 用例中也有体现(测试文件)。
  • 层名列表@layer foo, bar {}逐个名称独立检查foo合法而Bar不合法时,只会针对Bar报告问题,且两者都非法时会分别报告两条独立警告(见测试用例 L50-L68)。

2.@importlayer()函数

@import "foo.css" layer(bar)也是级联层命名的重要来源。规则通过root.walkAtRules(atRuleRegexes.importName, ...)遍历@import规则,先用mayIncludeRegexes.layerFunction(即/\blayer\(/i,见 regexes.mjs)做一次快速预筛,只有参数中疑似包含layer(@import才会进入value-parser的完整解析,从而避免对每个@import都做无谓的词法分析、提升性能。

解析时只认名称为layer(大小写不敏感)的函数节点isValueFunction(node) && node.value.toLowerCase() !== 'layer'则跳过),然后对其括号内的每个子节点执行check()

3. 只检查"单词"节点

无论哪条路径,最终都汇聚到check(node, atRule)(index.mjs):

function check(node, atRule) { if (!isValueWord(node)) return; const { value, sourceIndex } = node; if (pattern.test(value)) return; const index = atRuleParamIndex(atRule) + sourceIndex; const endIndex = index + value.length; report({ message: messages.expected, messageArgs: [value, primary], node: atRule, index, endIndex, ruleName, result, }); }

关键点在于isValueWord(node)类型守卫(来自 lib/utils/typeGuards.mjs):只有被value-parser判定为"单词"(word)的 token 才参与正则测试,函数名、标点、引号等非单词节点一律忽略。因此:

  • @layer foo.Bar {}foo.Bar是单个 word,整体参与正则匹配,不满足^[a-z][a-z0-9.-]*$中的大小写约束时被整体报告;
  • 报告位置由atRuleParamIndex(atRule) + sourceIndex计算(atRuleParamIndex定义在 lib/utils/nodeFieldIndices.mjs,负责定位 at-rule 参数起始偏移),并给出indexendIndex组成的精确范围,测试快照中的line/column/endLine/endColumn正是由此推导。

常见模式参考

根据团队约定,常见的层命名规范与对应正则包括:

规范正则说明
小写 kebab-case^[a-z][a-z0-9-]*$层名只能小写字母开头,允许数字与连字符,禁止下划线与大写
小写 + 点分嵌套^[a-z][a-z0-9.-]*$在 kebab-case 基础上允许点号,用于嵌套层名如foo.bar
严格小写下划线^[a-z][a-z0-9_]*$允许下划线的变体
camelCase 风格^[a-z][a-zA-Z0-9]*$允许驼峰命名

使用字符串选项时,正则在配置文件中不要/包围符;如果需要更复杂的断言(如前瞻/后顾),建议直接使用 JS 配置文件传入RegExp对象,避免 JSON 转义带来的维护负担。

源码与测试验证

  • 规则核心实现:lib/rules/layer-name-pattern/index.mjs —— 覆盖validateOptions校验、@layer/@import双路径遍历、value-parser词法分析、精确位置报告。
  • 规则测试:lib/rules/layer-name-pattern/tests/index.mjs —— 包含字符串配置与RegExp配置两组用例,验证了多名称列表的多警告输出、@import layer()的列位置(如column: 25column: 30)、以及@layer foo {}空参数不误报等边界行为。
  • 辅助正则:lib/utils/regexes.mjs 与 lib/utils/regexes.mjs —— 定义了layerNameimportNamelayerFunction三个关键正则。
  • 位置计算工具:lib/utils/nodeFieldIndices.mjs ——atRuleParamIndex负责换算 at-rule 参数在源码中的偏移。
  • 消息二次选项语法:docs/user-guide/configure.md#message —— 说明message支持字符串占位符与函数两种写法,以及消息参数(message arguments)的用法。

小结

layer-name-pattern是 Stylelint 命名约定体系中专门针对级联层的一环,它同时覆盖@layer声明与@import ... layer(...)两种层命名入口,支持字符串正则与RegExp对象两种配置方式,并可通过带两个参数的message二次选项定制提示。理解其基于postcss-value-parser的"只检查 word 节点、逐名称独立报告"的实现细节,能帮助你写出既严格又不误伤(例如空层、函数名、引号内容)的层命名规范,让级联层这一现代 CSS 特性在团队协作中保持整齐划一。

【免费下载链接】stylelintA mighty CSS linter that helps you avoid errors and enforce conventions.项目地址: https://gitcode.com/gh_mirrors/st/stylelint

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询