ESLint Custom Rules: Enforce Code Standards
Welcome to TopperBlog! 👋
I'm a tech content creator passionate about helping developers level up their careers and master cutting-edge technologies.
🎯 What I Write About:
• AI/ML Engineering & LLMs
• Web3 & Blockchain Development
• System Design & Architecture
• Interview Preparation (FAANG)
• Freelancing & Remote Work
• Modern Tech Stacks (Next.js, React, Rust, TypeScript)
• Performance Optimization & Best Practices
💼 Mission: Sharing practical, actionable insights that accelerate your tech career and maximize your earning potential.
📚 15+ In-Depth Guides covering everything from earning $10k/month as a freelancer to cracking FAANG interviews.
🌐 Let's connect and grow together in this amazing tech journey!
#TechBlogger #SoftwareEngineering #CareerGrowth #WebDevelopment #AIEngineering
1600w ESLint Custom Rules: Enforce Code Standards
Introduction
ESLint is a powerful static analysis tool for JavaScript that helps developers identify and fix problems in their code. While ESLint comes with a comprehensive set of built-in rules, custom rules allow teams to enforce organization-specific coding standards and best practices. This guide explores how to create, implement, and maintain ESLint custom rules effectively.
Why Custom ESLint Rules Matter
Standard ESLint rules cover general JavaScript best practices, but they don't address domain-specific requirements. Custom rules enable teams to:
- Enforce architectural patterns specific to their projects
- Prevent common mistakes in their codebase
- Maintain consistency across large teams
- Implement security policies tailored to their needs
- Educate developers through automated feedback
Anatomy of an ESLint Rule
Every ESLint rule follows a consistent structure:
module.exports = {
meta: {
type: 'problem', // or 'suggestion' or 'layout'
docs: {
description: 'Rule description',
category: 'Best Practices',
recommended: true
},
fixable: 'code', // or 'whitespace' or null
schema: [] // options schema
},
create(context) {
return {
// AST node selectors and handlers
};
}
};
The meta object provides metadata about the rule, while the create function returns an object with AST node handlers.
Creating Your First Custom Rule
Let's create a rule that prevents console statements in production code:
// rules/no-console-in-production.js
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'Disallow console methods in production code',
category: 'Best Practices',
recommended: true
},
fixable: null,
schema: [
{
type: 'object',
properties: {
allowedMethods: {
type: 'array',
items: { type: 'string' }
}
}
}
]
},
create(context) {
const options = context.options[0] || {};
const allowedMethods = options.allowedMethods || [];
return {
CallExpression(node) {
if (
node.callee.type === 'MemberExpression' &&
node.callee.object.name === 'console' &&
!allowedMethods.includes(node.callee.property.name)
) {
context.report({
node,
message: `Unexpected console.${node.callee.property.name}() in production code`
});
}
}
};
}
};
Advanced Rule: Enforcing Naming Conventions
Create a rule that enforces specific naming patterns for React components:
// rules/react-component-naming.js
module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'Enforce PascalCase naming for React components',
category: 'Best Practices'
},
fixable: null
},
create(context) {
return {
FunctionDeclaration(node) {
if (isReactComponent(node)) {
if (!/^[A-Z]/.test(node.id.name)) {
context.report({
node,
message: `React component '${node.id.name}' must start with uppercase letter`
});
}
}
},
VariableDeclarator(node) {
if (
node.init &&
isReactComponentExpression(node.init) &&
!/^[A-Z]/.test(node.id.name)
) {
context.report({
node,
message: `React component '${node.id.name}' must start with uppercase letter`
});
}
}
};
}
};
function isReactComponent(node) {
return node.params.length === 0 ||
(node.params.length === 1 && node.params[0].name === 'props');
}
function isReactComponentExpression(node) {
return node.type === 'ArrowFunctionExpression' ||
node.type === 'FunctionExpression';
}
Implementing Custom Rules in Your Project
Create a plugin to bundle your custom rules:
// eslint-plugin-custom/index.js
module.exports = {
rules: {
'no-console-in-production': require('./rules/no-console-in-production'),
'react-component-naming': require('./rules/react-component-naming'),
'no-hardcoded-strings': require('./rules/no-hardcoded-strings')
},
configs: {
recommended: {
plugins: ['custom'],
rules: {
'custom/no-console-in-production': 'error',
'custom/react-component-naming': 'warn',
'custom/no-hardcoded-strings': 'off'
}
}
}
};
Configure in .eslintrc.json:
{
"plugins": ["custom"],
"extends": ["plugin:custom/recommended"],
"rules": {
"custom/no-console-in-production": [
"error",
{ "allowedMethods": ["error", "warn"] }
]
}
}
Testing Custom Rules
Use ESLint's RuleTester for comprehensive testing:
// tests/no-console-in-production.test.js
const RuleTester = require('eslint').RuleTester;
const rule = require('../rules/no-console-in-production');
const ruleTester = new RuleTester();
ruleTester.run('no-console-in-production', rule, {
valid: [
'console.error("Error occurred")',
'console.warn("Warning")',
'logger.log("message")'
],
invalid: [
{
code: 'console.log("debug")',
errors: [{ message: 'Unexpected console.log() in production code' }]
},
{
code: 'console.info("info")',
errors: [{ message: 'Unexpected console.info() in production code' }]
}
]
});
Best Practices for Custom Rules
1. Keep Rules Focused: Each rule should address a single concern. Avoid creating monolithic rules that handle multiple scenarios.
2. Provide Clear Messages: Error messages should be specific and actionable, helping developers understand what's wrong and how to fix it.
3. Support Configuration: Use the schema property to allow teams to customize rule behavior for different contexts.
4. Document Thoroughly: Include clear documentation explaining the rule's purpose, examples, and configuration options.
5. Optimize Performance: Use efficient AST selectors and avoid unnecessary traversals that could slow down linting.
6. Make Rules Fixable: When possible, implement automatic fixes using the fixer API to improve developer experience.
Common Pitfalls to Avoid
- Over-engineering: Don't create rules for every possible code smell; focus on high-impact standards
- Ignoring context: Consider different file types and environments when designing rules
- Poor error messages: Vague messages frustrate developers; be specific and helpful
- Lack of testing: Thoroughly test rules with both valid and invalid code samples
- Breaking changes: When updating rules, consider backward compatibility
Conclusion
Custom ESLint rules are powerful tools for enforcing code standards and maintaining consistency across projects. By creating focused, well-documented rules tailored to your team's needs, you can automate code quality checks and reduce manual review overhead. Start with high-impact rules addressing your most common issues, then expand gradually as your linting infrastructure matures.