@pythonidaer/complexity-report 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +122 -0
- package/LICENSE +21 -0
- package/README.md +103 -0
- package/assets/prettify.css +1 -0
- package/assets/prettify.js +2 -0
- package/assets/sort-arrow-sprite.png +0 -0
- package/complexity-breakdown.js +53 -0
- package/decision-points/ast-utils.js +127 -0
- package/decision-points/decision-type.js +92 -0
- package/decision-points/function-matching.js +185 -0
- package/decision-points/in-params.js +262 -0
- package/decision-points/index.js +6 -0
- package/decision-points/node-helpers.js +89 -0
- package/decision-points/parent-map.js +62 -0
- package/decision-points/parse-main.js +101 -0
- package/decision-points/ternary-multiline.js +86 -0
- package/export-generators/helpers.js +309 -0
- package/export-generators/index.js +143 -0
- package/export-generators/md-exports.js +160 -0
- package/export-generators/txt-exports.js +262 -0
- package/function-boundaries/arrow-brace-body.js +302 -0
- package/function-boundaries/arrow-helpers.js +93 -0
- package/function-boundaries/arrow-jsx.js +73 -0
- package/function-boundaries/arrow-object-literal.js +65 -0
- package/function-boundaries/arrow-single-expr.js +72 -0
- package/function-boundaries/brace-scanning.js +151 -0
- package/function-boundaries/index.js +67 -0
- package/function-boundaries/named-helpers.js +227 -0
- package/function-boundaries/parse-utils.js +456 -0
- package/function-extraction/ast-utils.js +112 -0
- package/function-extraction/extract-callback.js +65 -0
- package/function-extraction/extract-from-eslint.js +91 -0
- package/function-extraction/extract-name-ast.js +133 -0
- package/function-extraction/extract-name-regex.js +267 -0
- package/function-extraction/index.js +6 -0
- package/function-extraction/utils.js +29 -0
- package/function-hierarchy.js +427 -0
- package/html-generators/about.js +75 -0
- package/html-generators/file-boundary-builders.js +36 -0
- package/html-generators/file-breakdown.js +412 -0
- package/html-generators/file-data.js +50 -0
- package/html-generators/file-helpers.js +100 -0
- package/html-generators/file-javascript.js +430 -0
- package/html-generators/file-line-render.js +160 -0
- package/html-generators/file.css +370 -0
- package/html-generators/file.js +207 -0
- package/html-generators/folder.js +424 -0
- package/html-generators/index.js +6 -0
- package/html-generators/main-index.js +346 -0
- package/html-generators/shared.css +471 -0
- package/html-generators/utils.js +15 -0
- package/index.js +36 -0
- package/integration/eslint/index.js +94 -0
- package/integration/threshold/index.js +45 -0
- package/package.json +64 -0
- package/report/cli.js +58 -0
- package/report/index.js +559 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AST-based function name extraction.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { parseAST, findAllFunctions, findAllNodesByType, findContainingNode, readFileIfExists, getNodeLine } from './ast-utils.js';
|
|
6
|
+
import { identifyCallbackContext } from './extract-callback.js';
|
|
7
|
+
|
|
8
|
+
export function isFirstArgOfDirectCall(funcNode, ast) {
|
|
9
|
+
const callExpressions = findAllNodesByType(ast, 'CallExpression');
|
|
10
|
+
const containingCall = findContainingNode(funcNode, callExpressions);
|
|
11
|
+
if (
|
|
12
|
+
!containingCall ||
|
|
13
|
+
!containingCall.arguments ||
|
|
14
|
+
containingCall.arguments[0] !== funcNode
|
|
15
|
+
) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return containingCall.callee.type === 'Identifier';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function findVariableDeclaratorForFunction(funcNode, ast) {
|
|
22
|
+
const variableDeclarators = findAllNodesByType(ast, 'VariableDeclarator');
|
|
23
|
+
for (const declarator of variableDeclarators) {
|
|
24
|
+
if (declarator.init === funcNode) return declarator;
|
|
25
|
+
if (
|
|
26
|
+
funcNode.range &&
|
|
27
|
+
declarator.range &&
|
|
28
|
+
declarator.range[0] <= funcNode.range[0] &&
|
|
29
|
+
declarator.range[1] >= funcNode.range[1]
|
|
30
|
+
) {
|
|
31
|
+
if (isFirstArgOfDirectCall(funcNode, ast)) return declarator;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function extractNameFromVariableDeclarator(funcNode, ast) {
|
|
38
|
+
const declarator = findVariableDeclaratorForFunction(funcNode, ast);
|
|
39
|
+
if (declarator && declarator.id) return declarator.id.name;
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function extractNameFromProperty(funcNode, ast) {
|
|
44
|
+
const properties = findAllNodesByType(ast, 'Property');
|
|
45
|
+
const containingProperty = findContainingNode(funcNode, properties);
|
|
46
|
+
if (containingProperty && containingProperty.key) {
|
|
47
|
+
if (containingProperty.key.type === 'Identifier') return containingProperty.key.name;
|
|
48
|
+
if (containingProperty.key.type === 'Literal') return String(containingProperty.key.value);
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function extractNameFromMethodDefinition(funcNode, ast) {
|
|
54
|
+
const methodDefinitions = findAllNodesByType(ast, 'MethodDefinition');
|
|
55
|
+
const containingMethod = findContainingNode(funcNode, methodDefinitions);
|
|
56
|
+
if (containingMethod && containingMethod.key && containingMethod.key.type === 'Identifier') {
|
|
57
|
+
return containingMethod.key.name;
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function extractNameFromASTNode(funcNode, ast) {
|
|
63
|
+
if (funcNode.type === 'FunctionDeclaration' && funcNode.id) return funcNode.id.name;
|
|
64
|
+
const varName = extractNameFromVariableDeclarator(funcNode, ast);
|
|
65
|
+
if (varName) return varName;
|
|
66
|
+
const propName = extractNameFromProperty(funcNode, ast);
|
|
67
|
+
if (propName) return propName;
|
|
68
|
+
const methodName = extractNameFromMethodDefinition(funcNode, ast);
|
|
69
|
+
if (methodName) return methodName;
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function doNodeTypesMatch(eslintNodeType, astNodeType) {
|
|
74
|
+
return (eslintNodeType === 'FunctionDeclaration' && astNodeType === 'FunctionDeclaration') ||
|
|
75
|
+
(eslintNodeType === 'ArrowFunctionExpression' && astNodeType === 'ArrowFunctionExpression') ||
|
|
76
|
+
(eslintNodeType === 'FunctionExpression' && astNodeType === 'FunctionExpression');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function findArrowFunctionLine(astFunc, sourceCode) {
|
|
80
|
+
if (!astFunc.range) return null;
|
|
81
|
+
const funcCode = sourceCode.substring(astFunc.range[0], astFunc.range[1]);
|
|
82
|
+
const arrowIndex = funcCode.indexOf('=>');
|
|
83
|
+
if (arrowIndex === -1) return null;
|
|
84
|
+
const linesBeforeArrow = sourceCode.substring(0, astFunc.range[0] + arrowIndex).split('\n');
|
|
85
|
+
return linesBeforeArrow.length;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function matchesArrowFunctionLine(astFunc, lineNumber, sourceCode) {
|
|
89
|
+
const arrowLine = findArrowFunctionLine(astFunc, sourceCode);
|
|
90
|
+
return arrowLine !== null && arrowLine === lineNumber;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function findMatchingASTFunction(
|
|
94
|
+
astFunctions,
|
|
95
|
+
lineNumber,
|
|
96
|
+
nodeType,
|
|
97
|
+
sourceCode
|
|
98
|
+
) {
|
|
99
|
+
for (const astFunc of astFunctions) {
|
|
100
|
+
if (!doNodeTypesMatch(nodeType, astFunc.type)) continue;
|
|
101
|
+
if (astFunc.type === 'ArrowFunctionExpression') {
|
|
102
|
+
if (matchesArrowFunctionLine(astFunc, lineNumber, sourceCode)) return astFunc;
|
|
103
|
+
} else {
|
|
104
|
+
if (getNodeLine(astFunc) === lineNumber) return astFunc;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function extractFunctionNameAST(filePath, lineNumber, nodeType, projectRoot) {
|
|
111
|
+
try {
|
|
112
|
+
const sourceCode = readFileIfExists(filePath, projectRoot);
|
|
113
|
+
if (!sourceCode) return null;
|
|
114
|
+
const ast = parseAST(sourceCode, filePath);
|
|
115
|
+
if (!ast) return null;
|
|
116
|
+
const astFunctions = findAllFunctions(ast);
|
|
117
|
+
const matchingFunc = findMatchingASTFunction(
|
|
118
|
+
astFunctions,
|
|
119
|
+
lineNumber,
|
|
120
|
+
nodeType,
|
|
121
|
+
sourceCode
|
|
122
|
+
);
|
|
123
|
+
if (!matchingFunc) return null;
|
|
124
|
+
const functionName = extractNameFromASTNode(matchingFunc, ast);
|
|
125
|
+
if (functionName) return functionName;
|
|
126
|
+
const callbackType = identifyCallbackContext(matchingFunc, ast);
|
|
127
|
+
if (callbackType) return callbackType;
|
|
128
|
+
if (matchingFunc.type === 'ArrowFunctionExpression') return 'anonymous arrow function';
|
|
129
|
+
return 'anonymous';
|
|
130
|
+
} catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regex-based function name extraction (fallback when AST fails).
|
|
3
|
+
* getFunctionNameForLine is optional; when provided,
|
|
4
|
+
* findParentFunctionWithFallback uses it for recursive lookup.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const FUNCTION_PATTERNS = [
|
|
8
|
+
/(?:export\s+)?function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*[<(]/,
|
|
9
|
+
/const\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*(?:\([^)]*\)\s*)?(?:=>|function)/,
|
|
10
|
+
/export\s+const\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*(?:\([^)]*\)\s*)?(?:=>|function)/,
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
export function findParentFunction(lines, lineNumber, maxLookBack = 50) {
|
|
14
|
+
let parentFunction = null;
|
|
15
|
+
let closestFunctionLine = -1;
|
|
16
|
+
for (
|
|
17
|
+
let lookBack = 1;
|
|
18
|
+
lookBack <= maxLookBack && lineNumber - lookBack >= 1;
|
|
19
|
+
lookBack += 1
|
|
20
|
+
) {
|
|
21
|
+
const checkLine = lineNumber - lookBack;
|
|
22
|
+
const checkLineContent = lines[checkLine - 1] || '';
|
|
23
|
+
for (const funcPattern of FUNCTION_PATTERNS) {
|
|
24
|
+
const funcMatch = checkLineContent.match(funcPattern);
|
|
25
|
+
if (funcMatch && funcMatch[1]) {
|
|
26
|
+
if (closestFunctionLine === -1 || checkLine > closestFunctionLine) {
|
|
27
|
+
parentFunction = funcMatch[1];
|
|
28
|
+
closestFunctionLine = checkLine;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return parentFunction;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function findParentFunctionWithFallback(
|
|
37
|
+
filePath,
|
|
38
|
+
lineNumber,
|
|
39
|
+
projectRoot,
|
|
40
|
+
lines,
|
|
41
|
+
getFunctionNameForLine
|
|
42
|
+
) {
|
|
43
|
+
const parentFunction = findParentFunction(lines, lineNumber);
|
|
44
|
+
if (parentFunction) return parentFunction;
|
|
45
|
+
if (typeof getFunctionNameForLine === 'function') {
|
|
46
|
+
return getFunctionNameForLine(
|
|
47
|
+
filePath,
|
|
48
|
+
lineNumber - 5,
|
|
49
|
+
'FunctionDeclaration',
|
|
50
|
+
projectRoot
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return 'anonymous';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function formatCallbackName(callbackType, parentFunction) {
|
|
57
|
+
if (
|
|
58
|
+
parentFunction &&
|
|
59
|
+
parentFunction !== 'anonymous' &&
|
|
60
|
+
parentFunction !== 'unknown'
|
|
61
|
+
) {
|
|
62
|
+
return `${parentFunction} (${callbackType})`;
|
|
63
|
+
}
|
|
64
|
+
return callbackType;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function checkNamedArrowFunction(prevLine, currentLine) {
|
|
68
|
+
const combinedContext = (prevLine + ' ' + currentLine).trim();
|
|
69
|
+
const namedArrowPattern =
|
|
70
|
+
/const\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*(?:\([^)]*\)\s*)?=>/;
|
|
71
|
+
const namedMatch = combinedContext.match(namedArrowPattern);
|
|
72
|
+
return namedMatch && namedMatch[1] ? namedMatch[1] : null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function findMethodCallCallback(
|
|
76
|
+
beforeArrow,
|
|
77
|
+
filePath,
|
|
78
|
+
lineNumber,
|
|
79
|
+
projectRoot,
|
|
80
|
+
lines,
|
|
81
|
+
getFunctionNameForLine
|
|
82
|
+
) {
|
|
83
|
+
const methodCallMatch = beforeArrow.match(/\.(\w+)\s*\(/);
|
|
84
|
+
if (!methodCallMatch || !methodCallMatch[1]) return null;
|
|
85
|
+
const callbackType = methodCallMatch[1];
|
|
86
|
+
const parentFunction = findParentFunctionWithFallback(
|
|
87
|
+
filePath,
|
|
88
|
+
lineNumber,
|
|
89
|
+
projectRoot,
|
|
90
|
+
lines,
|
|
91
|
+
getFunctionNameForLine
|
|
92
|
+
);
|
|
93
|
+
return formatCallbackName(callbackType, parentFunction);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function findFunctionCallCallback(
|
|
97
|
+
beforeArrow,
|
|
98
|
+
filePath,
|
|
99
|
+
lineNumber,
|
|
100
|
+
projectRoot,
|
|
101
|
+
lines,
|
|
102
|
+
getFunctionNameForLine
|
|
103
|
+
) {
|
|
104
|
+
const functionCallMatch = beforeArrow.match(
|
|
105
|
+
/(?!if|for|while|switch)\b(\w+)\s*\(/
|
|
106
|
+
);
|
|
107
|
+
if (!functionCallMatch || !functionCallMatch[1]) return null;
|
|
108
|
+
const callbackType = functionCallMatch[1];
|
|
109
|
+
const parentFunction = findParentFunctionWithFallback(
|
|
110
|
+
filePath,
|
|
111
|
+
lineNumber,
|
|
112
|
+
projectRoot,
|
|
113
|
+
lines,
|
|
114
|
+
getFunctionNameForLine
|
|
115
|
+
);
|
|
116
|
+
return formatCallbackName(callbackType, parentFunction);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function findCallbackWithFallbackPatterns(
|
|
120
|
+
combinedContext,
|
|
121
|
+
filePath,
|
|
122
|
+
lineNumber,
|
|
123
|
+
projectRoot,
|
|
124
|
+
lines,
|
|
125
|
+
getFunctionNameForLine
|
|
126
|
+
) {
|
|
127
|
+
const arrowFunctionPatterns = [
|
|
128
|
+
/\.(\w+)\s*\([^)]*\)\s*=>/,
|
|
129
|
+
/\.(\w+)\s*\([^)]*=>/,
|
|
130
|
+
/(\w+)\s*\([^)]*\)\s*=>/,
|
|
131
|
+
/(\w+)\s*\([^)]*=>/,
|
|
132
|
+
];
|
|
133
|
+
for (const pattern of arrowFunctionPatterns) {
|
|
134
|
+
const match = combinedContext.match(pattern);
|
|
135
|
+
if (match && match[1]) {
|
|
136
|
+
const parentFunction = findParentFunctionWithFallback(
|
|
137
|
+
filePath,
|
|
138
|
+
lineNumber,
|
|
139
|
+
projectRoot,
|
|
140
|
+
lines,
|
|
141
|
+
getFunctionNameForLine
|
|
142
|
+
);
|
|
143
|
+
return formatCallbackName(match[1], parentFunction);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function handleArrowFunctionFinalFallback(
|
|
150
|
+
filePath,
|
|
151
|
+
lineNumber,
|
|
152
|
+
projectRoot,
|
|
153
|
+
lines,
|
|
154
|
+
getFunctionNameForLine
|
|
155
|
+
) {
|
|
156
|
+
const parentFunction = findParentFunctionWithFallback(
|
|
157
|
+
filePath,
|
|
158
|
+
lineNumber,
|
|
159
|
+
projectRoot,
|
|
160
|
+
lines,
|
|
161
|
+
getFunctionNameForLine
|
|
162
|
+
);
|
|
163
|
+
if (
|
|
164
|
+
parentFunction &&
|
|
165
|
+
parentFunction !== 'anonymous' &&
|
|
166
|
+
parentFunction !== 'unknown'
|
|
167
|
+
) {
|
|
168
|
+
return `${parentFunction} (arrow function)`;
|
|
169
|
+
}
|
|
170
|
+
return 'anonymous arrow function';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function tryFindCallbackFromCurrentLine(
|
|
174
|
+
currentLine,
|
|
175
|
+
filePath,
|
|
176
|
+
lineNumber,
|
|
177
|
+
projectRoot,
|
|
178
|
+
lines,
|
|
179
|
+
getFunctionNameForLine
|
|
180
|
+
) {
|
|
181
|
+
const arrowIndex = currentLine.indexOf('=>');
|
|
182
|
+
if (arrowIndex === -1) return null;
|
|
183
|
+
const beforeArrow = currentLine.substring(0, arrowIndex);
|
|
184
|
+
const methodCallback = findMethodCallCallback(
|
|
185
|
+
beforeArrow,
|
|
186
|
+
filePath,
|
|
187
|
+
lineNumber,
|
|
188
|
+
projectRoot,
|
|
189
|
+
lines,
|
|
190
|
+
getFunctionNameForLine
|
|
191
|
+
);
|
|
192
|
+
if (methodCallback) return methodCallback;
|
|
193
|
+
return findFunctionCallCallback(
|
|
194
|
+
beforeArrow,
|
|
195
|
+
filePath,
|
|
196
|
+
lineNumber,
|
|
197
|
+
projectRoot,
|
|
198
|
+
lines,
|
|
199
|
+
getFunctionNameForLine
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function handleArrowFunctionExpression(
|
|
204
|
+
lines,
|
|
205
|
+
lineNumber,
|
|
206
|
+
filePath,
|
|
207
|
+
projectRoot,
|
|
208
|
+
getFunctionNameForLine
|
|
209
|
+
) {
|
|
210
|
+
const lineIndex = lineNumber - 1;
|
|
211
|
+
const currentLine = lines[lineIndex] || '';
|
|
212
|
+
const prevLine = lines[lineIndex - 1] || '';
|
|
213
|
+
const namedArrow = checkNamedArrowFunction(prevLine, currentLine);
|
|
214
|
+
if (namedArrow) return namedArrow;
|
|
215
|
+
const callbackFromLine = tryFindCallbackFromCurrentLine(
|
|
216
|
+
currentLine,
|
|
217
|
+
filePath,
|
|
218
|
+
lineNumber,
|
|
219
|
+
projectRoot,
|
|
220
|
+
lines,
|
|
221
|
+
getFunctionNameForLine
|
|
222
|
+
);
|
|
223
|
+
if (callbackFromLine) return callbackFromLine;
|
|
224
|
+
const combinedContext = (prevLine + ' ' + currentLine).trim();
|
|
225
|
+
const fallbackCallback = findCallbackWithFallbackPatterns(
|
|
226
|
+
combinedContext,
|
|
227
|
+
filePath,
|
|
228
|
+
lineNumber,
|
|
229
|
+
projectRoot,
|
|
230
|
+
lines,
|
|
231
|
+
getFunctionNameForLine
|
|
232
|
+
);
|
|
233
|
+
if (fallbackCallback) return fallbackCallback;
|
|
234
|
+
return handleArrowFunctionFinalFallback(
|
|
235
|
+
filePath,
|
|
236
|
+
lineNumber,
|
|
237
|
+
projectRoot,
|
|
238
|
+
lines,
|
|
239
|
+
getFunctionNameForLine
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function handleFunctionDeclaration(lines, lineNumber) {
|
|
244
|
+
const startLine = Math.max(0, lineNumber - 50);
|
|
245
|
+
const context = lines.slice(startLine, lineNumber).join('\n');
|
|
246
|
+
const patterns = [
|
|
247
|
+
/(?:export\s+)?function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*[<(]/,
|
|
248
|
+
/const\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*(?:\([^)]*\)\s*)?(?:=>|function)/,
|
|
249
|
+
/export\s+const\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*(?:\([^)]*\)\s*)?(?:=>|function)/,
|
|
250
|
+
/export\s+default\s+function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*[<(]/,
|
|
251
|
+
/(?:export\s+default\s+|const\s+)([A-Z][a-zA-Z0-9_$]*)\s*[:=]\s*(?:\([^)]*\)\s*)?=>/,
|
|
252
|
+
];
|
|
253
|
+
let lastMatch = null;
|
|
254
|
+
let lastIndex = -1;
|
|
255
|
+
for (const pattern of patterns) {
|
|
256
|
+
const matches = [...context.matchAll(new RegExp(pattern.source, 'g'))];
|
|
257
|
+
if (matches.length > 0) {
|
|
258
|
+
const match = matches[matches.length - 1];
|
|
259
|
+
const matchIndex = context.lastIndexOf(match[0]);
|
|
260
|
+
if (matchIndex > lastIndex) {
|
|
261
|
+
lastMatch = match[1];
|
|
262
|
+
lastIndex = matchIndex;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return lastMatch || 'anonymous';
|
|
267
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Complexity and path utilities.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function getComplexityLevel(complexity) {
|
|
6
|
+
const num = parseInt(complexity, 10);
|
|
7
|
+
if (num >= 20) return 'low';
|
|
8
|
+
if (num >= 15) return 'medium';
|
|
9
|
+
if (num > 10) return 'high';
|
|
10
|
+
if (num > 6) return 'acceptable';
|
|
11
|
+
return 'good';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getDirectory(filePath) {
|
|
15
|
+
const parts = filePath.split('/');
|
|
16
|
+
if (parts.length <= 1) return filePath;
|
|
17
|
+
return parts.slice(0, -1).join('/');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getBaseFunctionName(name) {
|
|
21
|
+
if (!name) return 'unknown';
|
|
22
|
+
let baseName = String(name).trim();
|
|
23
|
+
let prev = '';
|
|
24
|
+
while (prev !== baseName) {
|
|
25
|
+
prev = baseName;
|
|
26
|
+
baseName = baseName.replace(/\s*\([^)]+\)\s*$/g, '').replace(/\s*→\s*[^→]*$/g, '').trim();
|
|
27
|
+
}
|
|
28
|
+
return baseName || 'unknown';
|
|
29
|
+
}
|