自訂規則
您可以建立自訂規則以與 ESLint 搭配使用。如果 核心規則 沒有涵蓋您的使用案例,您可能需要建立自訂規則。
以下是自訂規則的基本格式
// customRule.js
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "Description of the rule",
},
fixable: "code",
schema: [] // no options
},
create: function(context) {
return {
// callback functions
};
}
};
規則結構
規則的原始檔匯出一個具有下列屬性的物件。自訂規則和核心規則都遵循此格式。
meta
: (物件
) 包含規則的中繼資料
-
type
: (字串
) 指示規則的類型,可以是"problem"
、"suggestion"
或"layout"
其中之一"problem"
:此規則正在識別會導致錯誤或可能導致混淆行為的程式碼。開發人員應將此視為高優先順序來解決。"suggestion"
:此規則正在識別可以用更好的方式完成的事情,但如果程式碼沒有變更,則不會發生錯誤。"layout"
:此規則主要關心空格、分號、逗號和括號,即程式的所有部分,決定程式碼的外觀而不是執行方式。這些規則作用於 AST 中未指定的程式碼部分。
-
docs
: (物件
) 通常用於產生文件和工具的屬性。核心規則為必要,自訂規則為選擇性。自訂規則可以根據需要在此處包含其他屬性。description
: (字串
) 提供規則的簡短描述。對於核心規則,這會用於 規則索引 中。recommended
: (布林值
) 對於核心規則,這會指定是否由@eslint/js
中的recommended
設定啟用規則。url
: (字串
) 指定可以存取完整文件的 URL。程式碼編輯器通常會使用此選項,在醒目提示的規則違規上提供有用的連結。
-
fixable
: (字串
) 如果 命令列上的--fix
選項自動修正規則回報的問題,則為"code"
或"whitespace"
。重要:
fixable
屬性對於可修正的規則是強制性的。如果未指定此屬性,則每當規則嘗試產生修正時,ESLint 都會擲回錯誤。如果規則不可修正,則省略fixable
屬性。 -
hasSuggestions
: (布林值
) 指定規則是否可以傳回建議 (如果省略,則預設為false
)。重要:
hasSuggestions
屬性對於提供建議的規則是強制性的。如果此屬性未設定為true
,則每當規則嘗試產生建議時,ESLint 都會擲回錯誤。如果規則不提供建議,則省略hasSuggestions
屬性。 -
schema
: (物件 | 陣列 | false
) 指定 選項,以便 ESLint 可以防止無效的 規則設定。當規則具有選項時為必要。 -
defaultOptions
: (陣列
) 指定規則的 預設選項。如果存在,則使用者在其設定中提供的任何選項都會以遞迴方式合併到它們之上。 -
deprecated
: (布林值
) 指示規則是否已棄用。如果規則尚未棄用,您可以省略deprecated
屬性。 -
replacedBy
: (陣列
) 在已棄用規則的情況下,請指定取代規則。
create()
:傳回一個物件,其中包含 ESLint 在遍歷 JavaScript 程式碼的抽象語法樹 (AST,由 ESTree 定義) 時呼叫的「訪問」節點的方法
- 如果鍵是節點類型或選取器,則 ESLint 會在向下遍歷樹狀結構時呼叫該訪問者函式。
- 如果鍵是節點類型或選取器加上
:exit
,則 ESLint 會在向上遍歷樹狀結構時呼叫該訪問者函式。 - 如果鍵是事件名稱,則 ESLint 會針對程式碼路徑分析呼叫該處理常式函式。
規則可以使用目前節點及其周圍的樹狀結構來回報或修正問題。
以下是 array-callback-return 規則的方法
function checkLastSegment (node) {
// report problem for function if last code path segment is reachable
}
module.exports = {
meta: { ... },
create: function(context) {
// declare the state of the rule
return {
ReturnStatement: function(node) {
// at a ReturnStatement node while going down
},
// at a function expression node while going up:
"FunctionExpression:exit": checkLastSegment,
"ArrowFunctionExpression:exit": checkLastSegment,
onCodePathStart: function (codePath, node) {
// at the start of analyzing a code path
},
onCodePathEnd: function(codePath, node) {
// at the end of analyzing a code path
}
};
}
};
Context 物件
context
物件是規則中 create
方法的唯一引數。例如
// customRule.js
module.exports = {
meta: { ... },
// `context` object is the argument
create(context) {
// ...
}
};
顧名思義,context
物件包含與規則內容相關的資訊。
context
物件具有下列屬性
id
: (字串
) 規則 ID。filename
: (字串
) 與來源相關聯的檔案名稱。physicalFilename
: (字串
) 當檢查檔案時,它會提供檔案在磁碟上的完整路徑,而沒有任何程式碼區塊資訊。當檢查文字時,它會提供傳遞給—stdin-filename
的值,如果未指定,則提供<text>
。cwd
: (字串
) 傳遞給 Linter 的cwd
選項。它是應該被視為目前工作目錄的目錄路徑。options
: (陣列
) 此規則的 設定選項陣列。此陣列不包含規則嚴重性 (請參閱 專用章節)。sourceCode
: (物件
) 您可以用來處理傳遞給 ESLint 的來源的SourceCode
物件 (請參閱存取原始碼)。settings
: (物件
) 設定中的 共用設定。languageOptions
: (物件
) 每個屬性的詳細資訊請參閱此處sourceType
: ('script' | 'module' | 'commonjs'
) 目前檔案的模式。ecmaVersion
: (數字
) 用於剖析目前檔案的 ECMA 版本。parser
: (物件
):用於剖析目前檔案的剖析器。parserOptions
: (物件
) 為此檔案設定的剖析器選項。globals
: (物件
) 指定的全域變數。
parserPath
: (字串
,已移除 請改用context.languageOptions.parser
。) 設定中parser
的名稱。parserOptions
: (已棄用 請改用context.languageOptions.parserOptions
。) 為此執行設定的剖析器選項 (詳細資訊請參閱此處)。
此外,context
物件具有下列方法
getCwd()
: (已棄用:請改用context.cwd
。) 傳回傳遞給 Linter 的cwd
選項。它是應該被視為目前工作目錄的目錄路徑。getFilename()
: (已棄用:請改用context.filename
。) 傳回與來源相關聯的檔案名稱。getPhysicalFilename()
: (已棄用:請改用context.physicalFilename
。) 當檢查檔案時,它會傳回檔案在磁碟上的完整路徑,而沒有任何程式碼區塊資訊。當檢查文字時,它會傳回傳遞給—stdin-filename
的值,如果未指定,則傳回<text>
。getSourceCode()
: (已棄用:請改用context.sourceCode
。) 傳回您可以用來處理傳遞給 ESLint 的來源的SourceCode
物件 (請參閱存取原始碼)。report(descriptor)
。回報程式碼中的問題 (請參閱專用章節)。
注意:較早版本的 ESLint 支援 context
物件上的其他方法。這些方法已在新格式中移除,不應依賴。
回報問題
撰寫自訂規則時,您會使用的主要方法是 context.report()
,它會發布警告或錯誤 (取決於所使用的設定)。此方法接受單一引數,該引數是包含下列屬性的物件
messageId
: (字串
) 訊息的 ID (請參閱messageIds) (建議優先於message
)。message
: (字串
) 問題訊息 (替代messageId
)。node
: (選擇性物件
) 與問題相關的 AST 節點。如果存在且未指定loc
,則會使用節點的起始位置作為問題的位置。loc
: (選擇性物件
) 指定問題的位置。如果同時指定loc
和node
,則會使用loc
中的位置,而不是node
中的位置。start
:起始位置的物件。line
: (數字
) 發生問題的 1 基準行號。column
: (數字
) 發生問題的 0 基準欄號。
end
:結束位置的物件。line
: (數字
) 發生問題的 1 基準行號。column
: (數字
) 發生問題的 0 基準欄號。
data
: (選擇性物件
)message
的 佔位符資料。fix(fixer)
: (選擇性函式
) 套用修正來解決問題。
請注意,至少需要 `node` 或 `loc` 其中一個。
最簡單的例子是僅使用 `node` 和 `message`。
context.report({
node: node,
message: "Unexpected identifier"
});
節點包含所有必要的資訊,以找出違規文字的行號、列號,以及代表節點的原始文字。
使用訊息佔位符
您也可以在訊息中使用佔位符,並提供 `data`。
context.report({
node: node,
message: "Unexpected identifier: {{ identifier }}",
data: {
identifier: node.name
}
});
請注意,訊息參數中開頭和結尾的空白是可選的。
節點包含所有必要的資訊,以找出違規文字的行號、列號,以及代表節點的原始文字。
`messageId`s
建議使用 `messageId`s 作為在 `context.report()` 呼叫中報告訊息的方法,因為它有以下優點:
- 規則違規訊息可以儲存在中央的 `meta.messages` 物件中,方便管理。
- 規則違規訊息不需要在規則檔案和規則測試檔案中重複。
- 因此,變更規則違規訊息的門檻較低,鼓勵更頻繁地貢獻,以改進和優化它們,使其達到最大的清晰度和實用性。
規則檔案
// avoid-name.js
module.exports = {
meta: {
messages: {
avoidName: "Avoid using variables named '{{ name }}'"
}
},
create(context) {
return {
Identifier(node) {
if (node.name === "foo") {
context.report({
node,
messageId: "avoidName",
data: {
name: "foo",
}
});
}
}
};
}
};
在要進行靜態檢查的檔案中
// someFile.js
var foo = 2;
// ^ error: Avoid using variables named 'foo'
在您的測試中
// avoid-name.test.js
var rule = require("../../../lib/rules/avoid-name");
var RuleTester = require("eslint").RuleTester;
var ruleTester = new RuleTester();
ruleTester.run("avoid-name", rule, {
valid: ["bar", "baz"],
invalid: [
{
code: "foo",
errors: [
{
messageId: "avoidName"
}
]
}
]
});
套用修正
如果您希望 ESLint 嘗試修復您報告的問題,您可以在使用 `context.report()` 時指定 `fix` 函式。 `fix` 函式接收一個單一參數,即一個 `fixer` 物件,您可以使用它來應用修復。例如:
context.report({
node: node,
message: "Missing semicolon",
fix(fixer) {
return fixer.insertTextAfter(node, ";");
}
});
在這裡,`fix()` 函式用於在節點後插入分號。請注意,修復不會立即套用,如果與其他修復發生衝突,則可能根本不會套用。套用修復後,ESLint 會在修復後的程式碼上再次執行所有啟用的規則,可能會套用更多修復。此過程將重複最多 10 次,或直到不再發現可修復的問題。之後,任何剩餘的問題將照常報告。
重要: `meta.fixable` 屬性對於可修復的規則是強制性的。如果實作了 `fix` 函式的規則沒有匯出 `meta.fixable` 屬性,ESLint 將會拋出錯誤。
`fixer` 物件具有以下方法:
- `insertTextAfter(nodeOrToken, text)`:在給定的節點或語彙單元之後插入文字。
- `insertTextAfterRange(range, text)`:在給定的範圍之後插入文字。
- `insertTextBefore(nodeOrToken, text)`:在給定的節點或語彙單元之前插入文字。
- `insertTextBeforeRange(range, text)`:在給定的範圍之前插入文字。
- `remove(nodeOrToken)`:移除給定的節點或語彙單元。
- `removeRange(range)`:移除給定範圍內的文字。
- `replaceText(nodeOrToken, text)`:替換給定節點或語彙單元中的文字。
- `replaceTextRange(range, text)`:替換給定範圍內的文字。
`range` 是一個包含原始碼中字元索引的雙元素陣列。第一個元素是範圍的開始(包含),第二個元素是範圍的結束(不包含)。每個節點和語彙單元都有一個 `range` 屬性來識別它們所代表的原始碼範圍。
上述方法會回傳一個 `fixing` 物件。 `fix()` 函式可以回傳下列值:
- 一個 `fixing` 物件。
- 一個包含 `fixing` 物件的陣列。
- 一個可列舉 `fixing` 物件的可迭代物件。特別是,`fix()` 函式可以是產生器。
如果您建立一個回傳多個 `fixing` 物件的 `fix()` 函式,這些 `fixing` 物件不得重疊。
修復的最佳實務
- 避免任何可能變更程式碼執行時行為並導致其停止運作的修復。
- 盡可能使修復的範圍越小越好。過大的修復可能會與其他修復衝突,並阻止它們被套用。
- 每個訊息只進行一次修復。這是強制執行的,因為您必須從 `fix()` 回傳修復器操作的結果。
- 由於在套用初始修復後會再次執行所有規則,因此規則沒有必要檢查修復的程式碼樣式是否會導致另一個規則報告錯誤。
-
例如,假設一個修復器想要用引號括住物件鍵,但不確定使用者是偏好單引號還是雙引號。
({ foo : 1 }) // should get fixed to either ({ 'foo': 1 }) // or ({ "foo": 1 })
-
此修復器可以隨意選擇引號類型。如果猜錯了,結果程式碼將會被 `quotes` 規則自動報告和修復。
-
注意:盡可能縮小修復範圍是一種最佳實務,但在某些情況下,為了故意防止其他規則在同一次傳遞中在周圍範圍內進行修復,可能需要擴展修復的範圍。例如,如果替換文字宣告了一個新的變數,防止該變數範圍內的其他變更可能會很有用,因為它們可能會導致名稱衝突。
以下範例替換了 `node`,同時確保在同一次傳遞中不會在 `node.parent` 的範圍內套用其他修復。
context.report({
node,
message,
*fix(fixer) {
yield fixer.replaceText(node, replacementText);
// extend range of the fix to the range of `node.parent`
yield fixer.insertTextBefore(node.parent, "");
yield fixer.insertTextAfter(node.parent, "");
}
});
衝突的修正
衝突的修復是指對原始碼的相同部分套用不同變更的修復。無法指定套用哪個衝突的修復。
例如,如果有兩個修復想要修改字元 0 到 5,則只會套用其中一個。
提供建議
在某些情況下,修復並不適合自動套用,例如,如果修復可能會變更功能,或者根據實作意圖,有多種有效的方法可以修復規則(請參閱上面列出的套用修復的最佳實務)。在這些情況下,`context.report()` 上有一個替代的 `suggest` 選項,允許其他工具(例如編輯器)向使用者顯示輔助工具以手動套用建議。
若要提供建議,請在 report 參數中使用帶有建議物件陣列的 `suggest` 鍵。建議物件代表可以套用的個別建議,並且需要一個描述套用建議會做什麼的 `desc` 鍵字串,或一個 `messageId` 鍵(請參閱下方),以及一個定義建議結果的 `fix` 鍵函式。這個 `fix` 函式遵循與一般修復相同的 API(在上面的套用修復中描述)。
context.report({
node: node,
message: "Unnecessary escape character: \\{{character}}.",
data: { character },
suggest: [
{
desc: "Remove the `\\`. This maintains the current functionality.",
fix: function(fixer) {
return fixer.removeRange(range);
}
},
{
desc: "Replace the `\\` with `\\\\` to include the actual backslash character.",
fix: function(fixer) {
return fixer.insertTextBeforeRange(range, "\\");
}
}
]
});
重要: `meta.hasSuggestions` 屬性對於提供建議的規則是強制性的。如果規則嘗試產生建議但沒有匯出此屬性,ESLint 將會拋出錯誤。
注意: 建議是作為獨立的變更套用的,不會觸發多次傳遞修復。每個建議都應該專注於程式碼中的單一變更,而不應嘗試符合使用者定義的樣式。例如,如果建議在程式碼庫中新增一個新的陳述式,則不應嘗試符合正確的縮排,或符合使用者對於分號存在/不存在的偏好。當使用者觸發時,所有這些事情都可以透過多次傳遞自動修復來更正。
建議的最佳實務
- 不要嘗試做太多,並建議可能引入大量破壞性變更的大型重構。
- 如上所述,不要嘗試符合使用者定義的樣式。
建議旨在提供修復。如果建議的 `fix` 函式回傳 `null` 或空陣列/序列,ESLint 會自動從靜態檢查輸出中移除整個建議。
建議 `messageId`s
可以使用 `messageId` 而不是為建議使用 `desc` 鍵。這與整體錯誤的 `messageId` 的工作方式相同(請參閱messageIds)。以下是在規則中使用建議 `messageId` 的範例:
module.exports = {
meta: {
messages: {
unnecessaryEscape: "Unnecessary escape character: \\{{character}}.",
removeEscape: "Remove the `\\`. This maintains the current functionality.",
escapeBackslash: "Replace the `\\` with `\\\\` to include the actual backslash character."
},
hasSuggestions: true
},
create: function(context) {
// ...
context.report({
node: node,
messageId: 'unnecessaryEscape',
data: { character },
suggest: [
{
messageId: "removeEscape", // suggestion messageId
fix: function(fixer) {
return fixer.removeRange(range);
}
},
{
messageId: "escapeBackslash", // suggestion messageId
fix: function(fixer) {
return fixer.insertTextBeforeRange(range, "\\");
}
}
]
});
}
};
建議訊息中的佔位符
您也可以在建議訊息中使用佔位符。這與整體錯誤的佔位符的工作方式相同(請參閱使用訊息佔位符)。
請注意,您必須在建議的物件上提供 `data`。建議訊息不能使用整體錯誤的 `data` 中的屬性。
module.exports = {
meta: {
messages: {
unnecessaryEscape: "Unnecessary escape character: \\{{character}}.",
removeEscape: "Remove `\\` before {{character}}.",
},
hasSuggestions: true
},
create: function(context) {
// ...
context.report({
node: node,
messageId: "unnecessaryEscape",
data: { character }, // data for the unnecessaryEscape overall message
suggest: [
{
messageId: "removeEscape",
data: { character }, // data for the removeEscape suggestion message
fix: function(fixer) {
return fixer.removeRange(range);
}
}
]
});
}
};
存取傳遞給規則的選項
有些規則需要選項才能正確運作。這些選項會出現在設定中(`.eslintrc`、命令列介面或註解)。例如:
{
"quotes": ["error", "double"]
}
此範例中的 `quotes` 規則有一個選項,`"double"`(`error` 是錯誤級別)。您可以使用 `context.options` 擷取規則的選項,這是一個包含規則所有已設定選項的陣列。在此情況下,`context.options[0]` 將包含 `"double"`。
module.exports = {
meta: {
schema: [
{
enum: ["single", "double", "backtick"]
}
]
},
create: function(context) {
var isDouble = (context.options[0] === "double");
// ...
}
};
由於 `context.options` 只是一個陣列,因此您可以使用它來確定已傳遞多少選項,以及擷取實際的選項本身。請記住,錯誤級別不是 `context.options` 的一部分,因為無法從規則內部得知或修改錯誤級別。
使用選項時,請確保您的規則在未提供選項的情況下有一些邏輯預設值。
具有選項的規則必須指定一個綱要。
存取原始碼
`SourceCode` 物件是取得有關正在進行靜態檢查的原始碼之更多資訊的主要物件。您隨時可以使用 `context.sourceCode` 屬性擷取 `SourceCode` 物件。
module.exports = {
create: function(context) {
var sourceCode = context.sourceCode;
// ...
}
};
已棄用: `context.getSourceCode()` 方法已棄用;請務必改用 `context.sourceCode` 屬性。
一旦您有了 `SourceCode` 的實例,您可以使用其上的以下方法來處理程式碼:
- `getText(node)`:回傳給定節點的原始碼。省略 `node` 以取得整個原始碼(請參閱專門章節)。
- `getAllComments()`:回傳原始碼中所有註解的陣列(請參閱專門章節)。
- `getCommentsBefore(nodeOrToken)`:回傳直接位於給定節點或語彙單元之前的註解語彙單元陣列(請參閱專門章節)。
- `getCommentsAfter(nodeOrToken)`:回傳直接位於給定節點或語彙單元之後的註解語彙單元陣列(請參閱專門章節)。
- `getCommentsInside(node)`:回傳給定節點內的所有註解語彙單元陣列(請參閱專門章節)。
- `isSpaceBetween(nodeOrToken, nodeOrToken)`:如果兩個語彙單元之間有空白字元,或者如果給定節點,則第一個節點的最後一個語彙單元和第二個節點的第一個語彙單元之間有空白字元,則回傳 true。
- `getFirstToken(node, skipOptions)`:回傳代表給定節點的第一個語彙單元。
- `getFirstTokens(node, countOptions)`:回傳代表給定節點的前 `count` 個語彙單元。
- `getLastToken(node, skipOptions)`:回傳代表給定節點的最後一個語彙單元。
- `getLastTokens(node, countOptions)`:回傳代表給定節點的最後 `count` 個語彙單元。
- `getTokenAfter(nodeOrToken, skipOptions)`:回傳給定節點或語彙單元之後的第一個語彙單元。
- `getTokensAfter(nodeOrToken, countOptions)`:回傳給定節點或語彙單元之後的 `count` 個語彙單元。
- `getTokenBefore(nodeOrToken, skipOptions)`:回傳給定節點或語彙單元之前的第一個語彙單元。
- `getTokensBefore(nodeOrToken, countOptions)`:回傳給定節點或語彙單元之前的 `count` 個語彙單元。
- `getFirstTokenBetween(nodeOrToken1, nodeOrToken2, skipOptions)`:回傳兩個節點或語彙單元之間的第一個語彙單元。
- `getFirstTokensBetween(nodeOrToken1, nodeOrToken2, countOptions)`:回傳兩個節點或語彙單元之間的前 `count` 個語彙單元。
- `getLastTokenBetween(nodeOrToken1, nodeOrToken2, skipOptions)`:回傳兩個節點或語彙單元之間的最後一個語彙單元。
- `getLastTokensBetween(nodeOrToken1, nodeOrToken2, countOptions)`:回傳兩個節點或語彙單元之間的最後 `count` 個語彙單元。
- `getTokens(node)`:回傳給定節點的所有語彙單元。
- `getTokensBetween(nodeOrToken1, nodeOrToken2)`:回傳兩個節點之間的所有語彙單元。
getTokenByRangeStart(index, rangeOptions)
:傳回一個 token,其範圍在原始碼中以給定的索引開始。getNodeByRangeIndex(index)
:傳回 AST 中包含給定原始碼索引的最深層節點。getLocFromIndex(index)
:傳回一個物件,其中包含line
和column
屬性,對應於給定原始碼索引的位置。line
是從 1 開始的,而column
是從 0 開始的。getIndexFromLoc(loc)
:傳回原始碼中給定位置的索引,其中loc
是一個物件,包含從 1 開始的line
鍵和從 0 開始的column
鍵。commentsExistBetween(nodeOrToken1, nodeOrToken2)
:如果兩個節點之間存在註解,則傳回true
。getAncestors(node)
:傳回給定節點的祖先陣列,從 AST 的根節點開始,並一路到給定節點的直接父節點。此陣列不包含給定節點本身。getDeclaredVariables(node)
:傳回由給定節點宣告的變數列表。此資訊可用於追蹤對變數的引用。- 如果節點是
VariableDeclaration
,則會傳回宣告中宣告的所有變數。 - 如果節點是
VariableDeclarator
,則會傳回宣告器中宣告的所有變數。 - 如果節點是
FunctionDeclaration
或FunctionExpression
,則會傳回函數名稱的變數,以及函數參數的變數。 - 如果節點是
ArrowFunctionExpression
,則會傳回參數的變數。 - 如果節點是
ClassDeclaration
或ClassExpression
,則會傳回類別名稱的變數。 - 如果節點是
CatchClause
,則會傳回異常的變數。 - 如果節點是
ImportDeclaration
,則會傳回其所有說明符的變數。 - 如果節點是
ImportSpecifier
、ImportDefaultSpecifier
或ImportNamespaceSpecifier
,則會傳回宣告的變數。 - 否則,如果節點未宣告任何變數,則會傳回空陣列。
- 如果節點是
getScope(node)
:傳回給定節點的作用域。此資訊可用於追蹤對變數的引用。markVariableAsUsed(name, refNode)
:將給定參考節點所指示的作用域中,具有給定名稱的變數標記為已使用。這會影響 no-unused-vars 規則。如果找到具有給定名稱的變數並標記為已使用,則傳回true
,否則傳回false
。
skipOptions
是一個物件,其中包含 3 個屬性:skip
、includeComments
和 filter
。預設值為 {skip: 0, includeComments: false, filter: null}
。
skip
:(number
) 正整數,表示要跳過的 token 數量。如果同時給定filter
選項,則不會將篩選過的 token 計算為已跳過。includeComments
:(boolean
) 將註解 token 包含在結果中的旗標。filter(token)
:一個函數,它將 token 作為第一個引數取得。如果函數傳回false
,則結果會排除該 token。
countOptions
是一個物件,其中包含 3 個屬性:count
、includeComments
和 filter
。預設值為 {count: 0, includeComments: false, filter: null}
。
count
:(number
) 正整數,表示要傳回的最大 token 數量。includeComments
:(boolean
) 將註解 token 包含在結果中的旗標。filter(token)
:一個函數,它將 token 作為第一個引數取得,如果函數傳回false
,則結果會排除該 token。
rangeOptions
是一個物件,其中包含 1 個屬性:includeComments
。預設值為 {includeComments: false}
。
includeComments
:(boolean
) 將註解 token 包含在結果中的旗標。
您也可以存取一些屬性
hasBOM
:(boolean
) 指示原始碼是否具有 Unicode BOM 的旗標。text
:(string
) 正在進行 lint 的程式碼的完整文字。Unicode BOM 已從此文字中移除。ast
:(object
) 正在進行 lint 的程式碼的 AST 的Program
節點。scopeManager
:程式碼的 ScopeManager 物件。visitorKeys
:(object
) 用於遍歷此 AST 的訪問鍵。parserServices
:(object
) 包含規則的解析器提供的服務。預設解析器不提供任何服務。但是,如果規則旨在與自訂解析器一起使用,則可以使用parserServices
來存取該解析器提供的任何內容。(例如,TypeScript 解析器可以提供取得給定節點的計算類型的能力。)lines
:(array
) 根據規格的換行符號定義分割的行陣列。
當您需要取得有關正在進行 lint 的程式碼的更多資訊時,您應該使用 SourceCode
物件。
存取原始文字
如果您的規則需要取得實際的 JavaScript 原始碼來使用,則使用 sourceCode.getText()
方法。此方法的工作方式如下
// get all source
var source = sourceCode.getText();
// get source for just this AST node
var nodeSource = sourceCode.getText(node);
// get source for AST node plus previous two characters
var nodeSourceWithPrev = sourceCode.getText(node, 2);
// get source for AST node plus following two characters
var nodeSourceWithFollowing = sourceCode.getText(node, 0, 2);
透過這種方式,您可以在 AST 未提供適當資料(例如逗號、分號、括號等的位置)時,尋找 JavaScript 文字本身的模式。
存取註解
雖然註解在技術上不是 AST 的一部分,但 ESLint 提供 sourceCode.getAllComments()
、sourceCode.getCommentsBefore()
、sourceCode.getCommentsAfter()
和 sourceCode.getCommentsInside()
來存取它們。
sourceCode.getCommentsBefore()
、sourceCode.getCommentsAfter()
和 sourceCode.getCommentsInside()
對於需要檢查與給定節點或 token 相關的註解的規則非常有用。
請記住,這些方法的結果是根據需要計算的。
您也可以使用 includeComments
選項,透過 sourceCode
的許多方法來存取註解。
選項綱要
具有選項的規則必須指定 meta.schema
屬性,該屬性是以 JSON Schema 格式描述規則選項的描述,ESLint 將使用該描述來驗證設定選項,並防止在將它們傳遞到 context.options
中的規則之前出現無效或意外的輸入。
如果您的規則具有選項,強烈建議您為選項驗證指定 schema。但是,可以透過設定 schema: false
來選擇不進行選項驗證,但不建議這樣做,因為這會增加錯誤和錯誤的可能性。
對於未指定 meta.schema
屬性的規則,當傳遞任何選項時,ESLint 會擲回錯誤。如果您的規則沒有選項,請不要設定 schema: false
,而只是省略 schema 屬性或使用 schema: []
,這兩者都會防止傳遞任何選項。
驗證規則的設定時,有五個步驟
- 如果規則設定不是陣列,則將該值包裝到陣列中(例如,
"off"
變成["off"]
);如果規則設定是陣列,則直接使用它。 - ESLint 會將規則設定陣列的第一個元素驗證為嚴重性(
"off"
、"warn"
、"error"
、0
、1
、2
) - 如果嚴重性為
off
或0
,則會停用規則,且驗證會停止,並忽略規則設定陣列的任何其他元素。 - 如果規則已啟用,則嚴重性之後的陣列的任何元素都會複製到
context.options
陣列中(例如,設定["warn", "never", { someOption: 5 }]
會產生context.options = ["never", { someOption: 5 }]
) - 規則的 schema 驗證會在
context.options
陣列上執行。
注意:這表示規則 schema 無法驗證嚴重性。規則 schema 僅驗證規則設定中嚴重性之後的陣列元素。規則無法知道它配置的嚴重性。
規則的 schema
有兩種格式
- JSON Schema 物件的陣列
- 每個元素都會針對
context.options
陣列中的相同位置進行檢查。 - 如果
context.options
陣列的元素少於 schema 的數量,則會忽略不符的 schema - 如果
context.options
陣列的元素多於 schema 的數量,則驗證會失敗 - 使用此格式有兩個重要的後果
- 使用者始終可以不為您的規則提供任何選項(超出嚴重性)
- 如果您指定一個空陣列,則使用者為您的規則提供任何選項(超出嚴重性)始終會是一個錯誤
- 每個元素都會針對
- 將驗證
context.options
陣列的完整 JSON Schema 物件
例如,yoda
規則接受 "always"
或 "never"
的主要模式引數,以及具有可選屬性 exceptRange
的額外選項物件
// Valid configuration:
// "yoda": "warn"
// "yoda": ["error"]
// "yoda": ["error", "always"]
// "yoda": ["error", "never", { "exceptRange": true }]
// Invalid configuration:
// "yoda": ["warn", "never", { "exceptRange": true }, 5]
// "yoda": ["error", { "exceptRange": true }, "never"]
module.exports = {
meta: {
schema: [
{
enum: ["always", "never"]
},
{
type: "object",
properties: {
exceptRange: { type: "boolean" }
},
additionalProperties: false
}
]
}
};
以下是等效的基於物件的 schema
// Valid configuration:
// "yoda": "warn"
// "yoda": ["error"]
// "yoda": ["error", "always"]
// "yoda": ["error", "never", { "exceptRange": true }]
// Invalid configuration:
// "yoda": ["warn", "never", { "exceptRange": true }, 5]
// "yoda": ["error", { "exceptRange": true }, "never"]
module.exports = {
meta: {
schema: {
type: "array",
minItems: 0,
maxItems: 2,
items: [
{
enum: ["always", "never"]
},
{
type: "object",
properties: {
exceptRange: { type: "boolean" }
},
additionalProperties: false
}
]
}
}
};
物件 schema 在允許的內容方面可以更精確和限制性。例如,以下 schema 始終要求指定第一個選項(介於 0 到 10 之間的數字),但第二個選項是可選的,並且可以是明確設定某些選項的物件,也可以是 "off"
或 "strict"
。
// Valid configuration:
// "someRule": ["error", 6]
// "someRule": ["error", 5, "strict"]
// "someRule": ["warn", 10, { someNonOptionalProperty: true }]
// Invalid configuration:
// "someRule": "warn"
// "someRule": ["error"]
// "someRule": ["warn", 15]
// "someRule": ["warn", 7, { }]
// "someRule": ["error", 3, "on"]
// "someRule": ["warn", 7, { someOtherProperty: 5 }]
// "someRule": ["warn", 7, { someNonOptionalProperty: false, someOtherProperty: 5 }]
module.exports = {
meta: {
schema: {
type: "array",
minItems: 1, // Can't specify only severity!
maxItems: 2,
items: [
{
type: "number",
minimum: 0,
maximum: 10
},
{
anyOf: [
{
type: "object",
properties: {
someNonOptionalProperty: { type: "boolean" }
},
required: ["someNonOptionalProperty"],
additionalProperties: false
},
{
enum: ["off", "strict"]
}
]
}
]
}
}
}
請記住,規則選項始終是一個陣列,因此請小心不要在頂層指定非陣列類型的 schema。如果您的 schema 未在頂層指定陣列,則使用者永遠無法啟用您的規則,因為當啟用規則時,他們的設定將始終無效。
以下是一個將始終失敗驗證的範例 schema
// Possibly trying to validate ["error", { someOptionalProperty: true }]
// but when the rule is enabled, config will always fail validation because the options are an array which doesn't match "object"
module.exports = {
meta: {
schema: {
type: "object",
properties: {
someOptionalProperty: {
type: "boolean"
}
},
additionalProperties: false
}
}
}
注意: 如果您的規則 schema 使用 JSON schema $ref
屬性,您必須使用完整的 JSON Schema 物件,而不是位置屬性 schema 的陣列。這是因為 ESLint 會將陣列速記轉換為單個 schema,而不會更新使其不正確的參考(它們會被忽略)。
若要瞭解更多有關 JSON Schema 的資訊,我們建議您查看 JSON Schema 網站上的一些範例,或閱讀免費的 瞭解 JSON Schema 電子書。
選項預設值
規則可能會為任何選項指定 meta.defaultOptions
陣列的預設值。當在使用者設定中啟用規則時,ESLint 會以遞迴方式將使用者提供的任何選項元素合併到預設元素之上。
例如,給定以下預設值
export default {
meta: {
defaultOptions: [{
alias: "basic",
}],
schema: [{
type: "object",
properties: {
alias: {
type: "string"
}
},
additionalProperties: false
}]
},
create(context) {
const [{ alias }] = context.options;
return { /* ... */ };
}
}
除非使用者設定指定不同的值(例如 ["error", { alias: "complex" }]
),否則規則的執行階段 alias
值將為 "basic"
。
選項陣列的每個元素都會根據以下規則合併
- 任何遺失的值或明確的使用者提供的
undefined
都會回退到預設選項 - 使用者提供的陣列和除了
undefined
以外的基本值會覆寫預設選項 - 使用者提供的物件將合併到預設選項物件中,否則會取代非物件預設值
選項預設值也將根據規則的 meta.schema
進行驗證。
注意: ESLint 在內部使用 Ajv 進行 schema 驗證,並啟用其 useDefaults
選項。使用者提供和 meta.defaultOptions
選項都會覆寫規則的 schema 中指定的任何預設值。ESLint 可能會在未來的主要版本中停用 Ajv 的 useDefaults
。
存取 Shebangs
Shebangs (#!) 以 "Shebang"
類型的獨特 token 表示。它們被視為註解,並且可以透過存取註解章節中概述的方法存取,例如 sourceCode.getAllComments()
。
存取變數作用域
SourceCode#getScope(node)
方法會回傳給定節點的作用域。這個方法對於尋找給定作用域中變數的資訊,以及它們如何在其他作用域中使用非常有用。
作用域類型
下表包含 AST 節點類型及其對應的作用域類型。有關作用域類型的更多資訊,請參閱 Scope
物件文件。
AST 節點類型 | 作用域類型 |
---|---|
Program |
global |
FunctionDeclaration |
function |
FunctionExpression |
function |
ArrowFunctionExpression |
function |
ClassDeclaration |
class |
ClassExpression |
class |
BlockStatement ※1 |
block |
SwitchStatement ※1 |
switch |
ForStatement ※2 |
for |
ForInStatement ※2 |
for |
ForOfStatement ※2 |
for |
WithStatement |
with |
CatchClause |
catch |
others | ※3 |
※1 僅當設定的解析器提供區塊作用域功能時。如果 parserOptions.ecmaVersion
不小於 6
,則預設解析器會提供區塊作用域功能。
※2 僅當 for
語句將迭代變數定義為區塊作用域變數時(例如,for (let i = 0;;) {}
)。
※3 具有自身作用域的最近祖先節點的作用域。如果最近的祖先節點有多個作用域,則選擇最內層的作用域(例如,如果 Program#sourceType
為 "module"
,則 Program
節點具有 global
作用域和 module
作用域。最內層的作用域是 module
作用域)。
作用域變數
Scope#variables
屬性包含 Variable
物件的陣列。這些是在目前作用域中宣告的變數。您可以使用這些 Variable
物件來追蹤整個模組中對變數的引用。
在每個 Variable
內部,Variable#references
屬性包含 Reference
物件的陣列。Reference
陣列包含變數在模組原始碼中被引用的所有位置。
在每個 Variable
內部,Variable#defs
屬性也包含 Definition
物件的陣列。您可以使用 Definitions
來尋找變數的定義位置。
全域變數具有以下額外屬性
Variable#writeable
(boolean | undefined
) … 如果為true
,則此全域變數可以被賦予任意值。如果為false
,則此全域變數為唯讀。Variable#eslintExplicitGlobal
(boolean | undefined
) … 如果為true
,則此全域變數是由原始碼檔案中的/* globals */
指令註解所定義。Variable#eslintExplicitGlobalComments
(Comment[] | undefined
) … 在原始碼檔案中定義此全域變數的/* globals */
指令註解陣列。如果沒有/* globals */
指令註解,則此屬性為undefined
。Variable#eslintImplicitGlobalSetting
("readonly" | "writable" | undefined
) … 在設定檔中設定的值。如果有/* globals */
指令註解,則這可能會與variable.writeable
不同。
有關使用 SourceCode#getScope()
來追蹤變數的範例,請參閱以下內建規則的原始碼
- no-shadow:在
Program
節點上呼叫sourceCode.getScope()
並檢查所有子作用域,以確保變數名稱不會在較低的作用域中重複使用。( no-shadow 文件 ) - no-redeclare:在每個作用域呼叫
sourceCode.getScope()
,以確保變數不會在同一作用域中宣告兩次。( no-redeclare 文件 )
將變數標記為已使用
某些 ESLint 規則,例如 no-unused-vars
,會檢查變數是否已被使用。ESLint 本身只知道變數存取的標準規則,因此自訂的變數存取方式可能不會被註冊為「已使用」。
為了協助處理這種情況,您可以使用 sourceCode.markVariableAsUsed()
方法。這個方法接受兩個參數:要標記為已使用的變數名稱,以及一個可選的參考節點,表示您正在處理的作用域。這裡有一個範例
module.exports = {
create: function(context) {
var sourceCode = context.sourceCode;
return {
ReturnStatement(node) {
// look in the scope of the function for myCustomVar and mark as used
sourceCode.markVariableAsUsed("myCustomVar", node);
// or: look in the global scope for myCustomVar and mark as used
sourceCode.markVariableAsUsed("myCustomVar");
}
}
// ...
}
};
在這裡,相對於 ReturnStatement
節點,myCustomVar
變數被標記為已使用,這表示 ESLint 將從最接近該節點的作用域開始搜尋。如果您省略第二個參數,則會使用頂層作用域。(對於 ESM 檔案,頂層作用域是模組作用域;對於 CommonJS 檔案,頂層作用域是第一個函式作用域。)
存取程式碼路徑
ESLint 在遍歷 AST 時會分析程式碼路徑。您可以使用與程式碼路徑相關的七個事件來存取程式碼路徑物件。有關更多資訊,請參閱 程式碼路徑分析。
已棄用的 SourceCode
方法
請注意,以下 SourceCode
方法已棄用,並將在未來版本的 ESLint 中移除
getTokenOrCommentBefore()
:已由SourceCode#getTokenBefore()
和{ includeComments: true }
選項取代。getTokenOrCommentAfter()
:已由SourceCode#getTokenAfter()
和{ includeComments: true }
選項取代。isSpaceBetweenTokens()
:已由SourceCode#isSpaceBetween()
取代getJSDocComment()
規則單元測試
ESLint 提供 RuleTester
公用程式,讓您輕鬆撰寫規則的測試。
規則命名慣例
雖然您可以為自訂規則指定任何您想要的名稱,但核心規則有命名慣例。將相同的命名慣例套用至您的自訂規則可能會更清楚。若要瞭解更多資訊,請參閱 核心規則命名慣例 文件。
執行階段規則
ESLint 與其他程式碼檢查工具不同之處在於,它可以在執行階段定義自訂規則。這非常適合於您的專案或公司特定的規則,而這些規則對於 ESLint 來說,沒有必要隨附或包含在外掛程式中。只需撰寫您的規則,並在執行階段包含它們即可。
執行階段規則的撰寫格式與所有其他規則相同。像其他任何規則一樣建立您的規則,然後執行以下步驟
- 將您所有的執行階段規則放在同一個目錄中(例如,
eslint_rules
)。 - 建立一個設定檔,並在
rules
鍵下指定您的規則 ID 錯誤層級。您的規則必須在設定檔中具有"warn"
或"error"
值才會執行。 - 使用
--rulesdir
選項指定您的執行階段規則位置,執行命令列介面。
設定規則效能
ESLint 具有追蹤個別規則效能的內建方法。設定 TIMING
環境變數將會在程式碼檢查完成後,觸發顯示執行時間最長的十個規則,以及它們的個別執行時間(規則建立 + 規則執行)和相對於總規則處理時間的效能影響百分比(規則建立 + 規則執行)。
$ TIMING=1 eslint lib
Rule | Time (ms) | Relative
:-----------------------|----------:|--------:
no-multi-spaces | 52.472 | 6.1%
camelcase | 48.684 | 5.7%
no-irregular-whitespace | 43.847 | 5.1%
valid-jsdoc | 40.346 | 4.7%
handle-callback-err | 39.153 | 4.6%
space-infix-ops | 35.444 | 4.1%
no-undefined | 25.693 | 3.0%
no-shadow | 22.759 | 2.7%
no-empty-class | 21.976 | 2.6%
semi | 19.359 | 2.3%
若要明確測試單個規則,請結合 --no-eslintrc
和 --rule
選項
$ TIMING=1 eslint --no-eslintrc --rule "quotes: [2, 'double']" lib
Rule | Time (ms) | Relative
:------|----------:|--------:
quotes | 18.066 | 100.0%
若要查看更長的結果清單(超過 10 個),請將環境變數設定為其他值,例如 TIMING=50
或 TIMING=all
。
若要取得更精細的時序資訊(每個檔案每個規則),請改用 stats
選項。