Files
Nikolay Vitkov 65f45cc921 [deps] Update TypeScript EsLint parser
Update the @typescript-eslint - eslint-plugin and parser to v7.18.0
cia incremental patches.
This is the last version before breaking changes need to start be applied to the infrastructure.
Needed to update EsLint to v8.56.0 as required by the above major v7.
Only one new error was uncovered in test file and fixed.

No-Presubmit: true
Bug: none
Change-Id: I4e4fe265b82deeb49786c5bf3e154dac8fcfecbd
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6054130
Reviewed-by: Danil Somsikov <dsv@chromium.org>
Commit-Queue: Nikolay Vitkov <nvitkov@chromium.org>
2024-11-29 13:15:35 +00:00

214 lines
6.9 KiB
JavaScript

/**
* @fileoverview A rule to set the maximum number of line of code in a function.
* @author Pete Ward <peteward44@gmail.com>
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const astUtils = require("./utils/ast-utils");
const { upperCaseFirst } = require("../shared/string-utils");
//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------
const OPTIONS_SCHEMA = {
type: "object",
properties: {
max: {
type: "integer",
minimum: 0
},
skipComments: {
type: "boolean"
},
skipBlankLines: {
type: "boolean"
},
IIFEs: {
type: "boolean"
}
},
additionalProperties: false
};
const OPTIONS_OR_INTEGER_SCHEMA = {
oneOf: [
OPTIONS_SCHEMA,
{
type: "integer",
minimum: 1
}
]
};
/**
* Given a list of comment nodes, return a map with numeric keys (source code line numbers) and comment token values.
* @param {Array} comments An array of comment nodes.
* @returns {Map<string, Node>} A map with numeric keys (source code line numbers) and comment token values.
*/
function getCommentLineNumbers(comments) {
const map = new Map();
comments.forEach(comment => {
for (let i = comment.loc.start.line; i <= comment.loc.end.line; i++) {
map.set(i, comment);
}
});
return map;
}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "Enforce a maximum number of lines of code in a function",
recommended: false,
url: "https://eslint.org/docs/latest/rules/max-lines-per-function"
},
schema: [
OPTIONS_OR_INTEGER_SCHEMA
],
messages: {
exceed: "{{name}} has too many lines ({{lineCount}}). Maximum allowed is {{maxLines}}."
}
},
create(context) {
const sourceCode = context.sourceCode;
const lines = sourceCode.lines;
const option = context.options[0];
let maxLines = 50;
let skipComments = false;
let skipBlankLines = false;
let IIFEs = false;
if (typeof option === "object") {
maxLines = typeof option.max === "number" ? option.max : 50;
skipComments = !!option.skipComments;
skipBlankLines = !!option.skipBlankLines;
IIFEs = !!option.IIFEs;
} else if (typeof option === "number") {
maxLines = option;
}
const commentLineNumbers = getCommentLineNumbers(sourceCode.getAllComments());
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Tells if a comment encompasses the entire line.
* @param {string} line The source line with a trailing comment
* @param {number} lineNumber The one-indexed line number this is on
* @param {ASTNode} comment The comment to remove
* @returns {boolean} If the comment covers the entire line
*/
function isFullLineComment(line, lineNumber, comment) {
const start = comment.loc.start,
end = comment.loc.end,
isFirstTokenOnLine = start.line === lineNumber && !line.slice(0, start.column).trim(),
isLastTokenOnLine = end.line === lineNumber && !line.slice(end.column).trim();
return comment &&
(start.line < lineNumber || isFirstTokenOnLine) &&
(end.line > lineNumber || isLastTokenOnLine);
}
/**
* Identifies is a node is a FunctionExpression which is part of an IIFE
* @param {ASTNode} node Node to test
* @returns {boolean} True if it's an IIFE
*/
function isIIFE(node) {
return (node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") && node.parent && node.parent.type === "CallExpression" && node.parent.callee === node;
}
/**
* Identifies is a node is a FunctionExpression which is embedded within a MethodDefinition or Property
* @param {ASTNode} node Node to test
* @returns {boolean} True if it's a FunctionExpression embedded within a MethodDefinition or Property
*/
function isEmbedded(node) {
if (!node.parent) {
return false;
}
if (node !== node.parent.value) {
return false;
}
if (node.parent.type === "MethodDefinition") {
return true;
}
if (node.parent.type === "Property") {
return node.parent.method === true || node.parent.kind === "get" || node.parent.kind === "set";
}
return false;
}
/**
* Count the lines in the function
* @param {ASTNode} funcNode Function AST node
* @returns {void}
* @private
*/
function processFunction(funcNode) {
const node = isEmbedded(funcNode) ? funcNode.parent : funcNode;
if (!IIFEs && isIIFE(node)) {
return;
}
let lineCount = 0;
for (let i = node.loc.start.line - 1; i < node.loc.end.line; ++i) {
const line = lines[i];
if (skipComments) {
if (commentLineNumbers.has(i + 1) && isFullLineComment(line, i + 1, commentLineNumbers.get(i + 1))) {
continue;
}
}
if (skipBlankLines) {
if (line.match(/^\s*$/u)) {
continue;
}
}
lineCount++;
}
if (lineCount > maxLines) {
const name = upperCaseFirst(astUtils.getFunctionNameWithKind(funcNode));
context.report({
node,
messageId: "exceed",
data: { name, lineCount, maxLines }
});
}
}
//--------------------------------------------------------------------------
// Public API
//--------------------------------------------------------------------------
return {
FunctionDeclaration: processFunction,
FunctionExpression: processFunction,
ArrowFunctionExpression: processFunction
};
}
};