Creating a Custom ESLint Rule to Ban console.log in Production Code
Learn how to create a custom ESLint rule that flags and optionally removes console.log calls in production source files using the AST-based rule API.
24 Sept 2026, 14:28 UTC

Desired outcome
Add a project-specific ESLint rule that flags any console.log call in source files matching a production pattern and optionally removes the call automatically.
Prerequisites
- Node.js 14 or later installed.
- ESLint 8 installed locally with npm i -D eslint.
- If the codebase uses TypeScript, install @typescript-eslint/parser and @typescript-eslint/eslint-plugin.
- A text editor or IDE for editing JavaScript or TypeScript files.
Focused procedure
Set up a custom plugin directory
Create a folder for the plugin and initialize a package.
mkdir eslint-plugin-no-console-log cd eslint-plugin-no-console-log npm init -yImplement the rule
Add lib/rules/no-console-log.js. The rule uses the ESTree AST provided by ESLint.
// lib/rules/no-console-log.js module.exports = { meta: { type: 'suggestion', docs: { description: 'disallow console.log in production files', category: 'Possible Errors', recommended: false, }, fixable: 'code', schema: [ { type: 'object', properties: { allowInTests: { type: 'boolean' } }, additionalProperties: false, }, ], }, create(context) { const options = context.options[0] || {}; const allowInTests = Boolean(options.allowInTests); function isTestFile(filename) { return /(^|[\\/])__tests__[\\/]/.test(filename) || /\\.(test|spec)\\.[tj]sx?$/.test(filename); } return { CallExpression(node) { if (allowInTests && isTestFile(context.getFilename())) { return; } if ( node.callee.type === 'MemberExpression' && node.callee.object.type === 'Identifier' && node.callee.object.name === 'console' && node.callee.property.type === 'Identifier' && node.callee.property.name === 'log' ) { context.report({ node, message: 'Unexpected console.log statement.', fix(fixer) { return fixer.remove(node); }, }); } }, }; }, };Expose the rule via a plugin index
Create index.js in the plugin root.
// index.js module.exports = { rules: { 'no-console-log': require('./lib/rules/no-console-log'), }, };Link the plugin in your ESLint configuration
Install the local plugin from the project root and add it to .eslintrc.js.
npm i -D ../eslint-plugin-no-console-logmodule.exports = { plugins: ['no-console-log'], rules: { 'no-console-log': ['error', { allowInTests: true }], }, };Run the rule against sample code
Create src/example.js with a console.log call and run ESLint.
npx eslint src/example.jsRun with --fix to automatically remove flagged calls.
Expected checks
- Run npx eslint --print-config and verify plugins contains no-console-log and the rule appears with the configured severity.
- Use ESLint RuleTester to assert the rule reports on invalid code and passes on valid code.
- Open an AST explorer with the ESTree parser, paste console.log('x'), and confirm the CallExpression to MemberExpression structure matches the visitor logic.
Recovery options
- Disable locally with an inline comment: // eslint-disable-next-line no-console-log
- Remove the plugin from devDependencies and delete the plugin folder.
- Revert .eslintrc.js changes to the previous state.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.