ESLint lines-around-comment 规则详解:强制注释前后空行以提升代码可读性
ESLint lines-around-comment 规则详解强制注释前后空行以提升代码可读性【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslintlines-around-comment是 ESLint 内置的布局layout类规则用于强制在块注释/* */与行注释//的前后保留空行使注释与代码在视觉上清晰分层。本文以 规则文档 为核心结合 规则源码 与 规则测试系统讲解该规则的全部 15 个可配置项、默认值、判定逻辑与源码实现帮助你精确控制注释周围的空行格式。Rule Details规则做了什么许多团队风格指南要求在注释前后保留空行本规则的目标正是让注释更易读、提升代码可读性。具体来说规则要求在块注释/* ... */和/或行注释// ...之前或之后保留空行beforeBlockComment与afterBlockComment分别控制块注释前后的空行beforeLineComment与afterLineComment分别控制行注释前后的空行两类注释互不影响可按需单独启用不检查与代码同行出现的注释如foo(); // 行尾注释也不要求文件开头或结尾出现空行。从源码看规则在Program节点上遍历sourceCode.getAllComments()获取的所有注释 token并按token.type分流处理lib/rules/lines-around-comment.js#L546-L579 中Line类型走beforeLineComment/afterLineComment逻辑Block类型走beforeBlockComment/afterBlockComment逻辑Shebang类型即#!注释则仅在启用afterHashbangComment时处理。Options完整的可配置项本规则接受一个对象选项下表汇总了所有参数、默认值与作用与源码 schema 定义 完全一致选项类型默认值说明beforeBlockCommentbooleantrue要求块注释前有空行afterBlockCommentbooleanfalse要求块注释后有空行beforeLineCommentbooleanfalse要求行注释前有空行afterLineCommentbooleanfalse要求行注释后有空行allowBlockStartbooleanfalse允许注释出现在块语句、函数体、类、switch 语句及类静态块static block的起始位置allowBlockEndbooleanfalse允许注释出现在上述块结构块语句、函数体、类、switch、类静态块的结尾位置allowObjectStartbooleanfalse允许注释出现在对象字面量含解构模式的起始位置allowObjectEndbooleanfalse允许注释出现在对象字面量含解构模式的结尾位置allowArrayStartbooleanfalse允许注释出现在数组字面量含解构模式的起始位置allowArrayEndbooleanfalse允许注释出现在数组字面量含解构模式的结尾位置allowClassStartbooleanfalse允许注释出现在类的起始位置未设置时沿用allowBlockStart的行为allowClassEndbooleanfalse允许注释出现在类的结尾位置未设置时沿用allowBlockEnd的行为ignorePatternstring无自定义被规则忽略的注释正则模式applyDefaultIgnorePatternsbooleantrue是否应用默认忽略模式afterHashbangCommentbooleanfalse要求 hashbang 注释#!...后有空行注意beforeBlockComment的默认值true是通过源码显式兜底实现的——lib/rules/lines-around-comment.js#L164-L167 中当该项为undefined时会被赋值为true其余三个before*/after*开关默认关闭。allowClassStart与allowClassEnd比较特殊schema 中未声明默认值其生效逻辑与allowBlockStart/allowBlockEnd联动详见下文类边界判定。配置示例eslint.config.js的 rules 字段export default [ { rules: { lines-around-comment: [error, { beforeBlockComment: true, afterBlockComment: true, beforeLineComment: true, afterLineComment: false, allowBlockStart: true, allowObjectStart: true, applyDefaultIgnorePatterns: false, ignorePattern: pragma, afterHashbangComment: true }] } } ];该规则的报告消息只有两种源码 messages 定义Expected line before comment.注释前缺少空行与Expected line after comment.注释后缺少空行。beforeBlockComment块注释前必须空行默认{ beforeBlockComment: true }下不正确的代码——块注释紧跟在上一条语句之后/*eslint lines-around-comment: [error, { beforeBlockComment: true }]*/ var night long; /* what a great and wonderful day */ var day great正确的代码——块注释前保留一个空行/*eslint lines-around-comment: [error, { beforeBlockComment: true }]*/ var night long; /* what a great and wonderful day */ var day greatafterBlockComment块注释后必须空行启用{ afterBlockComment: true }后不正确的代码——块注释之后直接跟代码/*eslint lines-around-comment: [error, { afterBlockComment: true }]*/ var night long; /* what a great and wonderful day */ var day great正确的代码——块注释前后都有空行/*eslint lines-around-comment: [error, { afterBlockComment: true }]*/ var night long; /* what a great and wonderful day */ var day greatbeforeLineComment行注释前必须空行启用{ beforeLineComment: true }后不正确的代码/*eslint lines-around-comment: [error, { beforeLineComment: true }]*/ var night long; // what a great and wonderful day var day great正确的代码/*eslint lines-around-comment: [error, { beforeLineComment: true }]*/ var night long; // what a great and wonderful day var day greatafterLineComment行注释后必须空行启用{ afterLineComment: true }后不正确的代码/*eslint lines-around-comment: [error, { afterLineComment: true }]*/ var night long; // what a great and wonderful day var day great正确的代码/*eslint lines-around-comment: [error, { afterLineComment: true }]*/ var night long; // what a great and wonderful day var day greatallowBlockStart / allowBlockEnd块结构边界的注释放行当同时启用beforeLineComment/afterLineComment或beforeBlockComment/afterBlockComment时处于块的开头或结尾的注释往往不需要额外的空行空行已由花括号分隔。allowBlockStart与allowBlockEnd正是为此设计覆盖的函数体、if块、class体、switch语句与类静态块static { }。{ beforeLineComment: true, allowBlockStart: true }下正确的代码/*eslint lines-around-comment: [error, { beforeLineComment: true, allowBlockStart: true }]*/ function foo(){ // what a great and wonderful day var day great return day; } if (bar) { // what a great and wonderful day foo(); } class C { // what a great and wonderful day method() { // what a great and wonderful day foo(); } static { // what a great and wonderful day foo(); } }{ beforeBlockComment: true, allowBlockStart: true }下正确的代码含switch/*eslint lines-around-comment: [error, { beforeBlockComment: true, allowBlockStart: true }]*/ function foo(){ /* what a great and wonderful day */ var day great return day; } if (bar) { /* what a great and wonderful day */ foo(); } class C { /* what a great and wonderful day */ method() { /* what a great and wonderful day */ foo(); } static { /* what a great and wonderful day */ foo(); } } switch (foo) { /* what a great and wonderful day */ case 1: bar(); break; }{ afterLineComment: true, allowBlockEnd: true }下正确的代码/*eslint lines-around-comment: [error, { afterLineComment: true, allowBlockEnd: true }]*/ function foo(){ var day great return day; // what a great and wonderful day } if (bar) { foo(); // what a great and wonderful day } class C { method() { foo(); // what a great and wonderful day } static { foo(); // what a great and wonderful day } // what a great and wonderful day }{ afterBlockComment: true, allowBlockEnd: true }下正确的代码含switch/*eslint lines-around-comment: [error, { afterBlockComment: true, allowBlockEnd: true }]*/ function foo(){ var day great return day; /* what a great and wonderful day */ } if (bar) { foo(); /* what a great and wonderful day */ } class C { method() { foo(); /* what a great and wonderful day */ } static { foo(); /* what a great and wonderful day */ } /* what a great and wonderful day */ } switch (foo) { case 1: bar(); break; /* what a great and wonderful day */ }源码块边界的判定方式allowBlockStart/allowBlockEnd的判定由 isCommentAtBlockStart 与 isCommentAtBlockEnd 完成二者分别委托isCommentAtParentStart/isCommentAtParentEnd覆盖ClassBody、BlockStatement、StaticBlock、SwitchCase、SwitchStatement五种父节点类型块起始注释所在行与父节点起始 token所在行相差 1token.loc.start.line - parentStartNodeOrToken.loc.start.line 1才判定为块开头块结尾父节点结束行与注释结束行相差 1parent.loc.end.line - token.loc.end.line 1才判定为块结尾。对于StaticBlock与SwitchStatement有特殊处理getParentNodeOfToken 中StaticBlock只把花括号内的注释视为静态块的一部分static\n// comment\n{ }中花括号外的注释返回nullSwitchStatement的起始 token 取判别表达式之后的左花括号因此注释必须出现在switch (x) {的左花括号之后才算块开头。测试用例 tests/lib/rules/lines-around-comment.js#L2776-L2814 专门验证了位于switch判别式括号内的注释不会被allowBlockStart放行这一边界行为。allowClassStart / allowClassEnd类边界的精细控制allowClassStart与allowClassEnd单独控制类体ClassBody的起始与结尾且优先级高于块开关当显式设为false时会覆盖allowBlockStart/allowBlockEnd对类体的放行见源码 checkForEmptyLine 中blockStartAllowed/blockEndAllowed的组合条件。{ beforeLineComment: true, allowClassStart: false }下不正确的代码/*eslint lines-around-comment: [error, { beforeLineComment: true, allowClassStart: false }]*/ class foo { // what a great and wonderful day day() {} };{ beforeLineComment: true, allowClassStart: false }下正确的代码/*eslint lines-around-comment: [error, { beforeLineComment: true, allowClassStart: false }]*/ class foo { // what a great and wonderful day day() {} };{ beforeLineComment: true, allowClassStart: true }下正确的代码/*eslint lines-around-comment: [error, { beforeLineComment: true, allowClassStart: true }]*/ class foo { // what a great and wonderful day day() {} };同样块注释下{ beforeBlockComment: true, allowClassStart: false }不正确/*eslint lines-around-comment: [error, { beforeBlockComment: true, allowClassStart: false }]*/ class foo { /* what a great and wonderful day */ day() {} };{ beforeBlockComment: true, allowClassStart: false }正确/*eslint lines-around-comment: [error, { beforeBlockComment: true, allowClassStart: false }]*/ class foo { /* what a great and wonderful day */ day() {} };{ beforeBlockComment: true, allowClassStart: true }正确/*eslint lines-around-comment: [error, { beforeBlockComment: true, allowClassStart: true }]*/ class foo { /* what a great and wonderful day */ day() {} };{ afterLineComment: true, allowClassEnd: true }下正确的代码/*eslint lines-around-comment: [error, { afterLineComment: true, allowClassEnd: true }]*/ class foo { day() {} // what a great and wonderful day };{ afterBlockComment: true, allowClassEnd: true }下正确的代码/*eslint lines-around-comment: [error, { afterBlockComment: true, allowClassEnd: true }]*/ class foo { day() {} /* what a great and wonderful day */ };allowObjectStart / allowObjectEnd对象字面量边界对象相关的放行同时覆盖对象字面量ObjectExpression与对象解构模式ObjectPattern见 isCommentAtObjectStart 与 isCommentAtObjectEnd因此对象解构const { ... } ...中的注释同样受控。{ beforeLineComment: true, allowObjectStart: true }下正确的代码/*eslint lines-around-comment: [error, { beforeLineComment: true, allowObjectStart: true }]*/ var foo { // what a great and wonderful day day: great }; const { // what a great and wonderful day foo: someDay } {foo: great}; const { // what a great and wonderful day day } {day: great};{ beforeBlockComment: true, allowObjectStart: true }下正确的代码/*eslint lines-around-comment: [error, { beforeBlockComment: true, allowObjectStart: true }]*/ var foo { /* what a great and wonderful day */ day: great }; const { /* what a great and wonderful day */ foo: someDay } {foo: great}; const { /* what a great and wonderful day */ day } {day: great};{ afterLineComment: true, allowObjectEnd: true }下正确的代码/*eslint lines-around-comment: [error, { afterLineComment: true, allowObjectEnd: true }]*/ var foo { day: great // what a great and wonderful day }; const { foo: someDay // what a great and wonderful day } {foo: great}; const { day // what a great and wonderful day } {day: great};{ afterBlockComment: true, allowObjectEnd: true }下正确的代码/*eslint lines-around-comment: [error, { afterBlockComment: true, allowObjectEnd: true }]*/ var foo { day: great /* what a great and wonderful day */ }; const { foo: someDay /* what a great and wonderful day */ } {foo: great}; const { day /* what a great and wonderful day */ } {day: great};allowArrayStart / allowArrayEnd数组字面量边界数组相关放行同时覆盖数组字面量ArrayExpression与数组解构模式ArrayPattern见 isCommentAtArrayStart 与 isCommentAtArrayEnd。{ beforeLineComment: true, allowArrayStart: true }下正确的代码/*eslint lines-around-comment: [error, { beforeLineComment: true, allowArrayStart: true }]*/ var day [ // what a great and wonderful day great, wonderful ]; const [ // what a great and wonderful day someDay ] [great, not great];{ beforeBlockComment: true, allowArrayStart: true }下正确的代码/*eslint lines-around-comment: [error, { beforeBlockComment: true, allowArrayStart: true }]*/ var day [ /* what a great and wonderful day */ great, wonderful ]; const [ /* what a great and wonderful day */ someDay ] [great, not great];{ afterLineComment: true, allowArrayEnd: true }下正确的代码/*eslint lines-around-comment: [error, { afterLineComment: true, allowArrayEnd: true }]*/ var day [ great, wonderful // what a great and wonderful day ]; const [ someDay // what a great and wonderful day ] [great, not great];{ afterBlockComment: true, allowArrayEnd: true }下正确的代码/*eslint lines-around-comment: [error, { afterBlockComment: true, allowArrayEnd: true }]*/ var day [ great, wonderful /* what a great and wonderful day */ ]; const [ someDay /* what a great and wonderful day */ ] [great, not great];ignorePattern自定义忽略模式默认情况下规则会忽略以eslint、jshint、jslint、istanbul、global、exported、jscs开头的注释。这些关键词的底层正则定义在 lib/rules/utils/ast-utils.js#L45-L46 的COMMENTS_IGNORE_PATTERNconst COMMENTS_IGNORE_PATTERN /^\s*(?:eslint|jshint\s|jslint\s|istanbul\s|globals?\s|exported\s|jscs)/u;注意其中eslint与jscs后不带空白限定\s即注释内容以这两个词开头即被忽略而jshint、jslint、istanbul、global(s)、exported后要求紧跟空白。因此默认配置下以下代码全部正确测试见 tests/lib/rules/lines-around-comment.js#L1196-L1203foo(); /* jshint mentioned in this comment */ bar();而/* fallthrough */这类不在默认模式中的注释不会被忽略仍会触发报告测试 tests/lib/rules/lines-around-comment.js#L2765-L2775。如需在默认模式之外额外忽略更多注释可将ignorePattern设置为一个字符串模式该字符串会被直接传入RegExp构造函数new RegExp(ignorePattern, u)见源码 lib/rules/lines-around-comment.js#L160因此可以使用正则语法并默认启用uUnicode标志。{ ignorePattern: pragma }下正确的代码/*eslint lines-around-comment: [error, { ignorePattern: pragma }] */ foo(); /* jshint mentioned in this comment */ bar(); foo(); /* a valid comment using pragma in it */{ ignorePattern: pragma }下不正确的代码/*eslint lines-around-comment: [error, { ignorePattern: pragma }] */ 1 1; /* something else */源码 checkForEmptyLine 的匹配顺序是先测applyDefaultIgnorePatterns defaultIgnoreRegExp.test(token.value)再测ignorePattern customIgnoreRegExp.test(token.value)二者任一命中即直接返回、不报告。applyDefaultIgnorePatterns关闭默认忽略模式默认忽略模式始终生效即使你同时提供了ignorePattern。如果希望完全放弃默认模式、只保留自定义模式请将applyDefaultIgnorePatterns设为false。源码实现见 lib/rules/lines-around-comment.js#L161-L162options.applyDefaultIgnorePatterns ! false即只有显式传false才会关闭。{ ignorePattern: pragma, applyDefaultIgnorePatterns: false }下正确的代码/*eslint lines-around-comment: [error, { ignorePattern: pragma, applyDefaultIgnorePatterns: false }] */ foo(); /* a valid comment using pragma in it */{ applyDefaultIgnorePatterns: false }下不正确的代码——默认模式被关闭后jshint注释不再被忽略/*eslint lines-around-comment: [error, { applyDefaultIgnorePatterns: false }] */ foo(); /* jshint mentioned in comment */测试 tests/lib/rules/lines-around-comment.js#L2664-L2762 逐一验证了关闭默认模式后eslint、jshint、jslint、istanbul、global、globals、exported、jscs全部不再豁免均会报告before错误。afterHashbangCommenthashbang 注释后的空行#!开头的 hashbang 注释shebang在 Node.js 可执行脚本中用于指定解释器。启用afterHashbangComment: true后规则要求 hashbang 注释之后必须紧跟一个空行。源码中 hashbang 只检查after方向、before恒为falselib/rules/lines-around-comment.js#L569-L576。不正确的代码#!foo var day great /*eslint lines-around-comment: [error, { afterHashbangComment: true }] */正确的代码#!foo var day great /*eslint lines-around-comment: [error, { afterHashbangComment: true }] */测试用例 tests/lib/rules/lines-around-comment.js#L2816-L2826 验证了#!foo\nvar a 1;会报告after错误并自动修复为#!foo\n\nvar a 1;。源码实现规则的核心判定流程理解规则内部逻辑有助于预测各种边界输入的行为其核心流程集中在 checkForEmptyLine忽略检查先检查默认忽略模式与自定义ignorePattern命中即跳过文件边界豁免prevLineNum 1注释在首行时取消before检查nextLineNum numLines注释在末行时取消after检查行内注释豁免通过 codeAroundComment 向前后查找非注释 token若任一 token 与注释在同一行借助astUtils.isTokenOnSameLine则视为行内注释直接跳过——这就是文档中不检查与代码同行注释的底层实现异常放行组合将blockStartAllowed、classStartAllowed、objectStartAllowed、arrayStartAllowed合并为exceptionStartAllowedend同理任一命中即跳过对应的before/after检查其中allowClassStart: false/allowClassEnd: false会否决allowBlockStart/allowBlockEnd对类体的放行空行判定基于sourceCode.lines与注释行号构建commentAndEmptyLines集合见 getEmptyLineNums 与 getCommentLineNums用注释上一行/下一行的行号是否在该集合中判断是否已有空行或相邻注释自动修复规则声明fixable: whitespacelib/rules/lines-around-comment.js#L88缺少前空行时在注释行首插入\nfixer.insertTextBeforeRange缺少后空行时在注释 token 后插入\nfixer.insertTextAfter因此eslint --fix可以自动补齐空行。内联注释与连续注释的处理行尾内联注释foo() // An inline comment不会被检查。测试 tests/lib/rules/lines-around-comment.js#L70-L78 中即使同时开启afterLineComment与beforeLineComment行内注释也不会触发错误连续的注释行相邻注释彼此之间不要求空行。测试 tests/lib/rules/lines-around-comment.js#L44-L46 显示// line\n// line两行连排时同时开启前后空行检查依然合法——因为注释行号本身加入了commentAndEmptyLines集合且源码 lib/rules/lines-around-comment.js#L505-L509 对前一个 token 是同行的注释也做了豁免。与相关规则的配合lines-around-comment与两个相邻的格式规则共同构成注释/块排版体系space-before-blocks要求块{前有空格控制的是块与语句之间的空白spaced-comment要求注释内容以空格开始如// comment而非//comment控制的是注释符号内部的格式。三者分别管住块前空格、注释符号内空格、注释周围空行通常一起启用以形成统一的注释排版风格。lines-around-comment的规则类型为layout布局类在 规则元信息 中标明recommended: false即不随eslint:recommended自动启用需要团队按需显式配置。与 JSX/注释指令的注意事项规则基于sourceCode.getAllComments()遍历所有注释 token因此同样会处理 JSX 中的注释。另外eslint开头的注释如/* eslint-disable */默认被COMMENTS_IGNORE_PATTERN忽略不会因缺少空行被报告——这保证了关闭/启用规则的指令注释不会反过来触发本规则的错误。When Not To Use It何时关闭该规则很多开发者偏爱紧凑的代码风格并不介意注释紧贴着代码出现。如果你属于这类风格或者你的代码库普遍使用行内注释、/* jshint ... */这类工具注释并且不希望因此引入大量空行那么本规则并不适合你——可以直接关闭lines-around-comment: off改用团队统一的注释规范或交给格式化工具处理。补充说明规则的维护状态从源码 meta.deprecated 可以看到本规则自ESLint v8.53.0起被标记为弃用deprecatedESLint 官方决定将格式化类规则移出核心库计划保留至 v11.0.0后续维护由stylistic/eslint-plugin中的同名lines-around-comment规则接管迁移指南见 ESLint Stylistic 项目。因此新项目若需要该规则建议直接使用 Stylistic 插件的对应规则在使用当前 ESLint 版本v11 之前时本规则依然可用但要注意它不会再有新功能演进。【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考