ESLintlines-around-directive规则详解:指令序言(directive prologue)周围的空行控制
【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint
lines-around-directive是 ESLint 内置的 layout(布局)类规则,用于强制或禁止在 JavaScript 文件的指令序言(directive prologue)——例如"use strict";、"use asm";——之前或之后出现空行。本篇以官方文档 docs/src/rules/lines-around-directive.md 为核心骨架,结合 lib/rules/lines-around-directive.js 源码与 tests/lib/rules/lines-around-directive.js 测试用例,系统讲解该规则的配置方式、判定逻辑、自动修复行为与弃用迁移方案,帮助你精确掌控指令序言周围的排版风格。
指令序言(Directive Prologue)是什么
JavaScript 规范允许在文件顶部或函数体顶部出现一组连续的字符串字面量表达式语句,它们向执行环境声明"脚本希望启用某项特性",最典型的例子是"use strict";(严格模式)。这一组语句被统称为指令序言(directive prologue),并且只作用于其所在的文件或函数作用域。
// 严格模式作用于整个脚本 "use strict"; var foo; function bar() { var baz; }var foo; function bar() { // 严格模式只作用于该函数内部 "use strict"; var baz; }从实现层面看,ESLint 在 lib/rules/utils/ast-utils.js 中通过getDirectivePrologue(node)来识别指令序言:它只对Program、FunctionDeclaration、FunctionExpression以及函数体为块语句(BlockStatement)的ArrowFunctionExpression生效,从函数体(或Program.body)的第一个语句开始,只要语句是ExpressionStatement且其expression是Literal(字符串字面量),就将其纳入指令列表;一旦遇到任何其他类型的语句便立即停止。注意代码注释中提到:() => "use strict";这种箭头函数隐式返回字符串的写法不是指令序言,规则不会对其生效。
Rule Details:规则判定范围
该规则只负责在指令序言整体之前和整体之后(即第一个指令之前、最后一个指令之后)强制或禁止空行,并明确不对指令与指令之间的空行做任何约定。此外,除非指令前紧邻注释,否则它不会强制要求在指令序言之前加空行。
关于空行的判定,源码中给出了精确的"行差"算法:
hasNewlineBefore(node)(lib/rules/lines-around-directive.js):取指令节点前一个 token(includeComments: true,即把注释也算进来),若node.loc.start.line - tokenLineBefore >= 2,则视为存在空行;hasNewlineAfter(node)(lib/rules/lines-around-directive.js):借助getLastTokenOnLine(node)拿到与节点处于同一行的最后一个 token,再比较下一 token 的行号差是否>= 2。getLastTokenOnLine之所以存在,是因为当尾随分号被换行隔开时(分号单独占一行),"同一行最后一个 token"会是倒数第二个 token,需要特殊处理。
规则的入口同时注册在Program、FunctionDeclaration、FunctionExpression、ArrowFunctionExpression四类节点上(lib/rules/lines-around-directive.js),因此文件顶层与每个函数体内的指令序言都会被独立检查。规则在 lib/rules/index.js 中注册为按需加载(lazy loading),fixable: "whitespace"表明它支持自动修复。
Options:两种配置形态
规则接受一个选项,可以是字符串或对象:
"always"(默认值)——要求指令周围必须有空行;"never"——禁止指令周围出现空行。
或使用对象形式分别控制前后两侧:
{ "before": "always" | "never", // 控制指令序言之前的空行 "after": "always" | "never" // 控制指令序言之后的空行 }对象形式的两侧取值都是必填的。这一点在源码的 schema 校验中有硬性约束(lib/rules/lines-around-directive.js):before与after只能是"always"或"never",additionalProperties: false禁止任何多余字段,且minProperties: 2要求两个属性必须同时出现。
选项的解析逻辑在create(context)入口(lib/rules/lines-around-directive.js):config = context.options[0] || "always",若为字符串则 before/after 同时取该值;若为对象则分别取config.before与config.after。
配置示例与正反例
always(默认)
错误示例("always"):
/* eslint lines-around-directive: ["error", "always"] */ // comment "use strict"; var foo; function foo() { "use strict"; "use asm"; var bar; } function foo() { // comment "use strict"; var bar; }/* eslint lines-around-directive: ["error", "always"] */ // comment "use strict"; "use asm"; var foo;正确示例("always"):
/* eslint lines-around-directive: ["error", "always"] */ // comment "use strict"; var foo; function foo() { "use strict"; "use asm"; var bar; } function foo() { // comment "use strict"; var bar; }/* eslint lines-around-directive: ["error", "always"] */ // comment "use strict"; "use asm"; var foo;注意观察"always"下的几个关键细节:
- 注释与第一个指令之间需要空行(
// comment之后有空行再到"use strict";); - 指令序言结束(最后一个指令)与后续语句之间需要空行;
- 指令与指令之间(如
"use strict";与"use asm";)不需要空行; - 当指令序言是整个函数体/文件的唯一内容时(如
function foo() { "use strict"; "use asm"; var bar; }之外没有任何语句的情况),规则不会强制在之后补空行——源码中明确:若最后一个指令就是 body 的最后一个语句且无尾随注释,直接return跳过 after 检查,以保证与padded-blocks规则的兼容性(lib/rules/lines-around-directive.js)。
never
错误示例("never"):
/* eslint lines-around-directive: ["error", "never"] */ // comment "use strict"; var foo; function foo() { "use strict"; "use asm"; var bar; } function foo() { // comment "use strict"; var bar; }/* eslint lines-around-directive: ["error", "never"] */ // comment "use strict"; "use asm"; var foo;正确示例("never"):
/* eslint lines-around-directive: ["error", "never"] */ // comment "use strict"; var foo; function foo() { "use strict"; "use asm"; var bar; } function foo() { // comment "use strict"; var bar; }/* eslint lines-around-directive: ["error", "never"] */ // comment "use strict"; "use asm"; var foo;在"never"模式下,指令与注释、指令与后续语句之间一律不允许空行,指令之间同样不受约束。这里还有一处边界行为:文件顶部没有注释时,"never"会检查第一个指令前是否有空行(因为文件首行不可能是空行,通常都能通过);但源码特意指出,仅当指令前有注释、或处于Program顶部且expectLineBefore === "never"时才检查 before(lib/rules/lines-around-directive.js),目的是不在文件最顶部强制空行,并与padded-blocks保持兼容。
before & after 对象形式
{ "before": "never", "after": "always" }—— 错误示例:
/* eslint lines-around-directive: ["error", { "before": "never", "after": "always" }] */ // comment "use strict"; var foo; function foo() { "use strict"; "use asm"; var bar; } function foo() { // comment "use strict"; var bar; }/* eslint lines-around-directive: ["error", { "before": "never", "after": "always" }] */ // comment "use strict"; "use asm"; var foo;{ "before": "never", "after": "always" }—— 正确示例:
/* eslint lines-around-directive: ["error", { "before": "never", "after": "always" }] */ // comment "use strict"; var foo; function foo() { "use strict"; "use asm"; var bar; } function foo() { // comment "use strict"; var bar; }/* eslint lines-around-directive: ["error", { "before": "never", "after": "always" }] */ // comment "use strict"; "use asm"; var foo;{ "before": "always", "after": "never" }—— 错误示例:
/* eslint lines-around-directive: ["error", { "before": "always", "after": "never" }] */ // comment "use strict"; var foo; function foo() { "use strict"; "use asm"; var bar; } function foo() { // comment "use strict"; var bar; }/* eslint lines-around-directive: ["error", { "before": "always", "after": "never" }] */ // comment "use strict"; "use asm"; var foo;{ "before": "always", "after": "never" }—— 正确示例:
/* eslint lines-around-directive: ["error", { "before": "always", "after": "never" }] */ // comment "use strict"; var foo; function foo() { "use strict"; "use asm"; var bar; } function foo() { // comment "use strict"; var bar; }/* eslint lines-around-directive: ["error", { "before": "always", "after": "never" }] */ // comment "use strict"; "use asm"; var foo;在配置文件中的写法
以上是内联注释(/* eslint ... */)形式的示例。在实际项目中,通常将规则配置在 ESLint 配置文件中(如eslint.config.js):
// eslint.config.js export default [ { rules: { "lines-around-directive": ["error", "always"], // 或 "lines-around-directive": ["error", "never"], // 或对象形式 "lines-around-directive": ["error", { before: "never", after: "always" }], }, }, ];规则默认recommended: false,不会随eslint:recommended自动启用,需要显式配置。同时注意:由于该规则针对脚本级指令,示例代码均需以脚本模式(sourceType: "script")解析,这从文档示例的::: incorrect { "sourceType": "script" }标注可以确认。
自动修复(Autofix)行为
lines-around-directive在 meta 中声明了fixable: "whitespace",支持--fix自动修复。修复逻辑位于 lib/rules/lines-around-directive.js 的fix(fixer)中:
- 期望空行但缺失时:在指令前插入
"\n"(before场景)或在最后一个 token 后插入"\n"(after场景); - 出现多余空行时:删除指令前的一个字符范围
[node.range[0] - 1, node.range[0]](before 场景),或删除最后一个 token 后的一个字符[lastToken.range[1], lastToken.range[1] + 1](after 场景)。
这里的"删除一个字符"正是针对空行中的换行符,而空行的判定基于>= 2的行差,因此多出的空行会先被消减一行。对应的修复期望(output字段)在 tests/lib/rules/lines-around-directive.js 中有大量验证,例如"'use strict';\nvar foo;"在"always"下被修复为"'use strict';\n\nvar foo;"。
测试文件还覆盖了许多边界场景,包括:#!/usr/bin/env nodeshebang 之后紧跟指令、单行/多行注释与指令之间空行的判定、"use asm";多指令序言、函数体内的指令检查、以及箭头函数块体() => { 'use strict'; ... }的检查等,可作为理解规则行为的补充依据。
弃用状态与迁移建议
该规则在ESLint v4.0.0起被标记为已弃用(deprecated),并在ESLint v11.0.0移除(availableUntil: "11.0.0"),当前仓库中的实现仍保留deprecated元信息(lib/rules/lines-around-directive.js),弃用原因被描述为"该规则被一个更通用的规则取代"。
官方推荐的替代方案是社区插件@stylistic/eslint-plugin中的padding-line-between-statements规则。该通用规则可以通过配置语句类型对(如"directive"与普通语句之间)来完全复刻lines-around-directive的能力,并且表达能力更强。在 flat config 下的大致迁移写法:
// 迁移示例(需要安装 @stylistic/eslint-plugin) import stylistic from "@stylistic/eslint-plugin"; export default [ { plugins: { "@stylistic": stylistic }, rules: { // 等价于 lines-around-directive: ["error", "always"] "@stylistic/padding-line-between-statements": [ "error", { blankLine: "always", prev: "directive", next: "*" }, { blankLine: "any", prev: "directive", next: "directive" }, ], }, }, ];迁移写法的语义映射说明:
blankLine: "always"表示指令与后续任意语句之间始终保留空行,blankLine: "any"表示指令与指令之间不做要求,二者组合可模拟"always";如需"never",将"always"替换为"never"即可。这里仅给出迁移思路,具体配置请以插件官方文档为准。
如果使用旧版eslintrc配置格式,弃用提示会随 ESLint 版本给出对应警告,同时可通过规则 meta 中的replacedBy信息定位替代规则。
When Not To Use It:何时关闭此规则
如果你对指令序言前后是否保留空行没有任何强制的排版约定,可以安全地禁用此规则。此外,考虑到该规则已弃用且即将移除,新项目更推荐直接使用@stylistic/eslint-plugin的padding-line-between-statements或其他风格方案,从源头避免后续迁移成本。
小结
lines-around-directive围绕"指令序言前后是否保留空行"这一个排版维度,提供了字符串与对象两种配置形态("always"/"never"/{ before, after }),支持--fix自动修复,并与padded-blocks、lines-around-comment等布局类规则在设计上保持兼容(例如仅在指令前存在注释时才对 before 侧提出空行要求、不检查仅有指令序言的函数体尾部)。理解其判定细节(行差>= 2视为空行、getDirectivePrologue的指令识别边界)后,你可以准确预测任意代码的检查结果,或借助测试文件 tests/lib/rules/lines-around-directive.js 进一步验证边界行为。
【免费下载链接】eslintFind and fix problems in your JavaScript code.项目地址: https://gitcode.com/GitHub_Trending/es/eslint
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考