Skip to content

Instantly share code, notes, and snippets.

@Illyism
Last active April 2, 2025 15:40
Show Gist options
  • Save Illyism/29981ba721a544cbe49044e6f4bb6869 to your computer and use it in GitHub Desktop.
Save Illyism/29981ba721a544cbe49044e6f4bb6869 to your computer and use it in GitHub Desktop.
ESLint 200-Line Max File Size Rule for Better AI Coding
/**
* 🧠 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,
},
}
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',
},
},
]
@ezequieltejada
Copy link

Such a great rule. Thank you for sharing!

@NarHakobyan
Copy link

sourceCode is not a function but field

@Illyism
Copy link
Author

Illyism commented Mar 16, 2025

sourceCode is not a function but field

Thank you, fixed!

@a19grey
Copy link

a19grey commented Mar 16, 2025

the Goat! I spent 3hrs refactoring a 1,500 line main.js file yesterday, this is an elegant prevention mechanism.

@a19grey
Copy link

a19grey commented Mar 16, 2025

I got a bunch of failed imports for es-lint I don't know anything about javascript. I had to rename as a .mjs file to get it to work and claude re-wrote the file to work.

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment