ESLint 规则semi-spacing全解:分号前后空格的强制与禁止
【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint
ESLint 的semi-spacing规则(docs/src/rules/semi-spacing.md)专门用于统一分号(;)前后的空格风格,避免var a = "b" ;这类前后缀空格混乱、以及var c = "d";var e = "f";这类缺少分隔空格的写法,从而提升代码的可读性与一致性。读完本文你将掌握该规则的完整配置参数(before/after)、默认行为、与semi、comma-spacing等相邻规则的职责划分,以及其底层实现原理和可自动修复(--fix)能力。
规则概览
JavaScript 语法允许在分号前后放置多余空格,例如:
var a = "b" ; var c = "d";var e = "f";第一行的分号前多了一个空格,第二行两个语句之间的分号后缺少空格。semi-spacing规则的目标就是在分号两侧强制执行一致的空格风格:禁止表达式中的分号前出现空格,并按配置约束分号后的空格。
从源码 lib/rules/semi-spacing.js 的meta定义可以看出,该规则属于layout(排版布局)类型,且声明为fixable: "whitespace",意味着它产生的全部报告都能被 ESLint 的--fix自动修复。同时,该规则未被纳入recommended(recommended: false),需要你在配置中显式开启。
注意:该规则已在 ESLint v8.53.0 中被标记为已废弃(deprecated),可用至 v11.0.0。核心团队正在将格式化类规则移出 ESLint 核心,交由 ESLint Stylistic 项目(
@stylistic/eslint-plugin中的semi-spacing规则)继续维护,详见源码中的 deprecated 元数据。如果项目从旧版本迁移,这条规则的行为与配置方式保持不变。
规则细节与检查范围
规则的检查核心逻辑位于checkSemicolonSpacing(lib/rules/semi-spacing.js),它只对真正是分号的 token(通过astUtils.isSemicolonToken判断,见 lib/rules/utils/ast-utils.js)进行两项判定:
- 分号前是否有空格(
hasLeadingSpace),结合配置决定是报告"多余空格"还是"缺失空格"; - 分号后是否有空格(
hasTrailingSpace),同样结合配置决定报告方向。
具体的报告消息(messageId)有四种,均定义在 lib/rules/semi-spacing.js:
| messageId | 文案 | 触发场景 |
|---|---|---|
unexpectedWhitespaceBefore | Unexpected whitespace before semicolon. | 配置禁止分号前空格,却出现了空格 |
unexpectedWhitespaceAfter | Unexpected whitespace after semicolon. | 配置禁止分号后空格,却出现了空格 |
missingWhitespaceBefore | Missing whitespace before semicolon. | 配置要求分号前空格,却缺失 |
missingWhitespaceAfter | Missing whitespace after semicolon. | 配置要求分号后空格,却缺失 |
规则不检查的三种情况
原文档明确了该规则不会检查以下三种场景,这些边界在源码中都有对应的守卫条件:
分号是行首第一个 token 时,不检查分号后的空格(对应源码中的
isFirstTokenInCurrentLine判断,lib/rules/semi-spacing.js)。例如下一行的;foo();这种用于防御 ASI 问题的写法不会被干涉。分号紧跟左括号
(或{时,不检查分号前的空格;分号紧跟右括号)或}时,不检查分号后的空格。这部分间距分别由space-in-parens和block-spacing负责(对应源码中的isBeforeClosingParen判断,lib/rules/semi-spacing.js)。例如if (true) {;}中的分号紧贴{与},semi-spacing会主动让位。空条件的 for 循环
for(;;)中的分号间距不检查。测试用例"for (;;) {}"在默认配置下是合法的(tests/lib/rules/semi-spacing.js)。
检查覆盖的语句类型
从规则底部的return对象(lib/rules/semi-spacing.js)可以看出,checkNode会检查所有"最后一个 token 是分号"的语句,包括:
VariableDeclaration(变量声明)ExpressionStatement(表达式语句)BreakStatement/ContinueStatement(break/continue)DebuggerStatement(debugger)DoWhileStatement(do...while)ReturnStatement(return)ThrowStatement(throw)ImportDeclaration/ExportNamedDeclaration/ExportAllDeclaration/ExportDefaultDeclaration(ES Module 导入导出)PropertyDefinition(类字段,ES2022,见测试 tests/lib/rules/semi-spacing.js)
此外ForStatement有专门的处理器,只检查for头部init与test之后、非末尾处的分号(lib/rules/semi-spacing.js)。
配置选项
规则接受一个对象参数,包含两个布尔键:
| 选项键 | 类型 | 默认值 | 含义 |
|---|---|---|---|
before | boolean | false | 为true时强制分号前有空格;为false时禁止分号前空格 |
after | boolean | true | 为true时强制分号后有空格;为false时禁止分号后空格 |
其中after选项只在分号不在行末时生效(即分号后面同一行还有别的 token)。默认配置为{"before": false, "after": true},即"分号前禁空格、分号后要空格"。
在配置文件中开启该规则的完整写法:
{ "rules": { "semi-spacing": ["error", { "before": false, "after": true }] } }如果省略选项对象,等价于使用默认值{ "before": false, "after": true },源码中create函数的初始化逻辑正是如此(lib/rules/semi-spacing.js)。
同时,schema 定义 声明了additionalProperties: false,即不接受任何未知键,传入{ "before": false, "after": true, "foo": 1 }这类配置会直接触发配置校验错误。
默认选项{"before": false, "after": true}
这是默认选项,也是最贴近社区主流风格(配合semi规则的"语句末必须加分号")的组合:禁止分号前空格,强制分号后空格。
不正确的代码示例:
/*eslint semi-spacing: "error"*/ var foo ; var foo;var bar; throw new Error("error") ; while (a) { break ; } for (i = 0 ; i < 10 ; i++) {} for (i = 0;i < 10;i++) {}这些例子分别触发了"分号前多余空格"(var foo ;、throw ... ;、break ;、for 头部)和"分号后缺失空格"(var foo;var bar;、for (i = 0;i < 10;i++) {})。
正确的代码示例:
/*eslint semi-spacing: "error"*/ var foo; var foo; var bar; throw new Error("error"); while (a) { break; } for (i = 0; i < 10; i++) {} for (;;) {} if (true) {;} ;foo();注意后三行属于前面提到的豁免场景:for(;;)空条件不检查;{;}中的分号紧贴括号由block-spacing负责;行首的;foo();属于 ASI 防御写法,分号后不强制空格。
反向选项{"before": true, "after": false}
该选项要求分号前必须有空格,分号后禁止空格。这是一种比较少见的风格,通常与"语句不以分号收尾"或semi: ["error", "never"]搭配使用(分号仅用于分割同一行的语句)。
不正确的代码示例:
/*eslint semi-spacing: ["error", { "before": true, "after": false }]*/ var foo; var foo ; var bar; throw new Error("error"); while (a) { break; } for (i = 0;i < 10;i++) {} for (i = 0; i < 10; i++) {}正确的代码示例:
/*eslint semi-spacing: ["error", { "before": true, "after": false }]*/ var foo ; var foo ;var bar ; throw new Error("error") ; while (a) {break ;} for (i = 0 ;i < 10 ;i++) {}对比两组例子可以直观看出:before: true时var foo;变成错误、var foo ;才是正确;after: false时var foo ; var bar;中的分号后空格成为错误,正确的写法是var foo ;var bar ;。
自动修复与源码实现原理
由于规则声明了fixable: "whitespace",所有报告都可以通过--fix自动修复。修复逻辑直接内嵌在checkSemicolonSpacing的fix回调中(lib/rules/semi-spacing.js):
- 移除分号前的多余空格:
fixer.removeRange([tokenBefore.range[1], token.range[0]]); - 在分号前插入一个空格:
fixer.insertTextBefore(token, " "); - 移除分号后的多余空格:
fixer.removeRange([token.range[1], tokenAfter.range[0]]); - 在分号后插入一个空格:
fixer.insertTextAfter(token, " ")。
报告位置也有讲究:分号前的问题,报告区间覆盖"分号前一个 token 的结尾到分号开头"的空白区域;分号后的问题同理(lib/rules/semi-spacing.js)。
测试文件 tests/lib/rules/semi-spacing.js 中的大量用例同时验证了code(原始代码)与output(修复后代码),例如:
"var a = 'b' ;"→"var a = 'b';"(移除分号前两个空格);"var a = 'b';c = 'd';"→"var a = 'b'; c = 'd';"(补上分号后缺失的空格);"for(a ; ; );"→"for(a;; );"(before: false, after: false组合下同时移除两个空格);"do {} while (true); foo"→"do {} while (true) ;foo"(before: true, after: false下先补后删)。
这些测试还覆盖了字符串内的分号不被误判("var a = 'b ; c';"合法)、跨行变量声明、IIFE、正则字面量后分号、debugger、类字段等边界场景(tests/lib/rules/semi-spacing.js)。
与相邻规则的职责划分
semi-spacing只是分号处理体系中的一环,原文档 frontmatter 声明的相关规则(related_rules)明确了各自的边界:
| 规则 | 职责 | 与 semi-spacing 的关系 |
|---|---|---|
semi | 决定是否必须/禁止写分号 | 搭配使用:semi: ["error", "always"]保证分号存在,semi-spacing保证分号两侧空格 |
no-extra-semi | 移除多余的分号 | 分号本体重复/多余的问题归它管;semi-spacing只关注空格。测试中"foo; ;;;;;;;;;"之所以合法,正是因为多余分号由no-extra-semi清理 |
comma-spacing | 控制逗号前后空格 | 对标分号场景,语义几乎一致 |
block-spacing | 控制代码块花括号{ }内部空格 | 负责{;}这类紧贴花括号的分号两侧空格 |
space-in-parens | 控制圆括号( )内部空格 | 负责(;;)这类紧贴圆括号的分号两侧空格 |
用一句话概括:semi-spacing只管分号两侧的空格,凡是分号紧贴括号或处于行首/行末的边界情形,都由上述相邻规则接管。
历史沿革:合并自no-space-before-semi
从 conf/rule-type-list.json 的移除规则清单可以看到,旧版规则no-space-before-semi(禁止分号前空格)已被移除,其功能合并进semi-spacing:
{ "removed": "no-space-before-semi", "replacedBy": [{ "rule": { "name": "semi-spacing" } }] }因此在配置中看到遗留的no-space-before-semi时,应当迁移为semi-spacing的before: false配置。
何时关闭该规则
如果你不关心分号前后空格的一致性——例如团队已经统一使用格式化工具(Prettier 等)管理空白、或刻意使用无分号风格且不在意行内分割——可以直接关闭该规则。另外需要重申:由于该规则已被标记为废弃(见 lib/rules/semi-spacing.js),新项目更建议直接使用 ESLint Stylistic 的semi-spacing规则,其选项与行为完全一致,迁移成本为零。
参考资源
- 规则文档:docs/src/rules/semi-spacing.md
- 规则实现:lib/rules/semi-spacing.js
- 单元测试(含全部自动修复用例):tests/lib/rules/semi-spacing.js
- 相关规则文档:docs/src/rules/semi.md、docs/src/rules/no-extra-semi.md、docs/src/rules/comma-spacing.md、docs/src/rules/block-spacing.md、docs/src/rules/space-in-parens.md
- 移除规则对照表:conf/rule-type-list.json
【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考