ESLint Rule Composer
ESLint Rule Composer is a utility library designed to facilitate the creation and modification of ESLint rules by composing them from existing ones. Currently at version 0.3.0, it allows developers to `filterReports`, `mapReports`, or `joinReports` from one or more base rules, enabling highly customized linting behavior without needing to rewrite entire rule definitions. This approach is particularly useful for adding exceptions to existing rules (e.g., ignoring specific patterns in `no-unused-expressions`) or combining the logic of multiple rules. Its primary differentiator is the programmatic manipulation of reported problems and rule definitions, offering a flexible layer over ESLint's core rule API. Given its 0.x.y version, API stability might still be evolving, and its release cadence is likely slow or on-demand, as the last update on NPM was in April 2018.
Common errors
-
TypeError: ruleComposer.filterReports is not a function
cause The `eslint-rule-composer` module was not correctly imported or `require`d, or it returned an unexpected value.fixEnsure `const ruleComposer = require('eslint-rule-composer');` is used and the package is correctly installed. Double-check the path if it's a local file. -
TypeError: Cannot read properties of undefined (reading 'getRules')
cause The `eslint.Linter` object was not correctly instantiated or imported, or the `eslint` package itself is not installed or compatible.fixVerify that `const { Linter } = require('eslint');` is correctly used and `const linter = new Linter();` is called before accessing `linter.getRules()`. -
TypeError: Cannot read properties of undefined (reading 'whitelist')
cause Attempted to access a property (e.g., `whitelist`) from `metadata.options[0]` when `metadata.options` or `metadata.options[0]` was `undefined` or `null`.fixAdd defensive coding for accessing rule options: `const whitelist = metadata.options?.[0]?.whitelist || [];` to safely handle cases where options might not be provided in the ESLint configuration.
Warnings
- breaking The package is currently in version 0.x.y. According to semantic versioning, minor versions (`0.y.z`) can introduce breaking API changes without a major version increment. Developers should pin exact versions or thoroughly test upgrades.
- gotcha Compatibility with `eslint` versions is not explicitly guaranteed. Changes in ESLint's internal `Linter` API or AST structures across major `eslint` versions could potentially break rules composed with `eslint-rule-composer`.
- gotcha When accessing rule options via `metadata.options`, directly indexing `metadata.options[0]` can lead to runtime errors if options are not provided or are malformed in the ESLint configuration. `metadata.options` could be `undefined` or an empty array.
- gotcha The package uses CommonJS `require()`. Integrating it into an ESM-only project or an `eslint.config.js` using flat config (which often implies ESM) might require specific setup (e.g., using `createRequire` or dynamic `import()`) or might not be directly compatible.
Install
-
npm install eslint-rule-composer -
yarn add eslint-rule-composer -
pnpm add eslint-rule-composer
Imports
- ruleComposer
import ruleComposer from 'eslint-rule-composer';
const ruleComposer = require('eslint-rule-composer'); - Linter
const { Linter } = require('eslint');
Quickstart
const ruleComposer = require('eslint-rule-composer');
const { Linter } = require('eslint');
// Instantiate ESLint's Linter to get access to core rules
const linter = new Linter();
const noUnusedExpressionsRule = linter.getRules().get('no-unused-expressions');
// Create a modified version of 'no-unused-expressions' that ignores lines starting with 'expect'
module.exports = ruleComposer.filterReports(
noUnusedExpressionsRule,
(problem, metadata) => {
// Ensure the problem node and its first token exist before accessing properties
if (!problem.node || !metadata.sourceCode || !metadata.sourceCode.getFirstToken(problem.node)) {
return true; // Keep the report if parsing issue or no token
}
return metadata.sourceCode.getFirstToken(problem.node).value !== 'expect';
}
);
/* To use this rule:
1. Save this code as a rule file (e.g., `rules/custom-no-unused-expressions.js`)
2. In your `.eslintrc.js` or equivalent config, define a plugin:
module.exports = {
plugins: {
'my-plugin': {
rules: {
'custom-no-unused-expressions': require('./rules/custom-no-unused-expressions'),
},
},
},
rules: {
'my-plugin/custom-no-unused-expressions': 'error',
},
};
*/