自訂規則
您可以建立自訂規則以搭配 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
: (object
) 包含規則的中繼資料
-
type
: (string
) 指示規則的類型,為"problem"
、"suggestion"
或"layout"
之一"problem"
: 規則正在識別可能會導致錯誤或可能導致混淆行為的程式碼。開發人員應將此視為高優先順序來解決。"suggestion"
: 規則正在識別可以做得更好但程式碼未變更也不會發生錯誤的事情。"layout"
: 規則主要關心空白字元、分號、逗號和括號,所有程式中決定程式碼外觀而非執行方式的部分。這些規則適用於 AST 中未指定的部分程式碼。
-
docs
: (object
) 通常用於文件產生和工具的屬性。核心規則為必要項,自訂規則為選用項。自訂規則可以根據需要在這裡包含其他屬性。description
: (string
) 提供規則的簡短描述。對於核心規則,這用於規則索引。recommended
: (boolean
) 對於核心規則,這指定規則是否由@eslint/js
中的recommended
設定啟用。url
: (string
) 指定可以存取完整文件的 URL。程式碼編輯器通常使用此屬性在醒目的規則違規上提供有用的連結。
-
fixable
: (string
) 如果 命令列上的--fix
選項自動修正規則回報的問題,則為"code"
或"whitespace"
。重要:
fixable
屬性對於可修正的規則是強制性的。如果未指定此屬性,則每當規則嘗試產生修正時,ESLint 都會擲回錯誤。如果規則不可修正,請省略fixable
屬性。 -
hasSuggestions
: (boolean
) 指定規則是否可以傳回建議(如果省略,則預設為false
)。重要:
hasSuggestions
屬性對於提供建議的規則是強制性的。如果此屬性未設定為true
,則每當規則嘗試產生建議時,ESLint 都會擲回錯誤。如果規則不提供建議,請省略hasSuggestions
屬性。 -
schema
: (object | array | false
) 指定選項,以便 ESLint 可以防止無效的規則設定。當規則具有選項時為強制性。 -
defaultOptions
: (array
) 指定規則的預設選項。如果存在,則使用者在其設定中提供的任何選項都將以遞迴方式合併到這些選項之上。 -
deprecated
: (boolean | DeprecatedInfo
) 指示規則是否已棄用。如果規則尚未棄用,您可以省略deprecated
屬性。
有一個專門介紹 DeprecatedInfo 的頁面 -
replacedBy
: (array
, 已棄用 請改用meta.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
: (string
) 規則 ID。filename
: (string
) 與原始碼相關聯的檔案名稱。physicalFilename
: (string
) 當檢查檔案時,它提供磁碟上檔案的完整路徑,而沒有任何程式碼區塊資訊。當檢查文字時,如果未指定,則提供傳遞至—stdin-filename
或<text>
的值。cwd
: (string
) 傳遞至 Linter 的cwd
選項。它是應該被視為目前工作目錄的目錄路徑。options
: (array
) 此規則的設定選項陣列。此陣列不包含規則嚴重性(請參閱專門章節)。sourceCode
: (object
) 您可以用來處理傳遞至 ESLint 的原始碼的SourceCode
物件(請參閱存取原始碼)。settings
: (object
) 來自設定的共用設定。languageOptions
: (object
) 每個屬性的更多詳細資訊請參閱此處sourceType
: ('script' | 'module' | 'commonjs'
) 目前檔案的模式。ecmaVersion
: (number
) 用於剖析目前檔案的 ECMA 版本。parser
: (object
): 用於剖析目前檔案的剖析器。parserOptions
: (object
) 為此檔案設定的剖析器選項。globals
: (object
) 指定的全域變數。
parserPath
: (string
, 已移除 請改用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
: (string
) 訊息的 ID(請參閱 messageIds)(建議使用,優於message
)。message
: (string
) 問題訊息(messageId
的替代方案)。node
: (選用object
) 與問題相關的 AST 節點。如果存在且未指定loc
,則節點的起始位置會用作問題的位置。loc
: (選用object
) 指定問題的位置。如果同時指定loc
和node
,則位置會從loc
而非node
使用。start
: 起始位置的物件。line
: (number
) 問題發生的以 1 為基礎的行號。column
: (number
) 問題發生的以 0 為基礎的欄號。
end
: 結束位置的物件。line
: (number
) 問題發生的以 1 為基礎的行號。column
: (number
) 問題發生的以 0 為基礎的欄號。
data
: (選用object
)message
的預留位置資料。fix(fixer)
: (選用function
) 套用修正以解決問題。
請注意,至少需要 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()
傳回 fixer 作業的結果。 - 由於在套用初始一輪修正後,所有規則都會再次執行,因此規則沒有必要檢查修正的程式碼樣式是否會導致另一個規則回報錯誤。
-
例如,假設 fixer 想要用引號括住物件索引鍵,但不確定使用者是否偏好單引號或雙引號。
({ foo : 1 }) // should get fixed to either ({ 'foo': 1 }) // or ({ "foo": 1 })
-
此 fixer 可以隨意選擇引號類型。如果猜錯了,則產生的程式碼將會自動由
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
除了對建議使用 desc
索引鍵外,還可以改用 messageId
。這與整體錯誤的 messageId
s 工作方式相同(請參閱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)
: 傳回範圍從原始碼中給定索引開始的權杖。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
) 正整數,要跳過的權杖數目。如果同時給定filter
選項,則它不會將篩選的權杖計為已跳過。includeComments
: (boolean
) 將註解權杖包含在結果中的旗標。filter(token)
: 函式,將權杖作為第一個引數取得。如果函式傳回false
,則結果會排除權杖。
countOptions
是一個具有 3 個屬性的物件;count
、includeComments
和 filter
。預設值為 {count: 0, includeComments: false, filter: null}
。
count
: (number
) 正整數,要傳回的權杖最大數目。includeComments
: (boolean
) 將註解權杖包含在結果中的旗標。filter(token)
: 函式,將權杖作為第一個引數取得,如果函式傳回false
,則結果會排除權杖。
rangeOptions
是一個具有 1 個屬性的物件 includeComments
。預設值為 {includeComments: false}
。
includeComments
: (boolean
) 將註解權杖包含在結果中的旗標。
您還可以存取一些屬性
hasBOM
: (boolean
) 指示原始碼是否具有 Unicode BOM 的旗標。text
: (string
) 正在檢查的程式碼的完整文字。Unicode BOM 已從此文字中移除。ast
: (object
) 正在檢查的程式碼的 AST 的Program
節點。scopeManager
: 程式碼的 ScopeManager 物件。visitorKeys
: (object
) 遍歷此 AST 的訪問器索引鍵。parserServices
: (object
) 包含剖析器為規則提供的服務。預設剖析器不提供任何服務。但是,如果規則旨在與自訂剖析器一起使用,則可以使用parserServices
來存取該剖析器提供的任何內容。(例如,TypeScript 剖析器可以提供取得給定節點的計算類型的能力。)lines
: (array
) 行陣列,根據規格對換行符號的定義分割。
當您需要取得有關正在檢查的程式碼的更多資訊時,應使用 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()
對於需要檢查與給定節點或權杖相關的註解的規則很有用。
請記住,這些方法的結果是按需計算的。
您也可以使用 includeComments
選項透過 sourceCode
的許多方法存取註解。
選項結構描述
具有選項的規則必須指定 meta.schema
屬性,這是規則選項的 JSON Schema 格式描述,ESLint 將使用它來驗證設定選項,並防止在將無效或意外的輸入傳遞到 context.options
中的規則之前。
如果您的規則具有選項,強烈建議您為選項驗證指定結構描述。但是,可以選擇退出選項驗證,方法是設定 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 }]
) - 規則的結構描述驗證會在
context.options
陣列上執行。
注意:這表示規則 schema 無法驗證嚴重性 (severity)。規則 schema 僅驗證規則設定中嚴重性之後的陣列元素。規則無法得知它被設定的嚴重性層級。
規則的 schema
有兩種格式:
- JSON Schema 物件的陣列
- 每個元素都會根據
context.options
陣列中相同位置的元素進行檢查。 - 如果
context.options
陣列的元素數量少於 schema 的數量,則未匹配的 schema 會被忽略。 - 如果
context.options
陣列的元素數量多於 schema 的數量,則驗證會失敗。 - 使用此格式有兩個重要的後果:
- 對於使用者來說,為您的規則提供零個選項(除了嚴重性之外)永遠是有效的。
- 如果您指定一個空陣列,那麼對於使用者來說,為您的規則提供任何選項(除了嚴重性之外)永遠是一個錯誤。
- 每個元素都會根據
- 一個完整的 JSON Schema 物件,將驗證
context.options
陣列
例如,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 網站 上的一些範例,或閱讀免費的 Understanding 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 表示。它們被視為註解,並且可以使用 Accessing Comments 章節中概述的方法來存取,例如 sourceCode.getAllComments()
。
存取變數作用域
SourceCode#getScope(node)
方法會傳回給定節點的作用域 (scope)。這是一個有用的方法,用於查找有關給定作用域中變數以及它們如何在其他作用域中使用的資訊。
作用域類型
下表包含 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 僅當設定的 parser 提供區塊作用域 (block-scope) 功能時。如果 parserOptions.ecmaVersion
不小於 6
,則預設 parser 提供區塊作用域功能。
※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");
}
}
// ...
}
};
在這裡,myCustomVar
變數被標記為相對於 ReturnStatement
節點為已使用,這表示 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 與其他 linters 不同的地方在於,它能夠在執行時期定義自訂規則。這對於特定於您的專案或公司的規則非常有用,這些規則對於 ESLint 來說沒有意義隨附或包含在插件中。只需編寫您的規則並在執行時期包含它們即可。
執行時期規則的編寫格式與所有其他規則相同。像編寫任何其他規則一樣建立您的規則,然後按照以下步驟操作
- 將您的所有執行時期規則放在同一個目錄中(例如,
eslint_rules
)。 - 建立一個 設定檔,並在
rules
鍵下指定您的規則 ID 錯誤層級。除非您的規則在設定檔中的值為"warn"
或"error"
,否則它不會執行。 - 使用
--rulesdir
選項執行 命令列介面,以指定您的執行時期規則的位置。
分析規則效能
ESLint 具有內建方法來追蹤個別規則的效能。設定 TIMING
環境變數將在 linting 完成後觸發顯示執行時間最長的十個規則,以及它們各自的執行時間(規則建立 + 規則執行)和相對效能影響(佔規則總處理時間的百分比,規則建立 + 規則執行)。
$ 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
選項。