UNPKG

@mindfiredigital/eslint-plugin-hub

Version:

eslint-plugin-hub is a powerful, flexible ESLint plugin that provides a curated set of rules to enhance code readability, maintainability, and prevent common errors. Whether you're working with vanilla JavaScript, TypeScript, React, or Angular, eslint-plu

170 lines (159 loc) 5.25 kB
/** * Gets the name of a function node. * @param {ASTNode} node The function node. * @returns {string} The name of the function or '[anonymous_function]'. */ function getFunctionName(node) { if (node.id && node.id.name) { return node.id.name; } if (node.parent) { if ( node.parent.type === 'VariableDeclarator' && node.parent.id && node.parent.id.name ) { return node.parent.id.name; } if (node.parent.type === 'Property' && node.parent.key) { return ( node.parent.key.name || node.parent.key.value || '[anonymous_function]' ); } if ( node.parent.type === 'AssignmentExpression' && node.parent.left && node.parent.left.name ) { return node.parent.left.name; } } return '[anonymous_function]'; } module.exports = { rules: { 'keep-functions-concise': { meta: { type: 'suggestion', docs: { description: 'Enforces a maximum number of lines per function, with options to skip blank lines and comments.', category: 'Best Practices', recommended: false, }, schema: [ { type: 'object', properties: { maxLines: { type: 'integer', minimum: 0, default: 60, }, skipBlankLines: { type: 'boolean', default: false, }, skipComments: { type: 'boolean', default: false, }, }, additionalProperties: false, }, ], messages: { tooManyLines: 'Function "{{name}}" has {{actualLines}} lines (max {{maxLines}} allowed). {{skippedLineInfo}}', }, }, create(context) { const sourceCode = context.getSourceCode(); const options = context.options[0] || {}; const maxLines = options.maxLines !== undefined ? options.maxLines : 60; const skipBlankLines = options.skipBlankLines !== undefined ? options.skipBlankLines : false; const skipComments = options.skipComments !== undefined ? options.skipComments : false; /** * Checks a given function node for line count violations. * @param {ASTNode} node - The function node to check. */ function checkFunction(node) { // Handle concise arrow functions (not enclosed in a block) if (node.body.type !== 'BlockStatement') { if (maxLines === 0) { context.report({ node, messageId: 'tooManyLines', data: { name: getFunctionName(node), actualLines: 1, maxLines: maxLines, skippedLineInfo: `(concise body counted as 1 line)`, }, }); } return; } const body = node.body; const allLines = sourceCode.getLines(); const startLineIndex = body.loc.start.line; const endLineIndex = body.loc.end.line - 2; let effectiveLineCount = 0; if (startLineIndex <= endLineIndex + 1) { for (let i = startLineIndex; i <= endLineIndex; i++) { const lineText = allLines[i]; const trimmedLine = lineText.trim(); if (skipBlankLines && trimmedLine === '') { continue; } if (skipComments) { if ( trimmedLine === '' || trimmedLine.startsWith('//') || (trimmedLine.startsWith('/*') && trimmedLine.endsWith('*/')) ) { const tokensOnLine = sourceCode.getTokens(body, { filter: token => token.loc.start.line === i + 1 && token.loc.end.line === i + 1, includeComments: false, }); if (tokensOnLine.length === 0) { continue; } } } effectiveLineCount++; } } let skippedLineInfoParts = []; if (skipBlankLines) skippedLineInfoParts.push('blank lines skipped'); if (skipComments) skippedLineInfoParts.push('comment lines skipped'); const skippedLineInfo = skippedLineInfoParts.length > 0 ? `(${skippedLineInfoParts.join(', ')})` : '(no lines skipped by options)'; if (effectiveLineCount > maxLines) { context.report({ node, messageId: 'tooManyLines', data: { name: getFunctionName(node), actualLines: effectiveLineCount, maxLines: maxLines, skippedLineInfo: skippedLineInfo, }, }); } } return { FunctionDeclaration: checkFunction, FunctionExpression: checkFunction, ArrowFunctionExpression: checkFunction, }; }, }, }, };