
ESLint 的 semi-style 规则精确约束分号位置的布局规范指南【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint本指南围绕 ESLint 核心规则semi-style分号样式展开它用于强制分号出现在语句的行尾或行首指定位置是维护 JavaScript 无分号semicolon-less代码风格一致性的关键工具。读完本文你将掌握semi-style的两种选项last与first的配置方法、其底层实现原理、for循环头与类静态块等特殊场景的处理逻辑以及该规则当前在 ESLint 中的弃用状态与迁移路径。规则概览为什么要指定分号位置通常情况下分号位于行的末尾foo(); bar();但在无分号风格semicolon-less style的代码中开发者会把分号放在行的开头作为前一条语句的安全延续标记从而避免 ASI自动分号插入带来的歧义foo() ;[1, 2, 3].forEach(bar)semi-style规则的作用就是检查分号周围的行终止符换行符强制分号出现在你配置的位置。它关注的是分号位于行首还是行尾而非是否写分号——后者由semi规则负责。因此semi-style通常与semi配合使用semi决定分号的有无semi-style决定分号的方位。该规则在 ESLint 官方分类中属于layout布局类规则默认不推荐启用recommended: false支持--fix自动修复fixable: whitespace。其元数据可在 lib/rules/semi-style.js 中查看type: layout影响代码排版而非逻辑正确性schema: [{ enum: [last, first] }]唯一合法选项值只有last与firstfixable: whitespace表示修复属于空白类变更不会改变语义报错信息expectedSemiColon文本为Expected this semicolon to be at {{pos}}.其中{{pos}}会被替换为 the end of the previous line 或 the beginning of the next line。配置选项规则接受一个字符串选项默认值为last。在 flat config 或 eslintrc 配置文件中写法如下{ semi-style: [error, last] }两种选项的语义选项含义说明last默认强制分号位于语句末尾上一行行尾符合常规书写习惯first强制分号位于语句开头下一行行首适用于无分号风格代码重要特例即使使用first选项for循环头for(a;b;c){}中的分号也必须放在行尾。这是由源码中的ForStatement处理器硬性规定的详见下文源码实现章节因为for头部的两个分号在语法上必须分隔三个表达式无法采用行首分号的写法。从源码看选项的读取逻辑为const option context.options[0] || last;lib/rules/semi-style.js即未提供选项时自动回退到last。用法示例选项last默认不正确的代码示例/*eslint semi-style: [error, last]*/ foo() ;[1, 2, 3].forEach(bar) for ( var i 0 ; i 10 ; i ) { foo() } class C { static { foo() ;bar() } }以上代码中分号被放在了行首违反了last要求。其中for循环头的分号位于行首; i 10、; i类静态块内;bar()的行首分号同样违规。正确的代码示例/*eslint semi-style: [error, last]*/ foo(); [1, 2, 3].forEach(bar) for ( var i 0; i 10; i ) { foo() } class C { static { foo(); bar() } }注意第二行[1, 2, 3].forEach(bar)末尾没有分号也能通过检查——semi-style只约束已存在的分号的位置不要求必须写分号。选项first不正确的代码示例/*eslint semi-style: [error, first]*/ foo(); [1, 2, 3].forEach(bar) for ( var i 0 ; i 10 ; i ) { foo() } class C { static { foo(); bar() } }在此配置下foo();行尾的分号违规应移到下一行行首但for循环头即使使用first分号依然必须留在行尾此处; i 10放在行首属于另一类违规见下文源码说明——实际上该示例同时触发了对for头分号的last检查类静态块中foo();的行尾分号同样违规。正确的代码示例/*eslint semi-style: [error, first]*/ foo() ;[1, 2, 3].forEach(bar) for ( var i 0; i 10; i ) { foo() } class C { static { foo() ;bar() } }在first模式下只有for循环头的两个分号保持在行尾var i 0;、i 10;普通语句的分号均位于行首。源码实现规则是如何工作的semi-style的实现位于 lib/rules/semi-style.js核心逻辑可以拆解为三个部分。1. 监听哪些节点规则通过一个 CSS 选择器字符串SELECTOR批量监听 13 种语句节点类型lib/rules/semi-style.jsBreakStatement, ContinueStatement, DebuggerStatement, DoWhileStatement, ExportAllDeclaration, ExportDefaultDeclaration, ExportNamedDeclaration, ExpressionStatement, ImportDeclaration, ReturnStatement, ThrowStatement, VariableDeclaration, PropertyDefinition这些节点覆盖了几乎所有的以分号结尾的语句类型包括import/export声明、return/throw、变量声明以及 ES2022 的类字段PropertyDefinition。2. 检查单个分号 tokencheck(semiToken, expected)函数lib/rules/semi-style.js是核心检查器取分号前后的两个 token判断分号与前一个 tokenprevIsSameLine、与后一个 tokennextIsSameLine是否在同一行若expected last且分号与前一个 token 不在同一行或expected first且分号与后一个 token 不在同一行则上报错误行内判断复用 lib/rules/utils/ast-utils.js 中的isTokenOnSameLine工具函数分号识别使用isSemicolonToken。自动修复逻辑修复器会先检查分号前后 token 之间是否存在注释sourceCode.commentsExistBetween若存在注释则拒绝修复返回null避免破坏注释否则将分号与其前/后 token 之间的整个区间替换为;\nlast模式或\n;first模式。这正是测试中foo\n;bar被修复为foo;\nbar的底层原因。3. 两个特殊处理块尾节点与 for 循环头块尾节点豁免isLastChild函数lib/rules/semi-style.js负责判断节点是否为父块的最后一条语句。当选项为first且节点是块内最后一条语句时规则直接跳过——因为块的}后面已经紧跟换行行首分号无从谈起。该函数识别以下情况IfStatement的consequent且存在alternate即else分支之前DoWhileStatement的循环体while关键字之前BlockStatement、StaticBlock、Program、ClassBody的body最后一项、SwitchCase的consequent最后一项即}之前。for 循环头固定为lastForStatement处理器lib/rules/semi-style.js独立于选项设置无论配置为last还是first都强制要求for头部的两个分号位于行尾。这正是文档中即使使用first选项for循环头的分号也应位于行尾这一规则在实现层的落实。边界情况与测试佐证规则的测试集位于 tests/lib/rules/semi-style.js共覆盖 741 行测试用例值得关注几类边界行为空语句与连续分号;、;foo;bar;baz;在两种选项下都视为合法说明规则允许独立分号语句的存在。do...while与if...elsedo foo; while (a)中foo;的行尾分号在first模式下合法因为位于while关键字之前属于isLastChild豁免if (a) foo; else bar同理。注释阻止自动修复测试中foo\n;/**/bar、foo;\n/**/bar的output均为null即规则会报告错误但拒绝提供修复验证了分号与注释相邻时不修复的保护机制。类静态块与类字段ES2022 语法的类字段PropertyDefinition和类静态块StaticBlock均被纳入检查范围测试覆盖了class C { static { foo\n; } }修复为class C { static { foo;\n} }等场景。for头多行换行for(a\n;b;c)d在两种选项下都会被修复为for(a;\nb;c)d确认for头分号始终以last标准检查。与相关规则的协同semi-style的 frontmatter 声明了三个相关规则见 docs/src/rules/semi-style.mdno-extra-semi禁止多余分号防止;;;之类的冗余写法semi控制是否要求分号always/never及单行块等细节选项semi-spacing约束分号与相邻 token 之间的空白字符。实践中推荐的组合semi决定分号有无semi-style决定分号方位行尾/行首semi-spacing决定分号两侧是否留空格三个规则各司其职、互不重叠。弃用状态与迁移指引需要特别说明的是semi-style已在 ESLintv8.53.0起被标记为弃用计划可用至v11.0.0随后将从 ESLint 核心中移除。原因正如源码元数据lib/rules/semi-style.js所写格式类规则正在从 ESLint 核心移出交由其继任者 ESLint Stylistic 项目维护。官方给出的替换方案是stylistic/eslint-plugin中的同名规则semi-style。因此如果项目使用的是 ESLint v8.53.0 及以上版本建议新项目直接使用stylistic/eslint-plugin中的对应规则或在旧项目升级时同步迁移避免规则在 v11.0.0 被移除后失效。若仍需在 ESLint 核心中使用该规则的文档即本文所讲解的 docs/src/rules/semi-style.md其配置与行为不因弃用而改变。何时不使用此规则如果你不关心分号位于行首还是行尾即不想收到关于分号位置的通知那么可以安全地关闭禁用此规则。在 flat config 中{ rules: { semi-style: off, }, }特别地对于无分号风格的代码库如果团队统一遵循ASI 由换行符自然处理的约定且不愿采用行首分号的防御性写法semi-style同样不是必需品只有当你明确要求分号必须锚定在行尾last或行首first时才值得启用。【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考