Creating Custom ESLint Plugins to Enforce Team Coding Standards
This article explains how to develop custom ESLint plugins—covering background concepts, Yeoman generator setup, rule implementation for typeof and instanceof checks, unit testing, local linking, and providing recommended configurations—to ensure consistent code style and best‑practice enforcement across a development team.
In many projects multiple developers work on the same codebase, making it hard to keep everyone aware of best‑practice guidelines, especially when team members change. Traditional code review is delayed and relies on manual inspection, which cannot guarantee 100% quality.
Using an intelligent assistant that can suggest library functions in real time solves this problem; for front‑end JavaScript development, ESLint serves that role. This guide shows how to extend ESLint with custom plugins to enforce project‑specific best practices.
Background Knowledge
The article first recalls a previous discussion about the pitfalls of using typeof and instanceof for type checking in JavaScript, illustrating the issues with a short code snippet.
typeof []; // 'object'
typeof {}; // 'object' typeof c; // 'object'
[] instanceof Array // true
[] instanceof Object // true // note thisTwo custom ESLint rules will be created to detect these patterns.
Plugin Creation
ESLint plugins are generated with the Yeoman generator. Install Yeoman and the ESLint generator globally:
$ npm i -g yo
$ npm i -g generator-eslintCreate a new directory for the plugin and run the generator:
$ mkdir eslint-plugin-utils
$ cd eslint-plugin-utils
$ yo eslint:pluginThe generator produces a project structure where lib/rules holds custom rules and tests/lib/rules holds their unit tests.
.
├── .eslintrc.js
├── README.md
├── lib
│ ├── index.js
│ └── rules
├── package-lock.json
├── package.json
└── tests
└── lib
└── rulestype-typeof-limit Rule
This rule flags any expression where typeof is compared to the string 'object' . The rule metadata must set type to problem and provide a description and category (e.g., Best Practices ).
module.exports = {
meta: {
type: null, // change to 'problem'
docs: {
description: 'typeof cannot be used for objects or arrays, use @jsmini/type',
category: 'Fill me in', // change to 'Best Practices'
recommended: false,
url: null,
},
fixable: null,
schema: [],
},
create(context) {
return {};
},
};The core logic inspects BinaryExpression nodes, checks that the left side is a UnaryExpression with operator typeof , the operator is == or === , and the right side is a literal 'object' . If all conditions match, context.report emits an error.
module.exports = {
create(context) {
return {
BinaryExpression: (node) => {
const operator = node.operator;
const left = node.left;
const right = node.right;
if (
(operator === '==' || operator === '===') &&
left.type === 'UnaryExpression' &&
left.operator === 'typeof' &&
right.type === 'Literal' &&
right.value === 'object'
) {
context.report({
node,
message: 'typeof不能用于对象和数组,请使用 @jsmini/type',
});
}
},
};
},
};Unit tests are written with ESLint's RuleTester . The test file defines one valid case and two invalid cases that should trigger the rule.
const rule = require('../../../lib/rules/type-typeof-limit'),
RuleTester = require('eslint').RuleTester;
const msg = 'typeof不能用于对象和数组,请使用@jsmini/type';
const ruleTester = new RuleTester();
ruleTester.run('type-typeof-limit', rule, {
valid: [{ code: 'typeof a == "number"' }, { code: 'a == "object"' }],
invalid: [
{ code: 'typeof a == "object"', errors: [{ message: msg }] },
{ code: 'typeof a === "object"', errors: [{ message: msg }] },
],
});Run the tests with npm test to verify the rule works.
type-instanceof-limit Rule
A second rule detects the use of instanceof Object , which may also be problematic. Its implementation follows the same pattern, checking for a BinaryExpression whose operator is instanceof and reporting an error.
module.exports = {
create(context) {
function check(node) {
const operator = node.operator;
if (operator === 'instanceof') {
context.report({
node,
message: 'instanceof操作符可能存在问题,请使用@jsmini/type',
});
}
}
return { BinaryExpression: check };
},
};Recommended Configuration
With multiple rules, a shared configuration simplifies usage. Add a configs section to lib/index.js that exports a recommended preset.
module.exports = {
rules: requireIndex(__dirname + '/rules'),
configs: {
plugins: ['@jsmini/utils'],
rules: {
'@jsmini/utils/type-typeof-limit': 'error',
'@jsmini/utils/type-instanceof-limit': 'error',
},
},
};Consumers can then enable the preset with:
module.exports = {
extends: ['@jsmini/utils:recommended'],
};Local Linking for Development
During development, link the plugin locally:
$ npm link
$ cd ../eslint-plugin-utils-demo
$ npm link @jsmini/eslint-plugin-utilsConfigure the demo project's .eslintrc.js to enable the custom rules, write a test file containing typeof a === 'object' , and verify that ESLint highlights the error.
Conclusion
The article demonstrates how to create two custom ESLint rules— type-typeof-limit and type-instanceof-limit —and integrate them into a project with unit tests and a recommended configuration, thereby ensuring consistent code style, reducing review overhead, and improving overall code quality.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.