Files
2026Technology-Competition/docs/superpowers/specs/2026-07-29-stylelint-rule-enhancement-design.md
T
范智鹏 5848eaa82a feat: Linter 规则精细化增强 + 模板导入/导出闭环
ESLint: +29 条 P1/P2 规则 + 12 条 TS 专属规则(含 no-shadow/no-array-constructor 冲突处理)
Stylelint: 集成 stylelint-config-recommended + 27 条额外规则
PMD: 排除 20 弃用 + 17 噪音规则,补启 Security/Multithreading,274+12 条精选
SQL-lint: 内置精选 57 条规则配置 + 按 tier 分级 severity + 无项目配置时自动注入临时配置
模板导出: export-service.ts 导出 2-sheet xlsx(复用 xlsx 零新依赖)
模板导入: template-converter.ts 固定列映射解析 + dedup-prompt.ts AI 语义去重
导入预览增强: 错误规则分组置顶只读、跳过行提示、空数据提示
i18n: 新增 18 条模板导入/导出相关翻译
WebView: 静态分析/自定义规则项默认可展开显示 suggestion
2026-07-30 23:17:20 +08:00

25 KiB
Raw Blame History

Stylelint 规则增强设计书

一、背景与目标

1.1 现状

当前 StylelintAdaptersrc/adapters/stylelint.ts)内置默认配置为硬编码的 12 条规则:

const DEFAULT_CONFIG: Record<string, unknown> = {
    rules: {
        'color-hex-length': 'short',
        'color-named': 'never',
        'color-no-invalid-hex': true,
        'length-zero-no-unit': true,
        'font-family-no-missing-generic-family-keyword': true,
        'block-no-empty': true,
        'declaration-block-no-duplicate-properties': true,
        'no-descending-specificity': true,
        'unit-no-unknown': true,
        'property-no-unknown': true,
        'selector-pseudo-class-no-unknown': true,
        'selector-pseudo-element-no-unknown': true,
    },
};

这 12 条规则仅覆盖 Stylelint 16.x 活跃规则(141 条)的 8.5%,存在以下问题:

  • 覆盖面严重不足:仅 7 条属于官方 recommended(41 条),缺少大量错误检测规则
  • 未使用官方配置包:与 ESLint 适配器使用 @eslint/js recommended 的方式不一致
  • 规则选择零散:12 条规则中混合了错误检测和约定强制两类,无系统性设计
  • 缺少自动修复规则:仅 3 条可自动修复,未充分利用 Stylelint 的 fix 能力

1.2 目标

将内置配置从 12 条扩展到 68 条(P0 官方推荐 41 + P1 强烈推荐 7 + P2 建议启用 20),通过引入 stylelint-config-recommended 作为基线,并追加 27 条额外规则,使 CSS 静态分析覆盖更多错误和约定问题。

1.3 设计原则

  • 与 ESLint 适配器保持一致:采用"官方配置包 + 额外规则对象"的相同架构
  • 不破坏现有配置优先级:全局配置 > 项目配置 > 内置配置,三层择一逻辑不变
  • 引入 stylelint-config-recommended 依赖:与 @eslint/js 的引入方式对齐
  • 同步更新去重数据static-rules.json 必须同步追加新增规则
  • 不修改规则 ID 前缀格式:诊断结果仍使用 stylelint:{ruleName} 格式

二、影响范围分析

2.1 需要修改的文件

文件 修改类型 修改内容
src/adapters/stylelint.ts 代码修改 替换 DEFAULT_CONFIG,引入 stylelint-config-recommended,追加 P1+P2 规则
src/rules/static-rules.json 数据修改 rules.stylelint 数组追加 27 条规则条目
package.json 依赖修改 dependencies 中添加 stylelint-config-recommended 显式声明

2.2 不需要修改的文件

文件 原因
scripts/build.mjs stylelint-config-recommended 不在 external 列表中,将被 esbuild 正确打包
src/adapters/eslint.ts 独立适配器,不受影响
src/orchestrator/orchestrator.ts 仅做调度,不涉及配置逻辑
src/config/linter.ts 配置读取层不变

2.3 不受影响的功能

  • 全局配置(linters.stylelintConfigPath):用户指定配置文件时完全替代内置配置,不受影响
  • 项目配置(.stylelintrc.* / stylelint.config.*):存在时完全替代内置配置,不受影响
  • 规则 ID 输出格式:仍为 stylelint:{ruleName},不变

三、详细设计

3.1 修改 src/adapters/stylelint.ts

3.1.1 当前代码

const DEFAULT_CONFIG: Record<string, unknown> = {
    rules: {
        'color-hex-length': 'short',
        'color-named': 'never',
        'color-no-invalid-hex': true,
        'length-zero-no-unit': true,
        'font-family-no-missing-generic-family-keyword': true,
        'block-no-empty': true,
        'declaration-block-no-duplicate-properties': true,
        'no-descending-specificity': true,
        'unit-no-unknown': true,
        'property-no-unknown': true,
        'selector-pseudo-class-no-unknown': true,
        'selector-pseudo-element-no-unknown': true,
    },
};

3.1.2 修改后代码

import recommendedConfig from 'stylelint-config-recommended';

// P1+P2 额外规则配置
const extraRules: Record<string, unknown> = {
    // === P1:强烈推荐(错误检测)===
    'color-no-invalid-hex': true,
    'function-linear-gradient-no-nonstandard-direction': true,
    'function-no-unknown': true,
    'unit-no-unknown': true,
    'no-unknown-animations': true,
    'no-unknown-custom-media': true,
    'no-unknown-custom-properties': true,

    // === P2:厂商前缀移除(均可自动修复)===
    'at-rule-no-vendor-prefix': true,
    'media-feature-name-no-vendor-prefix': true,
    'property-no-vendor-prefix': true,
    'selector-no-vendor-prefix': true,
    'value-no-vendor-prefix': true,

    // === P2:表示法一致性(均可自动修复)===
    'color-hex-length': 'short',
    'color-function-notation': 'modern',
    'length-zero-no-unit': true,
    'selector-pseudo-element-colon-notation': 'double',
    'import-notation': 'string',
    'alpha-value-notation': 'number',
    'hue-degree-notation': 'angle',
    'keyframe-selector-notation': 'percentage',

    // === P2:冗余检测 ===
    'declaration-block-no-redundant-longhand-properties': true,
    'shorthand-property-no-redundant-values': true,
    'block-no-redundant-nested-style-rules': true,

    // === P2:颜色与字体约定 ===
    'color-named': 'never',
    'font-family-name-quotes': 'always-where-required',

    // === P2:精度与格式 ===
    'number-max-precision': 4,
    'comment-whitespace-inside': 'always',
};

// 合并 recommended 配置与额外规则
const DEFAULT_CONFIG: Record<string, unknown> = {
    ...recommendedConfig,
    rules: {
        ...recommendedConfig.rules,
        ...extraRules,
    },
};

3.1.3 设计说明

为什么用 stylelint-config-recommended 而非硬编码 41 条规则?

  • 可维护性:规则升级时只需更新依赖版本,无需手动修改代码
  • 权威性:官方维护的推荐规则集,经过社区验证
  • 一致性:与 ESLint 适配器使用 @eslint/js 的方式完全对齐
  • 体积可控stylelint-config-recommended 是一个纯 JSON 配置包,无额外依赖,打包后体积增量可忽略

为什么用 extraRules 常量分离额外规则?

  • 可读性:27 条规则单独成常量,与 DEFAULT_CONFIG 合并逻辑分离
  • 可维护性:未来增删规则只需修改 extraRules 对象
  • 可测试性:常量可被测试文件直接导入验证

为什么用展开运算符合并而非覆盖?

Stylelint 配置中 rules 是一个扁平对象。先展开 recommendedConfig.rules41 条 P0),再展开 extraRules(27 条 P1+P2),后者覆盖前者同名规则。实际上 P1/P2 选型时已排除了与 recommended 重复的规则,不存在冲突。当前 12 条硬编码规则中,7 条已在 recommended 中(P0),2 条在 P1 中,3 条在 P2 中,全部被新配置覆盖。

为什么不用 stylelint-config-standard

stylelint-config-standard 包含 82 条规则(recommended 41 + standard 额外 41),但其中 41 条 standard 规则包含大量空行、大小写、引号等格式化约定,这些更适合交给 Prettier 等格式化工具处理。精确选择 27 条额外规则更合理。

3.2 修改 src/rules/static-rules.json

3.2.1 修改内容

rules.stylelint 数组末尾追加以下 27 条规则条目(格式与现有规则一致):

{"id": "stylelint/color-no-invalid-hex", "description": "Disallow invalid hex colors"},
{"id": "stylelint/function-linear-gradient-no-nonstandard-direction", "description": "Disallow non-standard directions in linear-gradient"},
{"id": "stylelint/function-no-unknown", "description": "Disallow unknown functions"},
{"id": "stylelint/unit-no-unknown", "description": "Disallow unknown units"},
{"id": "stylelint/no-unknown-animations", "description": "Disallow unknown animations"},
{"id": "stylelint/no-unknown-custom-media", "description": "Disallow unknown custom media queries"},
{"id": "stylelint/no-unknown-custom-properties", "description": "Disallow unknown custom properties"},
{"id": "stylelint/at-rule-no-vendor-prefix", "description": "Disallow vendor prefixes for at-rules"},
{"id": "stylelint/media-feature-name-no-vendor-prefix", "description": "Disallow vendor prefixes for media feature names"},
{"id": "stylelint/property-no-vendor-prefix", "description": "Disallow vendor prefixes for properties"},
{"id": "stylelint/selector-no-vendor-prefix", "description": "Disallow vendor prefixes for selectors"},
{"id": "stylelint/value-no-vendor-prefix", "description": "Disallow vendor prefixes for values"},
{"id": "stylelint/color-function-notation", "description": "Require modern or legacy notation for color-functions"},
{"id": "stylelint/selector-pseudo-element-colon-notation", "description": "Use single or double colon notation for pseudo-elements"},
{"id": "stylelint/import-notation", "description": "Require string or url notation for @import"},
{"id": "stylelint/alpha-value-notation", "description": "Require percentage or number notation for alpha-values"},
{"id": "stylelint/hue-degree-notation", "description": "Require number or angle notation for hue degrees"},
{"id": "stylelint/keyframe-selector-notation", "description": "Require keyword or percentage notation for keyframe selectors"},
{"id": "stylelint/declaration-block-no-redundant-longhand-properties", "description": "Disallow redundant longhand properties within declaration blocks"},
{"id": "stylelint/shorthand-property-no-redundant-values", "description": "Disallow redundant values within shorthand properties"},
{"id": "stylelint/block-no-redundant-nested-style-rules", "description": "Disallow redundant nested style rules within blocks"},
{"id": "stylelint/font-family-name-quotes", "description": "Require quotes for font-family names"},
{"id": "stylelint/number-max-precision", "description": "Limit the number of decimal places in numbers"},
{"id": "stylelint/comment-whitespace-inside", "description": "Require or disallow whitespace inside comments"}

注:color-hex-lengthcolor-namedlength-zero-no-unit 已在当前 12 条规则中存在于 static-rules.json,不在追加列表中。追加的 24 条均为新增规则。实际追加 24 条(color-no-invalid-hexunit-no-unknown 也已存在于当前规则中)。

3.2.2 修改 linterVersion 字段

"linterVersion": {
    "stylelint": "16.x (68 rules)",
    ...
}

将原有标记改为 "16.x (68 rules)",反映实际启用规则数量。

3.2.3 为什么必须同步更新 static-rules.json

static-rules.json 有两个用途:

  1. UI 展示:在设置面板中展示当前 linter 支持的规则清单
  2. 去重检测:自定义规则导入时,按 ID 匹配 static-rules.json 中的规则,若已存在则提示重复

如果不更新,用户新增的 function-no-unknown 自定义规则不会被识别为重复,导致规则重复。

3.3 修改 package.json

3.3.1 当前代码

"dependencies": {
    "eslint": "^9.39.3",
    "stylelint": "^17.14.0",
    "typescript-eslint": "^8.56.1",
    "xlsx": "^0.18.5"
}

3.3.2 修改后代码

"dependencies": {
    "@eslint/js": "^9.39.3",
    "eslint": "^9.39.3",
    "stylelint": "^17.14.0",
    "stylelint-config-recommended": "^18.0.0",
    "typescript-eslint": "^8.56.1",
    "xlsx": "^0.18.5"
}

3.3.3 修改理由

  • stylelint-config-recommended 需要显式声明为依赖,与 @eslint/js 的处理方式一致
  • 版本 ^18.0.0 兼容 Stylelint 17.xstylelint-config-recommended 自 v15.0.0 起声明 stylelint: ">=16.0.0"v18.0.0 兼容 17.x
  • 该包是纯 JSON 配置文件,无任何运行时依赖,打包后体积增量可忽略

四、新增规则分类详解

4.1 P1 强烈推荐(7 条,error 级别)

这些规则属于 "Possible errors" 类别但未被 recommended 包含,能检测确定性错误。

4.1.1 无效值检测(2 条)

规则 检测场景 误报评估
color-no-invalid-hex #fff00 等无效 hex 颜色 零误报,浏览器静默忽略无效颜色
function-linear-gradient-no-nonstandard-direction linear-gradient(top, ...) 非标准方向 零误报,标准方向使用 to top

4.1.2 未知引用检测(5 条)

规则 检测场景 误报评估
function-no-unknown translat() 等未知函数 低,需要配置 ignoreFunctions 排除自定义函数
unit-no-unknown 10p 等未知单位 零误报
no-unknown-animations 引用不存在的 @keyframes 零误报
no-unknown-custom-media 引用未定义的 --custom-media 低,需要确保自定义媒体已声明
no-unknown-custom-properties 引用未定义的 --custom-property 低,需要确保自定义属性已声明

4.2 P2 建议启用(20 条)

4.2.1 厂商前缀移除(5 条,均可自动修复)

规则 检测场景 自动修复
at-rule-no-vendor-prefix @-webkit-keyframes 可自动移除前缀
media-feature-name-no-vendor-prefix -webkit-min-device-pixel-ratio 可自动移除前缀
property-no-vendor-prefix -webkit-transform 可自动移除前缀
selector-no-vendor-prefix :-webkit-full-screen 可自动移除前缀
value-no-vendor-prefix display: -webkit-flex 可自动移除前缀

说明:现代 CSS 开发应使用 Autoprefixer 自动管理厂商前缀,手写前缀不仅冗余且难以维护。

4.2.2 表示法一致性(8 条,均可自动修复)

规则 配置值 检测场景
color-hex-length 'short' #ffffff 应简写为 #fff
color-function-notation 'modern' rgba(0,0,0,0.5) 应写 rgb(0 0 0 / 50%)
length-zero-no-unit true margin: 0px 应写 margin: 0
selector-pseudo-element-colon-notation 'double' :before 应写 ::before
import-notation 'string' @import url("x.css") 应写 @import "x.css"
alpha-value-notation 'number' opacity: 50% 应写 opacity: 0.5
hue-degree-notation 'angle' hsl(180 50% 50%) 应写 hsl(180deg 50% 50%)
keyframe-selector-notation 'percentage' from/to 应写 0%/100%

4.2.3 冗余检测(3 条)

规则 检测场景 自动修复
declaration-block-no-redundant-longhand-properties margin-top:0; margin-right:0; ... 应用 margin: 0 可自动合并
shorthand-property-no-redundant-values margin: 10px 10px 10px 10px 应简写 可自动简化
block-no-redundant-nested-style-rules 嵌套中重复父选择器 不可自动修复

4.2.4 颜色与字体约定(2 条)

规则 配置值 检测场景
color-named 'never' color: red 应使用十六进制
font-family-name-quotes 'always-where-required' 含空格的字体名需要引号

4.2.5 精度与格式(2 条)

规则 配置值 检测场景
number-max-precision 4 限制小数位数为 4 位
comment-whitespace-inside 'always' /*comment*/ 应写 /* comment */

五、规则级别设计

5.1 级别分配原则

级别 适用场景 数量
trueerror 错误检测、确定性约定 68 条
null(禁用) 不适用的规则 73 条

5.2 与 ESLint 适配器级别设计的差异

ESLint 适配器使用 'error''warn' 两个级别,而 Stylelint 适配器统一使用 true(等价于 error)。原因:

  • Stylelint 的 true 等价于 ESLint 的 'error',是唯一的标准级别
  • Stylelint 不支持 'warn' 级别(与 ESLint 不同),仅支持 true(启用为 error)或 null(禁用)
  • 如需降级为 warning,需通过 Stylelint 的 defaultSeverity 配置项全局设置,而非逐条配置

5.3 自动修复能力统计

分类 总数 可自动修复 自动修复率
P0 官方 recommended 41 3 7%
P1 强烈推荐 7 1 14%
P2 建议启用 20 15 75%
合计 68 19 28%

P2 规则中 75% 可自动修复,用户通过 --fix 可自动修正大部分约定问题。


六、兼容性分析

6.1 对现有用户代码的影响

启用新规则后,之前能通过审查的 CSS 代码可能会新增诊断:

影响程度 规则 说明
可能大量新增诊断 at-rule-no-unknown recommended 新增,未知 at 规则检测
可能大量新增诊断 selector-type-no-unknown recommended 新增,未知类型选择器检测
中等新增诊断 function-no-unknown P1 新增,需配置忽略自定义函数
中等新增诊断 property-no-vendor-prefix 等 5 条 P2 前缀规则,旧项目可能有大量厂商前缀
少量新增诊断 color-function-notation P2 新增,旧代码可能使用传统颜色函数
少量新增诊断 declaration-block-no-redundant-longhand-properties P2 新增,冗余长写属性
极少新增诊断 no-unknown-animations P1 新增,正常项目动画引用正确

6.2 对构建的影响

  • stylelint-config-recommended 是纯 JSON 配置包(约 2KB),无运行时依赖
  • 已确认不在 esbuild 的 external 列表中,将被正确打包到扩展中
  • 构建产物体积增量可忽略(仅增加一个 JSON 配置对象的引用)
  • 不影响 stylelint 引擎本身的加载和运行

6.3 对去重功能的影响

static-rules.json 同步更新后,去重检测范围从 12 条扩展到 68 条。用户导入的自定义规则如果与新增规则 ID 匹配,将正确识别为重复。

6.4 版本兼容性

依赖 当前版本 新增依赖 兼容性说明
stylelint ^17.14.0 项目主依赖,不变
stylelint-config-recommended 未安装 ^18.0.0 v18.0.0 声明 stylelint: ">=16.0.0",兼容 17.x

注意stylelint-config-recommended v18.0.0 的规则集与 Stylelint 16.x 的规则集一致。项目使用 Stylelint 17.x,规则集无变化(17.x 未新增/移除规则,仅修复 bug)。


七、与 ESLint 适配器的架构对比

维度 ESLint 适配器 Stylelint 适配器
官方配置包 @eslint/jsconfigs.recommended stylelint-config-recommended
额外规则来源 eslint 内置规则 stylelint 内置规则
规则合并方式 Flat Config 数组追加 { rules: extraRules } 对象展开 ...recommendedConfig.rules + ...extraRules
级别支持 'error' / 'warn' true(仅 error
TypeScript 规则 typescript-eslint recommended 无对应
自动修复 部分规则支持 部分规则支持
配置优先级 全局 > 项目 > 内置 全局 > 项目 > 内置
规则 ID 格式 eslint:{ruleId} stylelint:{ruleName}

两者在架构上保持一致:官方配置包作为基线 + 额外规则对象补充,仅在合并机制上因框架差异略有不同(Flat Config 数组 vs 配置对象展开)。


八、实施步骤

步骤 1:修改 package.json

dependencies 中添加 "stylelint-config-recommended": "^18.0.0"

步骤 2:修改 src/adapters/stylelint.ts

  1. 在文件顶部添加 import recommendedConfig from 'stylelint-config-recommended'
  2. 删除原有的 DEFAULT_CONFIG 硬编码对象
  3. 添加 extraRules 常量(27 条 P1+P2 规则)
  4. 用展开运算符构建新的 DEFAULT_CONFIGrecommended 41 + extra 27

步骤 3:修改 src/rules/static-rules.json

  1. rules.stylelint 数组中追加新增规则条目(去除已存在的)
  2. 更新 linterVersion.stylelint"16.x (68 rules)"

步骤 4:验证

  1. 执行 npm run lint 确认项目自身代码无新增报错
  2. 执行 npm run compile 确认编译通过
  3. 执行 npm run build 确认打包成功
  4. 检查 static-rules.json 中 stylelint 规则数量为 68 条
  5. 验证 stylelint-config-recommended 已正确打包到产物中

九、测试要点

9.1 单元测试

测试项 验证内容
DEFAULT_CONFIG.rules 键数 恰好 68 条(41 recommended + 27 extra
extraRules 规则数 恰好 27 条
规则名无重复 与 recommended 41 条无交集
recommendedConfig 导入 正确解析为包含 rules 对象的配置

9.2 集成测试

测试项 验证内容
color: #fff00 触发 color-no-invalid-hex 诊断结果包含 stylelint:color-no-invalid-hex
margin: 0px 触发 length-zero-no-unit 诊断结果包含 stylelint:length-zero-no-unit
-webkit-transform: scale(1) 触发 property-no-vendor-prefix 诊断结果包含 stylelint:property-no-vendor-prefix
@import url("style.css") 触发 import-notation 诊断结果包含 stylelint:import-notation
margin: 10px 10px 10px 10px 触发 shorthand-property-no-redundant-values 诊断结果包含对应规则
全局配置存在时忽略内置配置 使用用户配置,不触发新规则
项目配置存在时忽略内置配置 使用项目配置,不触发新规则

9.3 去重测试

测试项 验证内容
导入 function-no-unknown 自定义规则 提示与已有规则重复
导入 property-no-vendor-prefix 自定义规则 提示与已有规则重复
导入 recommended 中已有的规则 仍提示重复(未改变原有行为)

9.4 自动修复测试

测试项 验证内容
margin: 0pxmargin: 0 length-zero-no-unit 自动修复
color: #ffffffcolor: #fff color-hex-length 自动修复
-webkit-transform: scale(1)transform: scale(1) property-no-vendor-prefix 自动修复
@import url("x.css")@import "x.css" import-notation 自动修复

十、风险评估

10.1 主要风险

风险 级别 缓解措施
用户 CSS 新增大量诊断 P2 中 15/20 条可自动修复,用户可通过 --fix 批量修正
function-no-unknown 误报自定义函数 可通过项目配置添加 ignoreFunctions 列表排除
selector-type-no-unknown 对自定义元素误报 可通过 ignoreTypes 配置排除自定义元素
color-named: 'never' 对设计系统颜色名误报 可通过项目配置覆盖该规则
stylelint-config-recommended 版本不匹配 锁定 ^18.0.0,兼容 Stylelint 17.x

10.2 回滚方案

如果新规则导致严重问题,回滚步骤:

  1. DEFAULT_CONFIG 中移除 extraRules,恢复 recommendedConfig 为唯一配置源
  2. 如需完全回退,恢复原有 12 条硬编码 DEFAULT_CONFIG
  3. static-rules.json 中移除追加的规则条目
  4. 恢复 linterVersion.stylelint 为原值

package.jsonstylelint-config-recommended 的声明可以保留(引入依赖本身不影响功能)。


十一、附录

11.1 规则来源参考

11.2 规则数量统计

分类 数量 级别 来源
P0 官方 recommended 41 trueerror stylelint-config-recommended
P1 强烈推荐 7 trueerror Possible errors 非 recommended
P2 建议启用 20 trueerror Limit language features 选型
合计 68 true 占活跃规则的 48%
未启用(需配置/格式化/命名模式/空行/大小写) 73 不适合通用默认配置

11.3 当前 12 条规则映射关系

当前规则 新配置归属 说明
color-hex-length: 'short' P2 保留在 extraRules
color-named: 'never' P2 保留在 extraRules
color-no-invalid-hex: true P1 保留在 extraRules
length-zero-no-unit: true P2 保留在 extraRules
font-family-no-missing-generic-family-keyword: true P0 通过 recommendedConfig 自动启用
block-no-empty: true P0 通过 recommendedConfig 自动启用
declaration-block-no-duplicate-properties: true P0 通过 recommendedConfig 自动启用
no-descending-specificity: true P0 通过 recommendedConfig 自动启用
unit-no-unknown: true P1 保留在 extraRules
property-no-unknown: true P0 通过 recommendedConfig 自动启用
selector-pseudo-class-no-unknown: true P0 通过 recommendedConfig 自动启用
selector-pseudo-element-no-unknown: true P0 通过 recommendedConfig 自动启用

12 条原规则全部被新配置覆盖:5 条进入 P0(由 recommended 自动启用),2 条进入 P1,5 条进入 P2。无规则丢失。