@liendev/parser 0.39.0
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/dist/ast/chunker.d.ts +30 -0
- package/dist/ast/chunker.d.ts.map +1 -0
- package/dist/ast/chunker.js +310 -0
- package/dist/ast/chunker.js.map +1 -0
- package/dist/ast/complexity/cognitive.d.ts +16 -0
- package/dist/ast/complexity/cognitive.d.ts.map +1 -0
- package/dist/ast/complexity/cognitive.js +137 -0
- package/dist/ast/complexity/cognitive.js.map +1 -0
- package/dist/ast/complexity/cyclomatic.d.ts +12 -0
- package/dist/ast/complexity/cyclomatic.d.ts.map +1 -0
- package/dist/ast/complexity/cyclomatic.js +54 -0
- package/dist/ast/complexity/cyclomatic.js.map +1 -0
- package/dist/ast/complexity/halstead.d.ts +56 -0
- package/dist/ast/complexity/halstead.d.ts.map +1 -0
- package/dist/ast/complexity/halstead.js +196 -0
- package/dist/ast/complexity/halstead.js.map +1 -0
- package/dist/ast/complexity/index.d.ts +13 -0
- package/dist/ast/complexity/index.d.ts.map +1 -0
- package/dist/ast/complexity/index.js +12 -0
- package/dist/ast/complexity/index.js.map +1 -0
- package/dist/ast/extractors/index.d.ts +35 -0
- package/dist/ast/extractors/index.d.ts.map +1 -0
- package/dist/ast/extractors/index.js +41 -0
- package/dist/ast/extractors/index.js.map +1 -0
- package/dist/ast/extractors/symbol-helpers.d.ts +20 -0
- package/dist/ast/extractors/symbol-helpers.d.ts.map +1 -0
- package/dist/ast/extractors/symbol-helpers.js +58 -0
- package/dist/ast/extractors/symbol-helpers.js.map +1 -0
- package/dist/ast/extractors/types.d.ts +108 -0
- package/dist/ast/extractors/types.d.ts.map +1 -0
- package/dist/ast/extractors/types.js +2 -0
- package/dist/ast/extractors/types.js.map +1 -0
- package/dist/ast/languages/javascript.d.ts +134 -0
- package/dist/ast/languages/javascript.d.ts.map +1 -0
- package/dist/ast/languages/javascript.js +787 -0
- package/dist/ast/languages/javascript.js.map +1 -0
- package/dist/ast/languages/php.d.ts +84 -0
- package/dist/ast/languages/php.d.ts.map +1 -0
- package/dist/ast/languages/php.js +452 -0
- package/dist/ast/languages/php.js.map +1 -0
- package/dist/ast/languages/python.d.ts +96 -0
- package/dist/ast/languages/python.d.ts.map +1 -0
- package/dist/ast/languages/python.js +448 -0
- package/dist/ast/languages/python.js.map +1 -0
- package/dist/ast/languages/registry.d.ts +30 -0
- package/dist/ast/languages/registry.d.ts.map +1 -0
- package/dist/ast/languages/registry.js +95 -0
- package/dist/ast/languages/registry.js.map +1 -0
- package/dist/ast/languages/rust.d.ts +113 -0
- package/dist/ast/languages/rust.d.ts.map +1 -0
- package/dist/ast/languages/rust.js +614 -0
- package/dist/ast/languages/rust.js.map +1 -0
- package/dist/ast/languages/types.d.ts +52 -0
- package/dist/ast/languages/types.d.ts.map +1 -0
- package/dist/ast/languages/types.js +2 -0
- package/dist/ast/languages/types.js.map +1 -0
- package/dist/ast/languages/typescript.d.ts +3 -0
- package/dist/ast/languages/typescript.d.ts.map +1 -0
- package/dist/ast/languages/typescript.js +134 -0
- package/dist/ast/languages/typescript.js.map +1 -0
- package/dist/ast/parser.d.ts +29 -0
- package/dist/ast/parser.d.ts.map +1 -0
- package/dist/ast/parser.js +67 -0
- package/dist/ast/parser.js.map +1 -0
- package/dist/ast/symbols.d.ts +74 -0
- package/dist/ast/symbols.d.ts.map +1 -0
- package/dist/ast/symbols.js +171 -0
- package/dist/ast/symbols.js.map +1 -0
- package/dist/ast/traversers/index.d.ts +19 -0
- package/dist/ast/traversers/index.d.ts.map +1 -0
- package/dist/ast/traversers/index.js +21 -0
- package/dist/ast/traversers/index.js.map +1 -0
- package/dist/ast/traversers/types.d.ts +98 -0
- package/dist/ast/traversers/types.d.ts.map +1 -0
- package/dist/ast/traversers/types.js +2 -0
- package/dist/ast/traversers/types.js.map +1 -0
- package/dist/ast/types.d.ts +54 -0
- package/dist/ast/types.d.ts.map +1 -0
- package/dist/ast/types.js +2 -0
- package/dist/ast/types.js.map +1 -0
- package/dist/chunk-only-index.d.ts +25 -0
- package/dist/chunk-only-index.d.ts.map +1 -0
- package/dist/chunk-only-index.js +107 -0
- package/dist/chunk-only-index.js.map +1 -0
- package/dist/chunker.d.ts +12 -0
- package/dist/chunker.d.ts.map +1 -0
- package/dist/chunker.js +98 -0
- package/dist/chunker.js.map +1 -0
- package/dist/constants.d.ts +8 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +11 -0
- package/dist/constants.js.map +1 -0
- package/dist/content-hash.d.ts +20 -0
- package/dist/content-hash.d.ts.map +1 -0
- package/dist/content-hash.js +91 -0
- package/dist/content-hash.js.map +1 -0
- package/dist/dependency-analyzer.d.ts +79 -0
- package/dist/dependency-analyzer.d.ts.map +1 -0
- package/dist/dependency-analyzer.js +408 -0
- package/dist/dependency-analyzer.js.map +1 -0
- package/dist/ecosystem-presets.d.ts +32 -0
- package/dist/ecosystem-presets.d.ts.map +1 -0
- package/dist/ecosystem-presets.js +325 -0
- package/dist/ecosystem-presets.js.map +1 -0
- package/dist/gitignore.d.ts +22 -0
- package/dist/gitignore.d.ts.map +1 -0
- package/dist/gitignore.js +128 -0
- package/dist/gitignore.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -0
- package/dist/insights/chunk-complexity.d.ts +89 -0
- package/dist/insights/chunk-complexity.d.ts.map +1 -0
- package/dist/insights/chunk-complexity.js +332 -0
- package/dist/insights/chunk-complexity.js.map +1 -0
- package/dist/insights/types.d.ts +73 -0
- package/dist/insights/types.d.ts.map +1 -0
- package/dist/insights/types.js +9 -0
- package/dist/insights/types.js.map +1 -0
- package/dist/json-template-chunker.d.ts +12 -0
- package/dist/json-template-chunker.d.ts.map +1 -0
- package/dist/json-template-chunker.js +87 -0
- package/dist/json-template-chunker.js.map +1 -0
- package/dist/liquid-chunker.d.ts +16 -0
- package/dist/liquid-chunker.d.ts.map +1 -0
- package/dist/liquid-chunker.js +274 -0
- package/dist/liquid-chunker.js.map +1 -0
- package/dist/scanner.d.ts +16 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +95 -0
- package/dist/scanner.js.map +1 -0
- package/dist/symbol-extractor.d.ts +18 -0
- package/dist/symbol-extractor.d.ts.map +1 -0
- package/dist/symbol-extractor.js +343 -0
- package/dist/symbol-extractor.js.map +1 -0
- package/dist/test-associations.d.ts +16 -0
- package/dist/test-associations.d.ts.map +1 -0
- package/dist/test-associations.js +43 -0
- package/dist/test-associations.js.map +1 -0
- package/dist/types.d.ts +75 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/path-matching.d.ts +71 -0
- package/dist/utils/path-matching.d.ts.map +1 -0
- package/dist/utils/path-matching.js +258 -0
- package/dist/utils/path-matching.js.map +1 -0
- package/dist/utils/repo-id.d.ts +6 -0
- package/dist/utils/repo-id.d.ts.map +1 -0
- package/dist/utils/repo-id.js +12 -0
- package/dist/utils/repo-id.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ASTChunk } from './types.js';
|
|
2
|
+
export interface ASTChunkOptions {
|
|
3
|
+
maxChunkSize?: number;
|
|
4
|
+
minChunkSize?: number;
|
|
5
|
+
repoId?: string;
|
|
6
|
+
orgId?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Chunk a file using AST-based semantic boundaries
|
|
10
|
+
*
|
|
11
|
+
* Uses Tree-sitter to parse code into an AST and extract semantic chunks
|
|
12
|
+
* (functions, classes, methods) that respect code structure.
|
|
13
|
+
*
|
|
14
|
+
* **Known Limitations:**
|
|
15
|
+
* - Tree-sitter may fail with "Invalid argument" error on very large files (1000+ lines)
|
|
16
|
+
* - When this occurs, Lien automatically falls back to line-based chunking
|
|
17
|
+
* - Configure fallback behavior via `chunking.astFallback` ('line-based' or 'error')
|
|
18
|
+
*
|
|
19
|
+
* @param filepath - Path to the file
|
|
20
|
+
* @param content - File content
|
|
21
|
+
* @param options - Chunking options
|
|
22
|
+
* @returns Array of AST-aware chunks
|
|
23
|
+
* @throws Error if AST parsing fails and astFallback is 'error'
|
|
24
|
+
*/
|
|
25
|
+
export declare function chunkByAST(filepath: string, content: string, options?: ASTChunkOptions): ASTChunk[];
|
|
26
|
+
/**
|
|
27
|
+
* Check if AST chunking should be used for a file
|
|
28
|
+
*/
|
|
29
|
+
export declare function shouldUseAST(filepath: string): boolean;
|
|
30
|
+
//# sourceMappingURL=chunker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunker.d.ts","sourceRoot":"","sources":["../../src/ast/chunker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,YAAY,CAAC;AAY9D,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAwGD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,UAAU,CACxB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,eAAoB,GAC5B,QAAQ,EAAE,CA0CZ;AA+SD;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEtD"}
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { parseAST, detectLanguage, isASTSupported } from './parser.js';
|
|
2
|
+
import { extractSymbolInfo, extractImports, extractImportedSymbols, extractExports, extractCallSites, } from './symbols.js';
|
|
3
|
+
import { calculateCognitiveComplexity, calculateHalstead } from './complexity/index.js';
|
|
4
|
+
import { getTraverser } from './traversers/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* Validate language support and parse the file.
|
|
7
|
+
* @throws Error if language not supported or parsing fails
|
|
8
|
+
*/
|
|
9
|
+
function parseAndValidate(filepath, content) {
|
|
10
|
+
const language = detectLanguage(filepath);
|
|
11
|
+
if (!language) {
|
|
12
|
+
throw new Error(`Unsupported language for file: ${filepath}`);
|
|
13
|
+
}
|
|
14
|
+
const parseResult = parseAST(content, language);
|
|
15
|
+
if (!parseResult.tree) {
|
|
16
|
+
throw new Error(`Failed to parse ${filepath}: ${parseResult.error}`);
|
|
17
|
+
}
|
|
18
|
+
return { language, rootNode: parseResult.tree.rootNode };
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Prepare AST context by extracting imports, exports, and symbols.
|
|
22
|
+
*/
|
|
23
|
+
function prepareASTContext(content, rootNode, language) {
|
|
24
|
+
return {
|
|
25
|
+
lines: content.split('\n'),
|
|
26
|
+
fileImports: extractImports(rootNode, language),
|
|
27
|
+
importedSymbols: extractImportedSymbols(rootNode, language),
|
|
28
|
+
fileExports: extractExports(rootNode, language),
|
|
29
|
+
traverser: getTraverser(language),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Process a single top-level node into a chunk.
|
|
34
|
+
*/
|
|
35
|
+
function processTopLevelNode(node, filepath, content, context, language, tenantContext) {
|
|
36
|
+
const { lines, fileImports, fileExports, importedSymbols, traverser } = context;
|
|
37
|
+
// For variable declarations, try to find the function inside
|
|
38
|
+
let actualNode = node;
|
|
39
|
+
if (traverser.isDeclarationWithFunction(node)) {
|
|
40
|
+
const declInfo = traverser.findFunctionInDeclaration(node);
|
|
41
|
+
if (declInfo.functionNode) {
|
|
42
|
+
actualNode = declInfo.functionNode;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// For methods, find the parent container name (e.g., class name)
|
|
46
|
+
const parentClassName = traverser.findParentContainerName(actualNode);
|
|
47
|
+
const symbolInfo = extractSymbolInfo(actualNode, content, parentClassName, language);
|
|
48
|
+
const nodeContent = getNodeContent(node, lines);
|
|
49
|
+
return createChunk(filepath, node, nodeContent, symbolInfo, fileImports, language, tenantContext, fileExports, importedSymbols);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Process all top-level nodes into chunks.
|
|
53
|
+
*/
|
|
54
|
+
function processTopLevelNodes(topLevelNodes, filepath, content, context, language, tenantContext) {
|
|
55
|
+
return topLevelNodes.map(node => processTopLevelNode(node, filepath, content, context, language, tenantContext));
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Chunk a file using AST-based semantic boundaries
|
|
59
|
+
*
|
|
60
|
+
* Uses Tree-sitter to parse code into an AST and extract semantic chunks
|
|
61
|
+
* (functions, classes, methods) that respect code structure.
|
|
62
|
+
*
|
|
63
|
+
* **Known Limitations:**
|
|
64
|
+
* - Tree-sitter may fail with "Invalid argument" error on very large files (1000+ lines)
|
|
65
|
+
* - When this occurs, Lien automatically falls back to line-based chunking
|
|
66
|
+
* - Configure fallback behavior via `chunking.astFallback` ('line-based' or 'error')
|
|
67
|
+
*
|
|
68
|
+
* @param filepath - Path to the file
|
|
69
|
+
* @param content - File content
|
|
70
|
+
* @param options - Chunking options
|
|
71
|
+
* @returns Array of AST-aware chunks
|
|
72
|
+
* @throws Error if AST parsing fails and astFallback is 'error'
|
|
73
|
+
*/
|
|
74
|
+
export function chunkByAST(filepath, content, options = {}) {
|
|
75
|
+
const { minChunkSize = 5, repoId, orgId } = options;
|
|
76
|
+
const tenantContext = { repoId, orgId };
|
|
77
|
+
// Parse and validate
|
|
78
|
+
const { language, rootNode } = parseAndValidate(filepath, content);
|
|
79
|
+
// Prepare context
|
|
80
|
+
const context = prepareASTContext(content, rootNode, language);
|
|
81
|
+
// Find and process top-level nodes
|
|
82
|
+
const topLevelNodes = findTopLevelNodes(rootNode, context.traverser);
|
|
83
|
+
const topLevelChunks = processTopLevelNodes(topLevelNodes, filepath, content, context, language, tenantContext);
|
|
84
|
+
// Extract uncovered code (imports, exports, top-level statements)
|
|
85
|
+
const coveredRanges = topLevelNodes.map(n => ({
|
|
86
|
+
start: n.startPosition.row,
|
|
87
|
+
end: n.endPosition.row,
|
|
88
|
+
}));
|
|
89
|
+
const uncoveredChunks = extractUncoveredCode(context.lines, coveredRanges, filepath, minChunkSize, context.fileImports, language, tenantContext, context.fileExports, context.importedSymbols);
|
|
90
|
+
// Combine and sort by line number
|
|
91
|
+
return [...topLevelChunks, ...uncoveredChunks].sort((a, b) => a.metadata.startLine - b.metadata.startLine);
|
|
92
|
+
}
|
|
93
|
+
/** Check if node is a function-containing declaration at top level */
|
|
94
|
+
function isFunctionDeclaration(node, depth, traverser) {
|
|
95
|
+
if (depth !== 0 || !traverser.isDeclarationWithFunction(node))
|
|
96
|
+
return false;
|
|
97
|
+
return traverser.findFunctionInDeclaration(node).hasFunction;
|
|
98
|
+
}
|
|
99
|
+
/** Check if node is a target type at valid depth */
|
|
100
|
+
function isTargetNode(node, depth, traverser) {
|
|
101
|
+
return depth <= 1 && traverser.targetNodeTypes.includes(node.type);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Find all top-level nodes that should become chunks
|
|
105
|
+
*
|
|
106
|
+
* Uses a language-specific traverser to handle different AST structures.
|
|
107
|
+
* This function is now language-agnostic - all language-specific logic
|
|
108
|
+
* is delegated to the traverser.
|
|
109
|
+
*
|
|
110
|
+
* @param rootNode - Root AST node
|
|
111
|
+
* @param traverser - Language-specific traverser
|
|
112
|
+
* @returns Array of nodes to extract as chunks
|
|
113
|
+
*/
|
|
114
|
+
function findTopLevelNodes(rootNode, traverser) {
|
|
115
|
+
const nodes = [];
|
|
116
|
+
function traverse(node, depth) {
|
|
117
|
+
// Capture function declarations and target nodes
|
|
118
|
+
if (isFunctionDeclaration(node, depth, traverser) || isTargetNode(node, depth, traverser)) {
|
|
119
|
+
nodes.push(node);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
// Handle containers - emit the container itself AND traverse body for children
|
|
123
|
+
if (traverser.shouldExtractChildren(node)) {
|
|
124
|
+
nodes.push(node);
|
|
125
|
+
const body = traverser.getContainerBody(node);
|
|
126
|
+
if (body)
|
|
127
|
+
traverse(body, depth + 1);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
// Traverse children of traversable nodes
|
|
131
|
+
if (!traverser.shouldTraverseChildren(node))
|
|
132
|
+
return;
|
|
133
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
134
|
+
const child = node.namedChild(i);
|
|
135
|
+
if (child)
|
|
136
|
+
traverse(child, depth);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
traverse(rootNode, 0);
|
|
140
|
+
return nodes;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Extract content for a specific AST node
|
|
144
|
+
*/
|
|
145
|
+
function getNodeContent(node, lines) {
|
|
146
|
+
const startLine = node.startPosition.row;
|
|
147
|
+
const endLine = node.endPosition.row;
|
|
148
|
+
return lines.slice(startLine, endLine + 1).join('\n');
|
|
149
|
+
}
|
|
150
|
+
/** Maps symbol types to legacy symbol array keys */
|
|
151
|
+
const SYMBOL_TYPE_TO_ARRAY = {
|
|
152
|
+
function: 'functions',
|
|
153
|
+
method: 'functions',
|
|
154
|
+
class: 'classes',
|
|
155
|
+
interface: 'interfaces',
|
|
156
|
+
};
|
|
157
|
+
/** Symbol types that have meaningful complexity metrics */
|
|
158
|
+
const COMPLEXITY_SYMBOL_TYPES = new Set(['function', 'method']);
|
|
159
|
+
/**
|
|
160
|
+
* Build legacy symbols object for backward compatibility
|
|
161
|
+
*/
|
|
162
|
+
function buildLegacySymbols(symbolInfo) {
|
|
163
|
+
const symbols = {
|
|
164
|
+
functions: [],
|
|
165
|
+
classes: [],
|
|
166
|
+
interfaces: [],
|
|
167
|
+
};
|
|
168
|
+
if (symbolInfo?.name && symbolInfo.type) {
|
|
169
|
+
const arrayKey = SYMBOL_TYPE_TO_ARRAY[symbolInfo.type];
|
|
170
|
+
if (arrayKey)
|
|
171
|
+
symbols[arrayKey].push(symbolInfo.name);
|
|
172
|
+
}
|
|
173
|
+
return symbols;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Determine chunk type from symbol info
|
|
177
|
+
*/
|
|
178
|
+
function getChunkType(symbolInfo) {
|
|
179
|
+
if (!symbolInfo)
|
|
180
|
+
return 'block';
|
|
181
|
+
return symbolInfo.type === 'class' ? 'class' : 'function';
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Create a chunk from an AST node
|
|
185
|
+
*/
|
|
186
|
+
function createChunk(filepath, node, content, symbolInfo, imports, language, tenantContext, fileExports, importedSymbols) {
|
|
187
|
+
const symbols = buildLegacySymbols(symbolInfo);
|
|
188
|
+
const shouldCalcComplexity = symbolInfo?.type && COMPLEXITY_SYMBOL_TYPES.has(symbolInfo.type);
|
|
189
|
+
// Calculate complexity metrics only for functions and methods
|
|
190
|
+
const cognitiveComplexity = shouldCalcComplexity ? calculateCognitiveComplexity(node) : undefined;
|
|
191
|
+
// Calculate Halstead metrics only for functions and methods
|
|
192
|
+
const halstead = shouldCalcComplexity ? calculateHalstead(node, language) : undefined;
|
|
193
|
+
// Extract call sites for functions and methods
|
|
194
|
+
const callSites = shouldCalcComplexity ? extractCallSites(node, language) : undefined;
|
|
195
|
+
return {
|
|
196
|
+
content,
|
|
197
|
+
metadata: {
|
|
198
|
+
file: filepath,
|
|
199
|
+
startLine: node.startPosition.row + 1,
|
|
200
|
+
endLine: node.endPosition.row + 1,
|
|
201
|
+
type: getChunkType(symbolInfo),
|
|
202
|
+
language,
|
|
203
|
+
symbols,
|
|
204
|
+
symbolName: symbolInfo?.name,
|
|
205
|
+
symbolType: symbolInfo?.type,
|
|
206
|
+
parentClass: symbolInfo?.parentClass,
|
|
207
|
+
complexity: symbolInfo?.complexity,
|
|
208
|
+
cognitiveComplexity,
|
|
209
|
+
parameters: symbolInfo?.parameters,
|
|
210
|
+
signature: symbolInfo?.signature,
|
|
211
|
+
returnType: symbolInfo?.returnType,
|
|
212
|
+
imports,
|
|
213
|
+
// Symbol-level dependency tracking
|
|
214
|
+
// NOTE: `exports` and `importedSymbols` are file-level concepts, but we deliberately
|
|
215
|
+
// attach them to every chunk from the same file (including "uncovered" chunks).
|
|
216
|
+
// This duplicates some metadata, but greatly simplifies dependency analysis,
|
|
217
|
+
// since consumers can inspect a single chunk in isolation without additional lookups.
|
|
218
|
+
// This increases storage overhead but is acceptable given typical file sizes and chunk counts.
|
|
219
|
+
...(fileExports && fileExports.length > 0 && { exports: fileExports }),
|
|
220
|
+
...(importedSymbols && Object.keys(importedSymbols).length > 0 && { importedSymbols }),
|
|
221
|
+
...(callSites && callSites.length > 0 && { callSites }),
|
|
222
|
+
// Halstead metrics
|
|
223
|
+
halsteadVolume: halstead?.volume,
|
|
224
|
+
halsteadDifficulty: halstead?.difficulty,
|
|
225
|
+
halsteadEffort: halstead?.effort,
|
|
226
|
+
halsteadBugs: halstead?.bugs,
|
|
227
|
+
// Multi-tenant fields
|
|
228
|
+
...(tenantContext?.repoId && { repoId: tenantContext.repoId }),
|
|
229
|
+
...(tenantContext?.orgId && { orgId: tenantContext.orgId }),
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Find gaps between covered ranges (uncovered code)
|
|
235
|
+
*/
|
|
236
|
+
function findUncoveredRanges(coveredRanges, totalLines) {
|
|
237
|
+
const uncoveredRanges = [];
|
|
238
|
+
let currentStart = 0;
|
|
239
|
+
// Sort covered ranges
|
|
240
|
+
const sortedRanges = [...coveredRanges].sort((a, b) => a.start - b.start);
|
|
241
|
+
for (const range of sortedRanges) {
|
|
242
|
+
if (currentStart < range.start) {
|
|
243
|
+
// There's a gap before this range
|
|
244
|
+
uncoveredRanges.push({
|
|
245
|
+
start: currentStart,
|
|
246
|
+
end: range.start - 1,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
currentStart = range.end + 1;
|
|
250
|
+
}
|
|
251
|
+
// Handle remaining code after last covered range
|
|
252
|
+
if (currentStart < totalLines) {
|
|
253
|
+
uncoveredRanges.push({
|
|
254
|
+
start: currentStart,
|
|
255
|
+
end: totalLines - 1,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
return uncoveredRanges;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Create a chunk from a line range
|
|
262
|
+
*/
|
|
263
|
+
function createChunkFromRange(range, lines, filepath, language, imports, tenantContext, fileExports, importedSymbols) {
|
|
264
|
+
const uncoveredLines = lines.slice(range.start, range.end + 1);
|
|
265
|
+
const content = uncoveredLines.join('\n').trim();
|
|
266
|
+
return {
|
|
267
|
+
content,
|
|
268
|
+
metadata: {
|
|
269
|
+
file: filepath,
|
|
270
|
+
startLine: range.start + 1,
|
|
271
|
+
endLine: range.end + 1,
|
|
272
|
+
type: 'block',
|
|
273
|
+
language,
|
|
274
|
+
// Empty symbols for uncovered code (imports, exports, etc.)
|
|
275
|
+
symbols: { functions: [], classes: [], interfaces: [] },
|
|
276
|
+
imports,
|
|
277
|
+
// Symbol-level dependency tracking
|
|
278
|
+
...(fileExports && fileExports.length > 0 && { exports: fileExports }),
|
|
279
|
+
...(importedSymbols && Object.keys(importedSymbols).length > 0 && { importedSymbols }),
|
|
280
|
+
// Multi-tenant fields
|
|
281
|
+
...(tenantContext?.repoId && { repoId: tenantContext.repoId }),
|
|
282
|
+
...(tenantContext?.orgId && { orgId: tenantContext.orgId }),
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Validate that a chunk meets the minimum size requirements
|
|
288
|
+
*/
|
|
289
|
+
function isValidChunk(chunk, minChunkSize) {
|
|
290
|
+
const lineCount = chunk.metadata.endLine - chunk.metadata.startLine + 1;
|
|
291
|
+
return chunk.content.length > 0 && lineCount >= minChunkSize;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Extract code that wasn't covered by function/class chunks
|
|
295
|
+
* (imports, exports, top-level statements)
|
|
296
|
+
*/
|
|
297
|
+
function extractUncoveredCode(lines, coveredRanges, filepath, minChunkSize, imports, language, tenantContext, fileExports, importedSymbols) {
|
|
298
|
+
const uncoveredRanges = findUncoveredRanges(coveredRanges, lines.length);
|
|
299
|
+
const hasExports = fileExports && fileExports.length > 0;
|
|
300
|
+
return uncoveredRanges
|
|
301
|
+
.map(range => createChunkFromRange(range, lines, filepath, language, imports, tenantContext, fileExports, importedSymbols))
|
|
302
|
+
.filter(chunk => (hasExports ? chunk.content.length > 0 : isValidChunk(chunk, minChunkSize)));
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Check if AST chunking should be used for a file
|
|
306
|
+
*/
|
|
307
|
+
export function shouldUseAST(filepath) {
|
|
308
|
+
return isASTSupported(filepath);
|
|
309
|
+
}
|
|
310
|
+
//# sourceMappingURL=chunker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunker.js","sourceRoot":"","sources":["../../src/ast/chunker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type Parser from 'tree-sitter';
|
|
2
|
+
/**
|
|
3
|
+
* Calculate cognitive complexity of a function
|
|
4
|
+
*
|
|
5
|
+
* Based on SonarSource's Cognitive Complexity specification:
|
|
6
|
+
* - +1 for each break from linear flow (if, for, while, catch, etc.)
|
|
7
|
+
* - +1 for each nesting level when inside a control structure
|
|
8
|
+
* - +1 for each logical operator sequence break (a && b || c)
|
|
9
|
+
*
|
|
10
|
+
* @see https://www.sonarsource.com/docs/CognitiveComplexity.pdf
|
|
11
|
+
*
|
|
12
|
+
* @param node - AST node to analyze (typically a function/method)
|
|
13
|
+
* @returns Cognitive complexity score (minimum 0)
|
|
14
|
+
*/
|
|
15
|
+
export declare function calculateCognitiveComplexity(node: Parser.SyntaxNode): number;
|
|
16
|
+
//# sourceMappingURL=cognitive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cognitive.d.ts","sourceRoot":"","sources":["../../../src/ast/complexity/cognitive.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AA8HtC;;;;;;;;;;;;GAYG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAsC5E"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { getAllLanguages } from '../languages/registry.js';
|
|
2
|
+
/**
|
|
3
|
+
* Lazily-built union sets from all language definitions.
|
|
4
|
+
*/
|
|
5
|
+
let nestingTypesCache = null;
|
|
6
|
+
let nonNestingTypesCache = null;
|
|
7
|
+
let lambdaTypesCache = null;
|
|
8
|
+
function getNestingTypes() {
|
|
9
|
+
if (!nestingTypesCache) {
|
|
10
|
+
nestingTypesCache = new Set();
|
|
11
|
+
for (const lang of getAllLanguages()) {
|
|
12
|
+
for (const type of lang.complexity.nestingTypes) {
|
|
13
|
+
nestingTypesCache.add(type);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return nestingTypesCache;
|
|
18
|
+
}
|
|
19
|
+
function getNonNestingTypes() {
|
|
20
|
+
if (!nonNestingTypesCache) {
|
|
21
|
+
nonNestingTypesCache = new Set();
|
|
22
|
+
for (const lang of getAllLanguages()) {
|
|
23
|
+
for (const type of lang.complexity.nonNestingTypes) {
|
|
24
|
+
nonNestingTypesCache.add(type);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return nonNestingTypesCache;
|
|
29
|
+
}
|
|
30
|
+
function getLambdaTypes() {
|
|
31
|
+
if (!lambdaTypesCache) {
|
|
32
|
+
lambdaTypesCache = new Set();
|
|
33
|
+
for (const lang of getAllLanguages()) {
|
|
34
|
+
for (const type of lang.complexity.lambdaTypes) {
|
|
35
|
+
lambdaTypesCache.add(type);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return lambdaTypesCache;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Check if node is a logical operator and return normalized form
|
|
43
|
+
*/
|
|
44
|
+
function getLogicalOperator(node) {
|
|
45
|
+
if (node.type !== 'binary_expression' && node.type !== 'boolean_operator') {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const operator = node.childForFieldName('operator');
|
|
49
|
+
const opText = operator?.text;
|
|
50
|
+
if (opText === '&&' || opText === 'and')
|
|
51
|
+
return '&&';
|
|
52
|
+
if (opText === '||' || opText === 'or')
|
|
53
|
+
return '||';
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Determine nesting level for a child node based on SonarSource spec.
|
|
58
|
+
*/
|
|
59
|
+
function getChildNestingLevel(parent, child, currentLevel, nonNestingTypes) {
|
|
60
|
+
const isCondition = parent.childForFieldName('condition') === child;
|
|
61
|
+
const isNonNestingChild = nonNestingTypes.has(child.type);
|
|
62
|
+
return !isCondition && !isNonNestingChild ? currentLevel + 1 : currentLevel;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get complexity increment for nested lambda (only adds if already nested)
|
|
66
|
+
*/
|
|
67
|
+
function getNestedLambdaIncrement(nodeType, nestingLevel, lambdaTypes) {
|
|
68
|
+
return lambdaTypes.has(nodeType) && nestingLevel > 0 ? 1 : 0;
|
|
69
|
+
}
|
|
70
|
+
/** Traverse logical operator children, passing the operator type */
|
|
71
|
+
function traverseLogicalChildren(n, level, op, ctx) {
|
|
72
|
+
const operator = n.childForFieldName('operator');
|
|
73
|
+
for (let i = 0; i < n.namedChildCount; i++) {
|
|
74
|
+
const child = n.namedChild(i);
|
|
75
|
+
if (child && child !== operator)
|
|
76
|
+
ctx.traverse(child, level, op);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** Traverse nesting type children with proper nesting level adjustment */
|
|
80
|
+
function traverseNestingChildren(n, level, nonNestingTypes, ctx) {
|
|
81
|
+
for (let i = 0; i < n.namedChildCount; i++) {
|
|
82
|
+
const child = n.namedChild(i);
|
|
83
|
+
if (child)
|
|
84
|
+
ctx.traverse(child, getChildNestingLevel(n, child, level, nonNestingTypes), null);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** Traverse all children at specified level */
|
|
88
|
+
function traverseAllChildren(n, level, ctx) {
|
|
89
|
+
for (let i = 0; i < n.namedChildCount; i++) {
|
|
90
|
+
const child = n.namedChild(i);
|
|
91
|
+
if (child)
|
|
92
|
+
ctx.traverse(child, level, null);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Calculate cognitive complexity of a function
|
|
97
|
+
*
|
|
98
|
+
* Based on SonarSource's Cognitive Complexity specification:
|
|
99
|
+
* - +1 for each break from linear flow (if, for, while, catch, etc.)
|
|
100
|
+
* - +1 for each nesting level when inside a control structure
|
|
101
|
+
* - +1 for each logical operator sequence break (a && b || c)
|
|
102
|
+
*
|
|
103
|
+
* @see https://www.sonarsource.com/docs/CognitiveComplexity.pdf
|
|
104
|
+
*
|
|
105
|
+
* @param node - AST node to analyze (typically a function/method)
|
|
106
|
+
* @returns Cognitive complexity score (minimum 0)
|
|
107
|
+
*/
|
|
108
|
+
export function calculateCognitiveComplexity(node) {
|
|
109
|
+
let complexity = 0;
|
|
110
|
+
const ctx = { traverse };
|
|
111
|
+
const nestingTypes = getNestingTypes();
|
|
112
|
+
const nonNestingTypes = getNonNestingTypes();
|
|
113
|
+
const lambdaTypes = getLambdaTypes();
|
|
114
|
+
function traverse(n, nestingLevel, lastLogicalOp) {
|
|
115
|
+
const logicalOp = getLogicalOperator(n);
|
|
116
|
+
if (logicalOp) {
|
|
117
|
+
complexity += lastLogicalOp !== logicalOp ? 1 : 0;
|
|
118
|
+
traverseLogicalChildren(n, nestingLevel, logicalOp, ctx);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (nestingTypes.has(n.type)) {
|
|
122
|
+
complexity += 1 + nestingLevel;
|
|
123
|
+
traverseNestingChildren(n, nestingLevel, nonNestingTypes, ctx);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (nonNestingTypes.has(n.type)) {
|
|
127
|
+
complexity += 1;
|
|
128
|
+
traverseAllChildren(n, nestingLevel + 1, ctx);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
complexity += getNestedLambdaIncrement(n.type, nestingLevel, lambdaTypes);
|
|
132
|
+
traverseAllChildren(n, nestingLevel, ctx);
|
|
133
|
+
}
|
|
134
|
+
traverse(node, 0, null);
|
|
135
|
+
return complexity;
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=cognitive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cognitive.js","sourceRoot":"","sources":["../../../src/ast/complexity/cognitive.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type Parser from 'tree-sitter';
|
|
2
|
+
/**
|
|
3
|
+
* Calculate cyclomatic complexity of a function
|
|
4
|
+
*
|
|
5
|
+
* Complexity = 1 (base) + number of decision points
|
|
6
|
+
* Decision points: if, while, do...while, for, for...in, for...of, foreach, case, catch, &&, ||, ?:
|
|
7
|
+
*
|
|
8
|
+
* @param node - AST node to analyze (typically a function/method)
|
|
9
|
+
* @returns Cyclomatic complexity score (minimum 1)
|
|
10
|
+
*/
|
|
11
|
+
export declare function calculateComplexity(node: Parser.SyntaxNode): number;
|
|
12
|
+
//# sourceMappingURL=cyclomatic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cyclomatic.d.ts","sourceRoot":"","sources":["../../../src/ast/complexity/cyclomatic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAsBtC;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CA0BnE"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { getAllLanguages } from '../languages/registry.js';
|
|
2
|
+
/**
|
|
3
|
+
* Build the union set of decision point node types from all language definitions.
|
|
4
|
+
* Lazily initialized on first use.
|
|
5
|
+
*/
|
|
6
|
+
let decisionPointsCache = null;
|
|
7
|
+
function getDecisionPoints() {
|
|
8
|
+
if (!decisionPointsCache) {
|
|
9
|
+
const set = new Set();
|
|
10
|
+
for (const lang of getAllLanguages()) {
|
|
11
|
+
for (const type of lang.complexity.decisionPoints) {
|
|
12
|
+
set.add(type);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
decisionPointsCache = set;
|
|
16
|
+
}
|
|
17
|
+
return decisionPointsCache;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Calculate cyclomatic complexity of a function
|
|
21
|
+
*
|
|
22
|
+
* Complexity = 1 (base) + number of decision points
|
|
23
|
+
* Decision points: if, while, do...while, for, for...in, for...of, foreach, case, catch, &&, ||, ?:
|
|
24
|
+
*
|
|
25
|
+
* @param node - AST node to analyze (typically a function/method)
|
|
26
|
+
* @returns Cyclomatic complexity score (minimum 1)
|
|
27
|
+
*/
|
|
28
|
+
export function calculateComplexity(node) {
|
|
29
|
+
let complexity = 1; // Base complexity
|
|
30
|
+
const decisionPoints = getDecisionPoints();
|
|
31
|
+
function traverse(n) {
|
|
32
|
+
if (decisionPoints.has(n.type)) {
|
|
33
|
+
// For binary expressions, only count && and ||
|
|
34
|
+
if (n.type === 'binary_expression') {
|
|
35
|
+
const operator = n.childForFieldName('operator');
|
|
36
|
+
if (operator && (operator.text === '&&' || operator.text === '||')) {
|
|
37
|
+
complexity++;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
complexity++;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Traverse children
|
|
45
|
+
for (let i = 0; i < n.namedChildCount; i++) {
|
|
46
|
+
const child = n.namedChild(i);
|
|
47
|
+
if (child)
|
|
48
|
+
traverse(child);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
traverse(node);
|
|
52
|
+
return complexity;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=cyclomatic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cyclomatic.js","sourceRoot":"","sources":["../../../src/ast/complexity/cyclomatic.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE3D;;;GAGG;AACH,IAAI,mBAAmB,GAAuB,IAAI,CAAC;AAEnD,SAAS,iBAAiB;IACxB,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;QAC9B,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,EAAE,CAAC;YACrC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,cAAc,EAAE,CAAC;gBAClD,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC;QACH,CAAC;QACD,mBAAmB,GAAG,GAAG,CAAC;IAC5B,CAAC;IACD,OAAO,mBAAmB,CAAC;AAC7B,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAuB;IACzD,IAAI,UAAU,GAAG,CAAC,CAAC,CAAC,kBAAkB;IACtC,MAAM,cAAc,GAAG,iBAAiB,EAAE,CAAC;IAE3C,SAAS,QAAQ,CAAC,CAAoB;QACpC,IAAI,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,+CAA+C;YAC/C,IAAI,CAAC,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;gBACnC,MAAM,QAAQ,GAAG,CAAC,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;gBACjD,IAAI,QAAQ,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,IAAI,IAAI,QAAQ,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;oBACnE,UAAU,EAAE,CAAC;gBACf,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,UAAU,EAAE,CAAC;YACf,CAAC;QACH,CAAC;QAED,oBAAoB;QACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,eAAe,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3C,MAAM,KAAK,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YAC9B,IAAI,KAAK;gBAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,QAAQ,CAAC,IAAI,CAAC,CAAC;IACf,OAAO,UAAU,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type Parser from 'tree-sitter';
|
|
2
|
+
import type { SupportedLanguage } from '../languages/registry.js';
|
|
3
|
+
/** Raw Halstead counts from AST */
|
|
4
|
+
export interface HalsteadCounts {
|
|
5
|
+
n1: number;
|
|
6
|
+
n2: number;
|
|
7
|
+
N1: number;
|
|
8
|
+
N2: number;
|
|
9
|
+
operators: Map<string, number>;
|
|
10
|
+
operands: Map<string, number>;
|
|
11
|
+
}
|
|
12
|
+
/** Calculated Halstead metrics */
|
|
13
|
+
export interface HalsteadMetrics {
|
|
14
|
+
vocabulary: number;
|
|
15
|
+
length: number;
|
|
16
|
+
volume: number;
|
|
17
|
+
difficulty: number;
|
|
18
|
+
effort: number;
|
|
19
|
+
time: number;
|
|
20
|
+
bugs: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Count operators and operands in an AST node
|
|
24
|
+
*
|
|
25
|
+
* @param node - AST node to analyze (typically a function/method)
|
|
26
|
+
* @param language - Programming language for language-specific handling
|
|
27
|
+
* @returns HalsteadCounts with raw operator/operand counts
|
|
28
|
+
*/
|
|
29
|
+
export declare function countHalstead(node: Parser.SyntaxNode, language: SupportedLanguage): HalsteadCounts;
|
|
30
|
+
/**
|
|
31
|
+
* Calculate derived Halstead metrics from raw counts
|
|
32
|
+
*
|
|
33
|
+
* Formulas based on Maurice Halstead's "Elements of Software Science" (1977):
|
|
34
|
+
* - Vocabulary (n) = n1 + n2
|
|
35
|
+
* - Length (N) = N1 + N2
|
|
36
|
+
* - Volume (V) = N × log₂(n) - size of implementation
|
|
37
|
+
* - Difficulty (D) = (n1/2) × (N2/n2) - error-proneness
|
|
38
|
+
* - Effort (E) = D × V - mental effort required
|
|
39
|
+
* - Time (T) = E / 18 - seconds to understand (Stroud number)
|
|
40
|
+
* - Bugs (B) = E^(2/3) / 3000 - estimated delivered bugs (effort-based variant)
|
|
41
|
+
*
|
|
42
|
+
* @param counts - Raw Halstead counts from countHalstead()
|
|
43
|
+
* @returns Calculated HalsteadMetrics
|
|
44
|
+
*/
|
|
45
|
+
export declare function calculateHalsteadMetrics(counts: HalsteadCounts): HalsteadMetrics;
|
|
46
|
+
/**
|
|
47
|
+
* Calculate Halstead metrics for an AST node in one call
|
|
48
|
+
*
|
|
49
|
+
* Convenience function that combines countHalstead and calculateHalsteadMetrics.
|
|
50
|
+
*
|
|
51
|
+
* @param node - AST node to analyze
|
|
52
|
+
* @param language - Programming language
|
|
53
|
+
* @returns Calculated HalsteadMetrics
|
|
54
|
+
*/
|
|
55
|
+
export declare function calculateHalstead(node: Parser.SyntaxNode, language: SupportedLanguage): HalsteadMetrics;
|
|
56
|
+
//# sourceMappingURL=halstead.d.ts.map
|