Add project-specific ESLint rules
Published about 1 month ago
ESLint is an incredible tool, and LLMs are super good at writing custom ESLint rules. Writing custom, project-specific ESLint rules is now a low-hanging fruit for code quality.
To get set up, add the following to the dependencies
or devDependencies
list in your package.json
:
"eslint-plugin-<my project>": "file:./eslint-plugin-<my project>"
Then, create the eslint-plugin-<my project>
directory, and create eslint-plugin-<my project>/package.json
with the following content:
// eslint-plugin-<my project>/package.json
{
"name": "eslint-plugin-<my project>",
"version": "0.0.0",
"main": "index.js"
}
Create eslint-plugin-<my project>/index.js
with the following content:
// eslint-plugin-<my project>/index.js
// you'll `require` your custom rules here
const plugin = {
rules: {
// your rules will go here
},
};
module.exports = plugin;
You can now run npm install
to install your project-specific ESLint plugin.
To create a custom rule, add a new .js
file to your eslint-plugin-<my project>
directory, and export an ESLint rule object.
For example, here's one that I wrote with the help of ChatGPT to prevent devs from ignoring ESLint and TypeScript errors unless there's a preceding code comment explaining why the error is being ignored:
// eslint-plugin-<my project>/no-unexplained-ignored-rules.js
const rule = {
meta: {
type: 'suggestion',
docs: {
description: 'Do not ignore ESLint and TypeScript errors without explaining your rationale',
category: 'Best Practices',
recommended: false,
},
fixable: null,
schema: [],
},
create(context) {
return {
Program() {
const sourceCode = context.getSourceCode();
const comments = sourceCode.getAllComments();
comments.forEach((comment, index) => {
const commentText = comment.value.trim();
// Check if the comment includes any of the target directives
if (/^@ts-ignore|^@ts-expect-error|^eslint-disable/.test(commentText)) {
// Ensure it's not the first comment in the file
if (index === 0) {
context.report({
node: comment,
message: 'Add a comment above this line to explain why the error or warning is being ignored.',
});
return;
}
const previousComment = comments[index - 1];
// Check if there is a direct preceding comment line
if (comment.loc.start.line !== previousComment.loc.end.line + 1) {
context.report({
node: comment,
message: 'Add a comment above this line to explain why the error or warning is being ignored.',
});
}
}
});
},
};
},
};
module.exports = rule;
require
this rule in your index.js
file, and register it in the plugin:
// eslint-plugin-<my project>/index.js
const noUnexplainedIgnoredRules = require('./no-unexplained-ignored-rules');
const plugin = {
rules: {
'no-unexplained-ignored-rules': noUnexplainedIgnoredRules,
},
};
module.exports = plugin;
You can now use this rule in your ESLint configuration:
// .eslintrc.json
{
"plugins": ["<my project>"],
"rules": {
"<my project>/no-unexplained-ignored-rules": "error"
}
}