Alex Vipond
SHIFT + D

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"
  }
}

ON THIS PAGE

Add project-specific ESLint rules