Skip to content

Instantly share code, notes, and snippets.

@connorshea
Created November 30, 2025 21:20
Show Gist options
  • Select an option

  • Save connorshea/3caef27a87984176753f8e515da0cb1b to your computer and use it in GitHub Desktop.

Select an option

Save connorshea/3caef27a87984176753f8e515da0cb1b to your computer and use it in GitHub Desktop.
Oxlint Rules stats, as of Nov 30, 2025. This JS script needs to be placed in `tasks/lint_rules/src/` in the oxc repo, and then can be run as a node.js script.
#!/usr/bin/env node
import { createESLintLinter, loadTargetPluginRules } from "./eslint-rules.mjs";
import {
createRuleEntries,
updateImplementedStatus,
updateNotSupportedStatus,
updatePendingFixStatus,
overrideTypeScriptPluginStatusWithEslintPluginStatus,
syncVitestPluginStatusWithJestPluginStatus,
syncUnicornPluginStatusWithEslintPluginStatus,
} from "./oxlint-rules.mjs";
const main = async () => {
const linter = createESLintLinter();
loadTargetPluginRules(linter);
const ruleEntries = createRuleEntries(linter.getRules());
await updateImplementedStatus(ruleEntries);
updateNotSupportedStatus(ruleEntries);
await updatePendingFixStatus(ruleEntries);
// Sync / override statuses across plugins so counts are more accurate
await overrideTypeScriptPluginStatusWithEslintPluginStatus(ruleEntries);
await syncVitestPluginStatusWithJestPluginStatus(ruleEntries);
syncUnicornPluginStatusWithEslintPluginStatus(ruleEntries);
// Group by plugin prefix (before '/')
const byPlugin = new Map();
for (const [name, entry] of ruleEntries) {
const [plugin] = name.split("/");
const stats = byPlugin.get(plugin) ?? { total: 0, implemented: 0, notSupported: 0, pending: 0, deprecated: 0 };
// TODO: Maybe don't count deprecated in the total? Not sure.
stats.total += 1;
if (entry.isDeprecated) stats.deprecated += 1;
if (entry.isImplemented) stats.implemented += 1;
if (entry.isNotSupported) stats.notSupported += 1;
if (entry.isPendingFix) stats.pending += 1;
byPlugin.set(plugin, stats);
}
// Sort plugins alphabetically, but keep 'eslint' and 'typescript' at the top for readability
const plugins = Array.from(byPlugin.keys()).sort((a, b) => {
if (a === "eslint") return -1;
if (b === "eslint") return 1;
if (a === "typescript") return -1;
if (b === "typescript") return 1;
return a.localeCompare(b);
});
// Print markdown table header
console.log("| Plugin | Total | Implemented | Supported % | Not supported | Pending fix | Still need to implement |");
console.log("|---|---:|---:|---:|---:|---:|---:|");
let totalAll = 0;
let totalImplemented = 0;
let totalNotSupported = 0;
let totalPending = 0;
let totalDeprecated = 0;
for (const plugin of plugins) {
const { total, implemented, notSupported, pending, deprecated } = byPlugin.get(plugin);
const stillNeed = total - implemented - notSupported;
const actionableTotal = total - notSupported; // rules that actually need implementation
const pct = actionableTotal <= 0 ? 100 : (implemented / actionableTotal) * 100;
// Format percent with at most 1 decimal, only show decimal if needed
const pctStr = (() => {
const fixed = Math.round(pct * 10) / 10; // ensures one decimal precision rounded
if (Number.isInteger(fixed)) return `${fixed}%`;
return `${fixed.toFixed(1)}%`;
})();
totalAll += total;
totalImplemented += implemented;
totalNotSupported += notSupported;
totalPending += pending;
totalDeprecated += deprecated;
console.log(`| ${plugin} | ${total} | ${implemented} | ${pctStr} | ${notSupported} | ${pending} | ${stillNeed} |`);
}
const stillNeedAll = totalAll - totalImplemented - totalNotSupported;
console.log();
const actionableTotalAll = totalAll - totalNotSupported;
const overallPct = actionableTotalAll <= 0 ? 100 : (totalImplemented / actionableTotalAll) * 100;
const overallPctStr = (() => {
const fixed = Math.round(overallPct * 10) / 10;
return Number.isInteger(fixed) ? `${fixed}%` : `${fixed.toFixed(1)}%`;
})();
console.log(`Supported rules: ${totalImplemented} (${overallPctStr}), No need to implement: ${totalNotSupported}, Deprecated: ${totalDeprecated}, Still need to implement: ${stillNeedAll}, total: ${totalImplemented} supported/${totalImplemented + stillNeedAll} rules`);
console.log();
console.log(`(All rules counted: ${totalAll} total, with ${totalPending} marked implemented but pending fixes)`);
};
main().catch((e) => {
// eslint-disable-next-line no-console
console.error(e);
process.exit(1);
});
Plugin Total Implemented Supported % Not supported Pending fix Still need to implement
eslint 292 156 78.4% 93 19 43
typescript 72 57 79.2% 0 7 15
import 46 30 66.7% 1 3 15
jest 63 50 79.4% 0 0 13
jsdoc 74 18 24.3% 0 0 56
jsx-a11y 39 29 74.4% 0 0 10
n 41 3 7.3% 0 0 38
nextjs 21 21 100% 0 2 0
promise 17 16 100% 1 3 0
react 132 42 34.1% 9 4 81
react-perf 4 4 100% 0 0 0
regexp 82 0 0% 5 0 77
unicorn 141 116 83.5% 2 28 23
vitest 52 40 76.9% 0 1 12
vue 247 13 15.3% 162 5 72

Supported rules: 595 (56.7%), No need to implement: 273, Deprecated: 116, Still need to implement: 455, total: 595 supported/1050 rules

(All rules counted: 1323 total, with 72 marked implemented but pending fixes)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment