Last active
April 2, 2025 15:40
-
-
Save Illyism/29981ba721a544cbe49044e6f4bb6869 to your computer and use it in GitHub Desktop.
ESLint 200-Line Max File Size Rule for Better AI Coding
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 🧠 My game-changing ESLint rule that makes AI coding 10x better: | |
* - Enforces 200-line max file size | |
* - Counts only actual code (ignores comments) | |
* - Gives helpful refactoring suggestions | |
* - Works perfectly with Cursor AI's "Fix in Chat" | |
* | |
* Custom ESLint rule to limit file size to 200 lines | |
* @type {import("eslint").Rule.RuleModule} | |
*/ | |
export const maxFileLines = { | |
meta: { | |
type: 'suggestion', | |
docs: { | |
description: 'Enforce maximum file line count (default: 200)', | |
category: 'Best Practices', | |
recommended: true, | |
}, | |
schema: [ | |
{ | |
type: 'object', | |
properties: { | |
max: { type: 'integer', minimum: 1 }, | |
skipBlankLines: { type: 'boolean' }, | |
skipComments: { type: 'boolean' }, | |
skipImports: { type: 'boolean' }, | |
}, | |
additionalProperties: false, | |
}, | |
], | |
messages: { | |
tooManyLines: | |
'File has too many lines ({{count}}). Maximum allowed is {{max}} lines. Please split this file into smaller, more focused modules with single responsibilities. Consider extracting utility functions, separating components, or moving complex logic into dedicated files.', | |
}, | |
}, | |
create(context) { | |
// Get rule options with defaults | |
const options = { | |
max: 200, | |
skipBlankLines: true, | |
skipComments: true, | |
skipImports: true, | |
...context.options[0], | |
} | |
const maxLines = options.max | |
const skipBlankLines = options.skipBlankLines !== false | |
const skipComments = options.skipComments !== false | |
const skipImports = options.skipImports !== false | |
return { | |
Program(node) { | |
const lines = context.sourceCode.lines || context.sourceCode.text.split(/\r?\n/) | |
// Count non-empty, non-comment lines | |
let codeLineCount = 0 | |
const comments = context.sourceCode.getAllComments() | |
const commentLines = new Set() | |
const importLines = new Set() | |
// Mark all comment lines if we're skipping them | |
if (skipComments) { | |
comments.forEach(comment => { | |
const startLine = comment.loc.start.line | |
const endLine = comment.loc.end.line | |
for (let i = startLine; i <= endLine; i++) { | |
commentLines.add(i) | |
} | |
}) | |
} | |
// Mark all import lines if we're skipping them | |
if (skipImports) { | |
context.sourceCode.ast.body.forEach(statement => { | |
if ( | |
statement.type === 'ImportDeclaration' || | |
(statement.type === 'ExpressionStatement' && | |
statement.expression.type === 'CallExpression' && | |
statement.expression.callee.name === 'require') | |
) { | |
const startLine = statement.loc.start.line | |
const endLine = statement.loc.end.line | |
for (let i = startLine; i <= endLine; i++) { | |
importLines.add(i) | |
} | |
} | |
}) | |
} | |
// Count actual code lines | |
for (let i = 0; i < lines.length; i++) { | |
const lineNumber = i + 1 | |
const line = lines[i].trim() | |
// Skip blank lines if configured to do so | |
if (skipBlankLines && line.length === 0) { | |
continue | |
} | |
// Skip comment lines if configured to do so | |
if (skipComments && commentLines.has(lineNumber)) { | |
continue | |
} | |
// Skip import lines if configured to do so | |
if (skipImports && importLines.has(lineNumber)) { | |
continue | |
} | |
codeLineCount++ | |
} | |
if (codeLineCount > maxLines) { | |
context.report({ | |
node, | |
messageId: 'tooManyLines', | |
data: { | |
count: codeLineCount, | |
max: maxLines, | |
}, | |
}) | |
} | |
}, | |
} | |
}, | |
} | |
/** | |
* Plugin object with our custom rules | |
*/ | |
export const customRules = { | |
rules: { | |
'max-file-lines': maxFileLines, | |
}, | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import js from '@eslint/js' | |
import { customRules } from './custom-rules.js' | |
/** | |
* A minimal shared ESLint configuration with custom rules. | |
* | |
* @type {import("eslint").Linter.Config[]} | |
*/ | |
export const config = [ | |
js.configs.recommended, | |
{ | |
plugins: { | |
'custom-rules': customRules, | |
}, | |
rules: { | |
'custom-rules/max-file-lines': 'warn', | |
}, | |
}, | |
] |
sourceCode is not a function but field
sourceCode is not a function but field
Thank you, fixed!
the Goat! I spent 3hrs refactoring a 1,500 line main.js file yesterday, this is an elegant prevention mechanism.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Such a great rule. Thank you for sharing!