ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

eslint-plugin-unicorn 之 no-duplicate-font-family-names 规则深度解析:基于 AVA 快照报告的实战研读

eslint-plugin-unicorn 之 no-duplicate-font-family-names 规则深度解析:基于 AVA 快照报告的实战研读 eslint-plugin-unicorn 之 no-duplicate-font-family-names 规则深度解析基于 AVA 快照报告的实战研读【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn本篇指南以 eslint-plugin-unicorn 仓库中的 快照报告 为线索系统拆解 CSS 规则unicorn/no-duplicate-font-family-names的检测语义、等价性判定、自动修复行为及其背后的 ESLint 源码实现。读完本文你将掌握该规则覆盖哪些写法、哪些情况会被判重、修复器何时拒绝自动修复以及如何阅读和更新该规则的快照测试。快照报告是什么一张规则行为的「事实快照」test/snapshots/no-duplicate-font-family-names.js.md是由 AVA 测试框架自动生成的快照报告对应测试源文件 test/no-duplicate-font-family-names.js。它的头部明确说明实际快照内容保存在同目录的no-duplicate-font-family-names.js.snap文件中该报告由 AVA 生成通过t.snapshot()断言输出。快照测试的运行链路位于 test/utils/snapshot-rule-tester.js它通过SnapshotRuleTester调用 ESLint 的Linter.verify()对每个无效用例invalid case执行规则后把「输入代码、报错消息含行列定位、自动修复后的输出」渲染为快照。报告中每个invalid(N)小节即一个测试用例Error 1/1、Error 2/2表示该输入产生的报错数量。若需重新生成快照可运行npm run fix:snapshots对应ava --update-snapshots相关脚本定义在 package.json 中。这份快照报告实际上就是该规则行为的最权威「事实清单」下文所有结论都能在报告中找到对应用例佐证。规则定位与配置方式规则定义位于 rules/no-duplicate-font-family-names.js其meta声明typeproblem报告真实问题fixablecode可通过--fix自动修复schema[]无任何可配置选项languages[css/css]仅作用于 CSS 文件通过eslint/css语言插件生效见 test/utils/languages.js。规则文档 docs/rules/no-duplicate-font-family-names.md 说明该规则默认不在recommended与unopinionated配置中启用需要手动配置例如// eslint.config.js import unicorn from eslint-plugin-unicorn; export default [ { plugins: {unicorn}, rules: { unicorn/no-duplicate-font-family-names: error, }, }, ];其设计动机很直白font-family列表中的重复字体名不会提供额外的 fallback通常是编辑失误规则文档称之为 an editing mistake。核心检测范围font-family 与 font 简写从源码create函数看规则只监听Declaration节点且仅处理规范化后属性名为font或font-family的声明属性名先经ident.decode(...).toLowerCase()归一化因此大小写混写、CSS 转义写法均能命中。快照中两个典型用例invalid(1)a { font-family: Arial, Arial, sans-serif; }→ 修复为a { font-family: Arial, sans-serif; }invalid(33)a { font: 16px Arial, Arial, sans-serif; }→ 修复为a { font: 16px Arial, sans-serif; }。对font简写规则会先借助sourceCode.lexer.matchProperty(font, value)做语法匹配仅保留被判定为font-family部分的节点getFontShorthandGroups再对其做逗号分组。因此快照invalid(32)中font: italic 16px italic, italic也能准确识别出第二个italic是重复字体名而不会被font-style的italic干扰invalid(39)中复杂的font: italic small-caps 700 condensed 16px/1.5 Times New Roman, times new roman, serif同样能定位到重复项。语法无法匹配的非法font简写则被直接忽略getFontShorthandGroups在matchResult.matched为假时返回空数组这正是规则文档所述 Unmatchablefontshorthands are ignored。等价性判定大小写、转义与空格拼接快照报告最重要的价值是精确呈现了「哪些写法算同一个字体族名」。规则通过getFontFamily()为每个逗号分组生成一个归一化key重复即报错。等价规则包括大小写不敏感invalid(15)中SERIF, serif被判定重复报告消息保留后出现的写法serifinvalid(41)中Arial, Arial, ARIAL产生两条错误Error 1/2与Error 2/2分别指向第二个Arial与ARIAL。CSS 转义解码invalid(11)中A\72 ial与Arial等价\72即字母rinvalid(12)中Times\20 New\20 Roman\20为空格与Times New Roman等价invalid(36)中font: 16px A\72 ial, Arial, sans-serif同理。引号内转义同样参与解码invalid(13)中\41 rial\41即A与Arial等价invalid(10)中Times\20 New Roman与Times New Roman等价。未加引号的多个标识符按一个空格拼接invalid(9)中Times New Roman与times new roman等价报告消息统一为小写的times new romaninvalid(46)中Times New Roman与Times /* duplicate */ New Roman等价中间注释不影响拼接结果。引号内空白保持原样快照valid侧的Times New Roman, times new roman 与Times New Roman, Times New Roman均不报错因为带引号名称中的多余空格会被保留字符串比较不相等。这印证了规则文档所述 Whitespace in quoted names and escaped identifiers remains significant。转义标识符中的空白也保留valid侧的Times\20 \20 New与Times New不判重\20 \20是两个空格。报告中的错误消息统一为模板Remove duplicate font family name \{{fontFamilyName}}.消息文本定义在规则源码的messages 对象中。引号族名 vs 通用族关键字刻意不判重一个容易踩坑的细节带引号的名称与通用族关键字是两类不同的实体。invalid(15)中SERIF, serif判重是因为二者都不是字符串但valid侧大量用例表明serif, serif、monospace, monospace、sans-serif, sans-serif、system-ui, system-ui等全部不报错——serif是名为 serif 的具体字体族而serif是通用族关键字浏览器语义不同规则刻意放行。源码中的getFontFamily()用key前缀区分二者字符串族名使用family:前缀识别为通用族的裸标识符使用generic:前缀。快照invalid(17)中serif, serif, SERIF只报最后一条SERIF与serif同为字符串族名大小写归一后重复serif这个通用关键字则被排除在比较之外。源码维护了三组名称集合genericFontFamilyNames标准与浏览器私有通用族名如serif、sans-serif、monospace、system-ui、-webkit-body、-webkit-pictograph等源码位于 rules/no-duplicate-font-family-names.jsgenericFontFamilyAliasesFirefox 仅在整组恰为该名时才视为通用族的别名如-moz-fixed、mono、sansreservedFontFamilyNamesinherit、initial、revert、caption、menu等保留关键字这些名称即使重复也不报告快照valid侧有inherit, inherit、caption, caption、revert-rule, revert-rule等用例。此外当一组包含多个标识符如Arial serif时其中的通用族关键字不再作为关键字参与判定nodes.length 1时跳过通用族名快照valid侧Arial serif, Arial serif与Arial serif, Arial s\65 rif均不报错即源于此。动态值与 generic() 函数规则只对静态可判定的字族名做重复检查同时保留对动态值的兼容var()占位invalid(40)中font-family: Arial, var(--fonts), arial, sans-serif仍能跨过var(--fonts)报出arial重复修复时保留var()Arial, var(--fonts), sans-serifinvalid(22)、invalid(23)的BlinkMacSystemFont、-apple-system亦同。这正是规则文档所述 Staticfont-familyentries are checked around dynamic values。generic()函数CSS 语法generic(fangsong)用于引用通用族。invalid(27)中generic(fangsong), GENERIC(FANGSONG), serif判重函数名大小写不敏感invalid(28)中g\65 neric(f\61 ngsong)转义后的generic与fangsong同样与generic(fangsong)判重。注意generic(fangsong), generic(fangsong)在valid侧不报错——带引号的字符串族名与generic()函数形式彼此独立。动态值开头的简写valid侧font: var(--size) Arial, Arial不报错因为var()使font简写无法被词法匹配为合法font-family序列。自动修复的边界注释是主要障碍规则元数据声明fixable: code快照报告为绝大多数用例都输出了Output:修复结果。修复策略是删除重复项及其前导逗号例如invalid(2)中Arial, Helvetica, Arial, sans-serif修复为Arial, Helvetica, sans-serif。但当重复项附近存在注释时规则会只报告、不修复invalid(37)、invalid(38)、invalid(43)、invalid(44)、invalid(45)等用例的快照中均只有Message:而没有Output:。其实现位于fix函数若从重复项的前一个逗号到下一个逗号或声明值末尾之间存在注释则调用abort()放弃修复因为移除区间会连带删除或错误移动注释。唯一的例外是注释位于删除区间之外如快照invalid(46)中Times New Roman, Times /* duplicate */ New Roman, serif——注释落在被删分组内部而invalid(42)中Arial /* first */, Arial, sans-serif的注释属于首个保留的分组二者均可安全修复。在 test/no-duplicate-font-family-names.js 的test()非快照用例中还验证了更多修复细节Arial, Arial !important /* keep */修复后保留!important /* keep */Arial, Arial, /* next */ sans-serif修复后保留/* next */注释只删除第二个Arial及其前导逗号。另外报告消息中的字体名展示经过formatFontFamilyName()处理把控制字符、分隔符等转义为\u{...}形式。test()中最后一个用例展示了极端的消息格式Remove duplicate font family name \foo\n\u001b[31m\u{9b}31m\u{2028}\u{2029}\u{202e}bar.。多错误报告每个重复项各报一次快照报告中的Error 1/2/Error 2/2表明规则会为每个重复项独立报告。以invalid(35)font: 16px Arial, Arial, ARIAL为例错误 1 指向第二个Arial单独修复后输出font: 16px Arial, ARIAL错误 2 指向ARIAL单独修复后输出font: 16px Arial, Arial。两条错误展示的是各自独立应用修复后的输出快照测试器对每条消息分别执行applyFix实际运行eslint --fix时二者会合并应用。invalid(41)font-family: Arial, Arial, ARIAL行为完全一致。作用域限制只在样式规则与关键帧内检查规则并非对所有font-family声明生效isInRule()会向上追溯祖先Block仅在包含Rule祖先时报告。因此以下场景全部放行见测试valid侧font-face内的font-family: Arial, Arialfont-face中font-family是描述符而非字族列表语义font-palette-values内的同名声明自定义属性--font-family: Arial, Arial自定义属性声明不解析为字族列表supports或container style()内的条件表达式font-family: Arial, Arial顶层非规则内的声明。反之普通规则、media嵌套invalid(7)、keyframes的from/to帧invalid(8)都在检查范围内后者修复为font-family: Arial;。快照之外的对照哪些写法被判定为「非重复」快照报告只展示无效用例但结合 test/no-duplicate-font-family-names.js 的valid数组可以反向确认规则的容错边界与上文各节呼应大小写但非 ASCII 规则Straße, STRASSE不判重ß小写化后不等于SS即比较基于逐字符的 Unicode 小写化而非德语 ß 展开转义后不相等serif, s\65 rif不判重前者是字符串后者是通用族关键字字符串与对应通用族关键字并列不判重前述引号族名 vs 通用关键字带引号名称中多空格、generic()与字符串形式、动态值开头等已在前文覆盖。实战总结与建议结合规则文档 docs/rules/no-duplicate-font-family-names.md 的补充说明使用本规则时有几点实用建议旧浏览器的monospace重复写法部分老旧样式表会为不识别通用族的旧浏览器刻意写font-family: monospace, monospace规则会照常报告。规则文档给出了保留该 workaround 的官方写法——使用 ESLint 行内禁用注释pre { /* eslint-disable-next-line unicorn/no-duplicate-font-family-names -- Legacy browser workaround. */ font-family: monospace, monospace; }--fix前先审视注释由于注释会使修复被跳过含注释的重复声明只能手动整理--fix不会替你删除。验证规则行为在仓库中可运行npm run test:js即ava执行全部测试或单独运行npx ava test/no-duplicate-font-family-names.js验证本规则快照与实现不一致时会提示 diff必要时用npm run fix:snapshots更新快照本仓库环境未安装依赖时需先执行安装。总而言之no-duplicate-font-family-names是一把「语义精确、边界严谨」的 CSS 质量小工具它对转义、大小写、引号与多词族名做归一化比较对font简写借助词法匹配精准取段对动态值与注释保持克制并通过快照测试将 47 个无效用例与数十个有效用例固化为可回归的行为契约。阅读 快照报告 是理解其全部规则细节的最高效途径。【免费下载链接】eslint-plugin-unicornMore than 300 powerful ESLint rules项目地址: https://gitcode.com/GitHub_Trending/es/eslint-plugin-unicorn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表