新增 Linter 規則
對 Oxlint 做出貢獻的最佳和最簡單方法是新增新的 linter 規則。
本指南將使用 ESLint 的 no-debugger
規則為例,引導您完成此過程。
提示
請先閱讀設定說明。
步驟 1:選擇規則
我們的Linter 產品計劃和進度議題追蹤了我們想要從現有 ESLint 外掛實作的所有規則狀態。從那裡選擇一個您感興趣的外掛,並找到一個尚未實作的規則。
大多數 ESLint 規則的文件頁面都包含指向規則原始碼的連結。以此作為參考將有助於您的實作。
步驟 2:規則生成
接下來,執行 rulegen 指令碼以生成新規則的樣板程式碼。
just new-rule no-debugger
這將在 crates/oxc_linter/rules/<外掛名稱>/<規則名稱>.rs
中建立一個新檔案,其中包含規則實作的開頭和從 ESLint 移植過來的所有測試案例。
對於屬於不同外掛的規則,您需要使用該外掛自己的 rulegen 指令碼。
提示
執行沒有參數的 just
以查看所有可用的指令。
just new-jest-rule [name] # for eslint-plugin-jest
just new-jsx-a11y-rule [name] # for eslint-plugin-jsx-a11y
just new-n-rule [name] # for eslint-plugin-n
just new-nextjs-rule [name] # for eslint-plugin-next
just new-oxc-rule [name] # for oxc's own rules
just new-promise-rule [name] # for eslint-plugin-promise
just new-react-rule [name] # for eslint-plugin-react and eslint-plugin-react-hooks
just new-ts-rule [name] # for @typescript-eslint/eslint-plugin
just new-unicorn-rule [name] # for eslint-plugin-unicorn
just new-vitest-rule [name] # for eslint-plugin-vitest
產生的檔案會像這樣
點擊展開
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{
context::LintContext,
fixer::{RuleFix, RuleFixer},
rule::Rule,
AstNode,
};
#[derive(Debug, Default, Clone)]
pub struct NoDebugger;
declare_oxc_lint!(
/// ### What it does
///
///
/// ### Why is this bad?
///
///
/// ### Examples
///
/// Examples of **incorrect** code for this rule:
/// ```js
/// FIXME: Tests will fail if examples are missing or syntactically incorrect.
/// ```
///
/// Examples of **correct** code for this rule:
/// ```js
/// FIXME: Tests will fail if examples are missing or syntactically incorrect.
/// ```
NoDebugger,
nursery, // TODO: change category to `correctness`, `suspicious`, `pedantic`, `perf`, `restriction`, or `style`
// See <https://oxc.dev.org.tw/docs/contribute/linter.html#rule-category> for details
pending // TODO: describe fix capabilities. Remove if no fix can be done,
// keep at 'pending' if you think one could be added but don't know how.
// Options are 'fix', 'fix_dangerous', 'suggestion', and 'conditional_fix_suggestion'
);
impl Rule for NoDebugger {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {}
}
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec!["var test = { debugger: 1 }; test.debugger;"];
let fail = vec!["if (foo) debugger"];
Tester::new(NoDebugger::NAME, pass, fail).test_and_snapshot();
}
然後您需要向 linter 註冊新建立的規則。將規則新增至rules.rs
中適當的 mod
(例如此處的 no-debugger
) 並將其新增至 oxc_macros::declare_all_lint_rules!
(例如此處)。
您的規則現在應該可以執行了!您可以使用 cargo test -p oxc_linter
來試用它。由於您尚未實作規則,因此測試應該會失敗。
步驟 3:填寫範本
文件
填寫各種文件區段。
- 提供規則作用的清晰簡潔摘要。
- 解釋規則為何重要以及它防止哪些不良行為。
- 提供違反規則的程式碼範例和不違反規則的程式碼範例。
請記住,我們使用此文件為本網站生成規則文件頁面,因此請確保您的文件清晰且有幫助!
規則類別
首先,選擇最符合規則的規則類別。請記住,correctness
規則預設會執行,因此在選擇此類別時請謹慎。在 declare_oxc_lint!
巨集中設定您的類別。
修正器狀態
如果規則有修正器,請在 declare_oxc_lint!
中註冊它提供的修正類型。如果您不習慣實作修正器,您也可以使用 pending
作為佔位符。這有助於其他貢獻者找到並實作後續缺少的修正器。
診斷
建立一個函數來建立規則違規的診斷。遵循以下原則
message
應該是關於哪裡出錯的祈使句,而不是關於規則作用的描述。help
訊息應該是一個類似指令的陳述,告訴使用者如何解決問題。
fn no_debugger_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("`debugger` statement is not allowed")
.with_help("Remove this `debugger` statement")
.with_label(span)
}
fn no_debugger_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Disallow `debugger` statements")
.with_help("`debugger` statements are not allowed.")
.with_label(span)
步驟 4:規則實作
閱讀規則的原始碼以了解其運作方式。雖然 Oxlint 的運作方式與 ESLint 相似,但規則不太可能直接移植。
ESLint 規則有一個 create
函數,該函數會傳回一個物件,其鍵是觸發規則的 AST 節點,值是在這些節點上執行 lint 的函數。Oxlint 規則在幾個觸發器之一上執行,每個觸發器都來自Rule
trait
- 在每個 AST 節點上執行 (透過
run
) - 在每個符號上執行 (透過
run_on_symbol
) - 在整個檔案上執行一次 (透過
run_once
)
就 no-debugger
而言,我們正在尋找 DebuggerStatement
節點,因此我們將使用 run
。以下是規則的簡化版本
點擊展開
use oxc_ast::AstKind;
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use crate::{context::LintContext, rule::Rule, AstNode};
fn no_debugger_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("`debugger` statement is not allowed")
.with_label(span)
}
#[derive(Debug, Default, Clone)]
pub struct NoDebugger;
declare_oxc_lint!(
/// ### What it does
/// Checks for usage of the `debugger` statement
///
/// ### Why is this bad?
/// `debugger` statements do not affect functionality when a
/// debugger isn't attached. They're most commonly an
/// accidental debugging leftover.
///
/// ### Example
///
/// Examples of **incorrect** code for this rule:
/// ```js
/// async function main() {
/// const data = await getData();
/// const result = complexCalculation(data);
/// debugger;
/// }
/// ```
NoDebugger,
correctness
);
impl Rule for NoDebugger {
// Runs on each node in the AST
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
// `debugger` statements have their own AST kind
if let AstKind::DebuggerStatement(stmt) = node.kind() {
// Report a violation
ctx.diagnostic(no_debugger_diagnostic(stmt.span));
}
}
}
步驟 5:測試
若要在每次變更時測試您的規則,請執行
just watch "test -p oxc_linter -- rule-name"
或者若要只測試一次,請執行
cargo test -p oxc_linter -- rule-name
# Or
cargo insta test -p oxc_linter -- rule-name
Oxlint 使用 cargo insta
進行快照測試。如果快照已變更或剛建立,則 cargo test
將會失敗。您可以執行 cargo insta test -p oxc_linter
以查看測試結果中的差異。您可以透過執行 cargo insta review
來檢閱快照,或者跳過檢閱並使用 cargo insta accept
接受所有變更。
當您準備好提交您的 PR 時,請執行 just ready
或 just r
以在本地執行 CI 檢查。您也可以執行 just fix
來自動修正任何 lint、格式或錯字問題。一旦 just ready
通過,請建立 PR,維護人員將會檢閱您的變更。
一般建議
將錯誤訊息精確指向最短的程式碼範圍
我們希望使用者專注於有問題的程式碼,而不是解讀錯誤訊息來識別程式碼的哪個部分有錯誤。
使用 let-else
陳述式
如果您發現自己深度巢狀 if-let
陳述式,請考慮改用 let-else
。
提示
CodeAesthetic 的永不巢狀影片更詳細地解釋了這個概念。
// let-else is easier to read
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
let AstKind::JSXOpeningElement(jsx_opening_elem) = node.kind() else {
return;
};
let Some(expr) = container.expression.as_expression() else {
return;
};
let Expression::BooleanLiteral(expr) = expr.without_parenthesized() else {
return;
};
// ...
}
// deep nesting is hard to read
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
if let AstKind::JSXOpeningElement(jsx_opening_elem) = node.kind() {
if let Some(expr) = container.expression.as_expression() {
if let Expression::BooleanLiteral(expr) = expr.without_parenthesized() {
// ...
}
}
}
}
盡可能使用 CompactStr
盡可能減少配置對於 oxc
中的效能至關重要。String
類型需要在堆積上配置記憶體,這會耗費記憶體和 CPU 週期。可以使用 CompactStr
將小型字串內聯儲存在堆疊上(在 64 位元系統上最多 24 個位元組),這表示我們不需要配置記憶體。如果字串太大而無法內聯儲存,它將會配置必要的空間。CompactStr
可以用在幾乎任何具有類型 String
或 &str
的地方,並且與 String
類型相比,可以節省大量的記憶體和 CPU 週期。
struct Element {
name: CompactStr
}
let element = Element {
name: "div".into()
};
struct Element {
name: String
}
let element = Element {
name: "div".to_string()
};