則:關(guān)鍵字前置空格的強(qiáng)制規(guī)范與 `keyword-spacing` 演進(jìn)之路)
深入解析 ESLintspace-before-keywords規(guī)則關(guān)鍵字前置空格的強(qiáng)制規(guī)范與keyword-spacing演進(jìn)之路【免費(fèi)下載鏈接】eslintFind and fix problems in your JavaScript code.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/es/eslint關(guān)鍵字keyword是 JavaScript 語法結(jié)構(gòu)中的保留標(biāo)識符例如function、if、return等。它們在語言中具有特殊含義其前后空格的使用方式往往是團(tuán)隊(duì)代碼風(fēng)格規(guī)范的重要一環(huán)。本文基于 ESLint 倉庫中的space-before-keywords規(guī)則文檔完整還原該規(guī)則的設(shè)計(jì)理念、參數(shù)選項(xiàng)與正反示例并結(jié)合倉庫源碼剖析其接替者keyword-spacing的底層實(shí)現(xiàn)原理。讀完本文你將掌握關(guān)鍵字前置空格的兩種風(fēng)格always/never如何配置理解該規(guī)則為何在 ESLint v2.0.0 被移除、如何平滑遷移到keyword-spacing以及新規(guī)則在源碼層面如何完成檢查與自動修復(fù)。規(guī)則背景為什么需要規(guī)范關(guān)鍵字前的空格關(guān)鍵字是 JavaScript 語法元素的組成部分如function和if。這些標(biāo)識符對語言具有特殊含義因此代碼編輯器中通常會以不同的顏色顯示它們。作為語言的重要組成部分各類風(fēng)格指南常常對關(guān)鍵字周圍的空格做出約定。例如你可能有一個風(fēng)格指南要求關(guān)鍵字必須始終被空格前置那么if-else語句必須寫成這樣if (foo) { // ... } else { // ... }當(dāng)然也可能存在相反的風(fēng)格指南——禁止在關(guān)鍵字前出現(xiàn)空格。space-before-keywords規(guī)則正是為了在團(tuán)隊(duì)中統(tǒng)一關(guān)鍵字前是否留空格這一細(xì)節(jié)而設(shè)計(jì)的。該規(guī)則屬于布局類layout規(guī)則并且支持自動修復(fù)原文檔明確指出通過命令行的--fix選項(xiàng)可以自動修復(fù)該規(guī)則報告的問題fixable。重要提示該規(guī)則已在 ESLint v2.0.0 中被移除由 keyword-spacing 規(guī)則取代。倉庫的版本數(shù)據(jù)文件 rule_versions.json 中記錄著這條規(guī)則的生命周期其首次出現(xiàn)于 1.4.0space-before-keywords: 1.4.0在 2.0.0-beta.3space-before-keywords: 2.0.0-beta.3之后正式退役。規(guī)則詳情覆蓋的關(guān)鍵字與兩種風(fēng)格選項(xiàng)該規(guī)則將強(qiáng)制以下關(guān)鍵字之前的空格一致性條件與循環(huán)if、else、for、while、do、switch異常處理throw、try、catch、finally流程控制with、break、continue、return聲明function、yield、class以及變量聲明let、const、var標(biāo)簽語句label statements參數(shù)選項(xiàng)該規(guī)則接收一個參數(shù)always或never選項(xiàng)行為默認(rèn)值always關(guān)鍵字前必須至少有一個空格? 默認(rèn)值never關(guān)鍵字else、whiledo...while 場景、finally和catch前不允許有空格—值得注意的是當(dāng)選項(xiàng)為always時該規(guī)則允許關(guān)鍵字前面出現(xiàn)左花括號{即}else {會被要求修正但{后的換行與空格不在此規(guī)則管轄范圍。如果你希望調(diào)整這一行為可以考慮使用 block-spacing 規(guī)則。代碼示例always選項(xiàng)下的正反例默認(rèn)always選項(xiàng)下的錯誤代碼/*eslint space-before-keywords: [error, always]*/ if (foo) { // ... }else {} // else 前缺少空格 const foo bar;let baz qux; // let 前缺少空格 var qux function bar () {} // function 前缺少空格 function bar() { if (foo) {return; } // return 前缺少空格 }默認(rèn)always選項(xiàng)下的正確代碼/*eslint space-before-keywords: [error, always]*/ if (foo) { // ... } else {} // else 前有空格 (function() {})(); // function 前是 ( ) 等起始符號不受限制 Foo onClick{function bar() {}} / // JSX 屬性中的 function 表達(dá)式 for (let foo of [bar, baz, qux]) {} // for/of 前均有空格從上面的 JSX 示例可以看出該規(guī)則同樣作用于 JSX 語法上下文中的關(guān)鍵字示例中開啟了parserOptions.ecmaFeatures.jsx。代碼示例never選項(xiàng)下的正反例never選項(xiàng)下的錯誤代碼/*eslint space-before-keywords: [error, never]*/ if (foo) { // ... } else {} // else 前不應(yīng)有空格 do { } while (foo) // do...while 的 while 前不應(yīng)有空格 try {} finally {} // finally 前不應(yīng)有空格 try {} catch(e) {} // catch 前不應(yīng)有空格never選項(xiàng)下的正確代碼/*eslint space-before-keywords: [error, never]*/ if (foo) { // ... }else {} // else 前無空格 do {}while (foo) // do...while 的 while 前無空格 try {}finally {} // finally 前無空格 try{}catch(e) {} // catch 前無空格何時不使用該規(guī)則如果你不希望強(qiáng)制執(zhí)行關(guān)鍵字空格的一致性則可以完全關(guān)閉此規(guī)則不啟用space-before-keywords。規(guī)則的演進(jìn)v2.0.0 移除與keyword-spacing接替space-before-keywords之所以被移除是因?yàn)?ESLint 團(tuán)隊(duì)在 v2.0.0 推出了能力更全面的keyword-spacing規(guī)則——后者不僅能控制關(guān)鍵字之前before的空格還能控制關(guān)鍵字之后after的空格并且支持對每個關(guān)鍵字單獨(dú)定制overrides。倉庫中的 replacements.json 明確記錄了這一替代關(guān)系space-before-keywords: [keyword-spacing]同時遷移指南 migrating-to-2.0.0.md 也寫明了對應(yīng)說明space-before-keywordsis replaced bykeyword-spacing.因此如果你從舊版本升級到 ESLint 2.0.0 或更高版本只需將配置中的規(guī)則名替換為keyword-spacing并按下文所述方式設(shè)置before選項(xiàng)即可保持原有的檢查行為。源碼剖析keyword-spacing如何實(shí)現(xiàn)關(guān)鍵字前空格檢查替代規(guī)則 keyword-spacing.js 的實(shí)現(xiàn)位于倉庫lib/rules/目錄其meta聲明為type: layout布局類與fixable: whitespace可自動修復(fù)空白。其配置 Schema 如下源碼 lib/rules/keyword-spacing.js 第 112–136 行schema: [ { type: object, properties: { before: { type: boolean, default: true }, after: { type: boolean, default: true }, overrides: { type: object, properties: KEYS.reduce((retv, key) { retv[key] { type: object, properties: { before: { type: boolean }, after: { type: boolean }, }, additionalProperties: false, }; return retv; }, {}), additionalProperties: false, }, }, additionalProperties: false, }, ],該 Schema 揭示了三層能力before默認(rèn)true控制關(guān)鍵字前是否需要空格等價于舊的space-before-keywords的alwaystrue與neverfalseafter默認(rèn)true控制關(guān)鍵字后是否需要空格這是舊規(guī)則不具備的能力overrides以關(guān)鍵字名為鍵可針對單個關(guān)鍵字單獨(dú)覆蓋before/after行為實(shí)現(xiàn)細(xì)粒度的特例控制。在檢查邏輯中規(guī)則維護(hù)了兩組正則源碼第 20–23 行const PREV_TOKEN /^[)\]}]$/u; const NEXT_TOKEN /^(?:[([{~!]|\\?|--?)$/u; const PREV_TOKEN_M /^[)\]}*]$/u; const NEXT_TOKEN_M /^[{*]$/u;這些模式用于判斷關(guān)鍵字前/后的相鄰 token 是什么。當(dāng)關(guān)鍵字前一個 token 匹配PREV_TOKEN如}、)、]、時才觸發(fā)期望空格expectSpaceBefore或禁止空格unexpectSpaceBefore的判斷。修復(fù)邏輯的底層實(shí)現(xiàn)expectSpaceBefore源碼第 156–177 行通過sourceCode.getTokenBefore(token)獲取關(guān)鍵字的前一個 token在滿足前一個 token 類型或值匹配模式、與關(guān)鍵字處于同一行、且兩者之間沒有空格時報告錯誤并調(diào)用修復(fù)器fix(fixer) { return fixer.insertTextBefore(token, ); }即在關(guān)鍵字前插入一個空格。與之對應(yīng)的unexpectSpaceBefore源碼第 185–209 行則在兩個 token 之間存在空格時報告錯誤并移除兩者之間的空白區(qū)域fix(fixer) { return fixer.removeRange([ prevToken.range[1], token.range[0], ]); }這種先取前一個 token、再判斷行內(nèi)空格的機(jī)制保證了規(guī)則只在同一行內(nèi)檢查空格不會誤傷跨行代碼例如else換行到下一行的寫法。規(guī)則在create階段會根據(jù)options.before ! false決定對每個關(guān)鍵字掛載expectSpaceBefore還是unexpectSpaceBefore源碼第 280–300 行并支持overrides中的逐關(guān)鍵字覆蓋這正是遷移自space-before-keywords后最直接的對應(yīng)關(guān)系。測試用例印證在測試文件 tests/lib/rules/keyword-spacing.js 中可以看到對before: false的驗(yàn)證等價于舊的never選項(xiàng)options: [{ before: false }],以及通過overrides對單個關(guān)鍵字做特判的用例例如對else、if單獨(dú)設(shè)置before: false、as設(shè)置before: true等測試第 982–987 行、1234–1248 行、2395 行、4904 行。這些用例直接覆蓋了從全局統(tǒng)一到逐關(guān)鍵字定制的全部配置形態(tài)。遷移對照從space-before-keywords到keyword-spacing綜合原規(guī)則文檔與keyword-spacing的源碼 Schema兩者的配置映射關(guān)系如下舊規(guī)則配置新規(guī)則等價配置說明[error, always][error, { before: true }]關(guān)鍵字前必須有空格before默認(rèn)即為true[error, never][error, { before: false }]關(guān)鍵字前禁止空格無法實(shí)現(xiàn)[error, { before: true, after: true }]同時控制關(guān)鍵字后的空格無法實(shí)現(xiàn)[error, { before: true, overrides: { else: { before: false } } }]僅對else特例禁止前置空格遷移時的關(guān)鍵提醒原space-before-keywords的never只作用于else、whiledo...while、finally和catch四個關(guān)鍵字而keyword-spacing的before: false會影響其關(guān)鍵字列表中的全部關(guān)鍵字源碼中KEYS由keywords模塊導(dǎo)出并額外包含as、async、await、from、get、let、of、set、yield等見 keyword-spacing.js 第 28–38 行。因此如果舊配置使用never遷移到新規(guī)則后若不想改變對if、for等關(guān)鍵字的行為需要借助overrides精確指定哪些關(guān)鍵字前置空格被禁用例如{ rules: { keyword-spacing: [error, { before: true, overrides: { else: { before: false }, catch: { before: false }, finally: { before: false } } }] } }補(bǔ)充keyword-spacing的后續(xù)命運(yùn)與格式化規(guī)則遷移需要留意的是keyword-spacing規(guī)則本身在 ESLint 8.53.0 也被標(biāo)記為棄用。倉庫源碼 keyword-spacing.js 第 80–101 行的meta.deprecated字段記錄了這一事實(shí)該規(guī)則屬于格式化類規(guī)則ESLint 團(tuán)隊(duì)正逐步將格式化規(guī)則移出核心keyword-spacing計(jì)劃在 11.0.0 之后從核心中移除其維護(hù)職責(zé)已移交至 ESLint Stylistic 項(xiàng)目stylistic/eslint-plugin中的keyword-spacing規(guī)則。這意味著新項(xiàng)目若需要關(guān)鍵字空格檢查建議直接使用 ESLint Stylistic 提供的對應(yīng)規(guī)則而歷史項(xiàng)目在升級時則需關(guān)注這一遷移路徑??偨Y(jié)space-before-keywords雖然已在 ESLint v2.0.0 退役但它所承載的關(guān)鍵字前空格一致性這一風(fēng)格訴求至今仍是格式化檢查的重要一環(huán)。通過本文你不僅完整掌握了該規(guī)則的兩個選項(xiàng)always/never及其全部正反示例還深入理解了其接替者keyword-spacing在 lib/rules/keyword-spacing.js 中的 Schema 設(shè)計(jì)、token 相鄰判斷機(jī)制與自動修復(fù)實(shí)現(xiàn)以及從舊規(guī)則到新規(guī)則的精確遷移對照。當(dāng)你需要在團(tuán)隊(duì)中統(tǒng)一關(guān)鍵字空格風(fēng)格時無論是直接啟用現(xiàn)代規(guī)則還是閱讀歷史代碼中的舊配置都可以借助本文快速定位到正確的配置形態(tài)?!久赓M(fèi)下載鏈接】eslintFind and fix problems in your JavaScript code.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/es/eslint創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考