@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,787 @@
|
|
|
1
|
+
import JavaScript from 'tree-sitter-javascript';
|
|
2
|
+
import { extractSignature, extractParameters, extractReturnType, } from '../extractors/symbol-helpers.js';
|
|
3
|
+
import { calculateComplexity } from '../complexity/index.js';
|
|
4
|
+
// =============================================================================
|
|
5
|
+
// TRAVERSERS
|
|
6
|
+
// =============================================================================
|
|
7
|
+
/**
|
|
8
|
+
* TypeScript/JavaScript AST traverser
|
|
9
|
+
*
|
|
10
|
+
* Handles TypeScript and JavaScript AST node types and traversal patterns.
|
|
11
|
+
* Both languages share the same AST structure (via tree-sitter-typescript).
|
|
12
|
+
*/
|
|
13
|
+
export class TypeScriptTraverser {
|
|
14
|
+
targetNodeTypes = [
|
|
15
|
+
'function_declaration',
|
|
16
|
+
'function',
|
|
17
|
+
'interface_declaration',
|
|
18
|
+
'method_definition',
|
|
19
|
+
'lexical_declaration', // For const/let with arrow functions
|
|
20
|
+
'variable_declaration', // For var with functions
|
|
21
|
+
];
|
|
22
|
+
containerTypes = [
|
|
23
|
+
'class_declaration', // We extract methods, not the class itself
|
|
24
|
+
];
|
|
25
|
+
declarationTypes = [
|
|
26
|
+
'lexical_declaration', // const/let
|
|
27
|
+
'variable_declaration', // var
|
|
28
|
+
];
|
|
29
|
+
functionTypes = ['arrow_function', 'function_expression', 'function'];
|
|
30
|
+
shouldExtractChildren(node) {
|
|
31
|
+
return this.containerTypes.includes(node.type);
|
|
32
|
+
}
|
|
33
|
+
isDeclarationWithFunction(node) {
|
|
34
|
+
return this.declarationTypes.includes(node.type);
|
|
35
|
+
}
|
|
36
|
+
getContainerBody(node) {
|
|
37
|
+
if (node.type === 'class_declaration') {
|
|
38
|
+
return node.childForFieldName('body');
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
shouldTraverseChildren(node) {
|
|
43
|
+
return (node.type === 'program' || node.type === 'export_statement' || node.type === 'class_body');
|
|
44
|
+
}
|
|
45
|
+
findParentContainerName(node) {
|
|
46
|
+
let current = node.parent;
|
|
47
|
+
while (current) {
|
|
48
|
+
if (current.type === 'class_declaration') {
|
|
49
|
+
const nameNode = current.childForFieldName('name');
|
|
50
|
+
return nameNode?.text;
|
|
51
|
+
}
|
|
52
|
+
current = current.parent;
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
findFunctionInDeclaration(node) {
|
|
57
|
+
const search = (n, depth) => {
|
|
58
|
+
if (depth > 3)
|
|
59
|
+
return null;
|
|
60
|
+
if (this.functionTypes.includes(n.type)) {
|
|
61
|
+
return n;
|
|
62
|
+
}
|
|
63
|
+
for (let i = 0; i < n.childCount; i++) {
|
|
64
|
+
const child = n.child(i);
|
|
65
|
+
if (child) {
|
|
66
|
+
const result = search(child, depth + 1);
|
|
67
|
+
if (result)
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
};
|
|
73
|
+
const functionNode = search(node, 0);
|
|
74
|
+
return {
|
|
75
|
+
hasFunction: functionNode !== null,
|
|
76
|
+
functionNode,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* JavaScript uses the same traverser as TypeScript
|
|
82
|
+
*/
|
|
83
|
+
export class JavaScriptTraverser extends TypeScriptTraverser {
|
|
84
|
+
}
|
|
85
|
+
// =============================================================================
|
|
86
|
+
// EXPORT EXTRACTORS
|
|
87
|
+
// =============================================================================
|
|
88
|
+
/**
|
|
89
|
+
* JavaScript/TypeScript export extractor
|
|
90
|
+
*
|
|
91
|
+
* Handles ES module exports:
|
|
92
|
+
* - Named exports: export { foo, bar }
|
|
93
|
+
* - Declaration exports: export function foo() {}, export const bar = ...
|
|
94
|
+
* - Default exports: export default ...
|
|
95
|
+
* - Re-exports: export { foo } from './module'
|
|
96
|
+
*
|
|
97
|
+
* Handles CommonJS exports:
|
|
98
|
+
* - module.exports = { foo, bar }
|
|
99
|
+
* - module.exports = function/class
|
|
100
|
+
* - exports.foo = ... / module.exports.bar = ...
|
|
101
|
+
*/
|
|
102
|
+
export class JavaScriptExportExtractor {
|
|
103
|
+
extractExports(rootNode) {
|
|
104
|
+
const exports = [];
|
|
105
|
+
const seen = new Set();
|
|
106
|
+
const addExport = (name) => {
|
|
107
|
+
if (name && !seen.has(name)) {
|
|
108
|
+
seen.add(name);
|
|
109
|
+
exports.push(name);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
for (let i = 0; i < rootNode.namedChildCount; i++) {
|
|
113
|
+
const child = rootNode.namedChild(i);
|
|
114
|
+
if (child?.type === 'export_statement') {
|
|
115
|
+
this.extractExportStatementSymbols(child, addExport);
|
|
116
|
+
}
|
|
117
|
+
else if (child?.type === 'expression_statement') {
|
|
118
|
+
this.extractCJSExportSymbols(child, addExport);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return exports;
|
|
122
|
+
}
|
|
123
|
+
extractExportStatementSymbols(node, addExport) {
|
|
124
|
+
const defaultKeyword = node.children.find(c => c.type === 'default');
|
|
125
|
+
if (defaultKeyword) {
|
|
126
|
+
addExport('default');
|
|
127
|
+
}
|
|
128
|
+
const declaration = node.childForFieldName('declaration');
|
|
129
|
+
if (declaration) {
|
|
130
|
+
this.extractDeclarationExports(declaration, addExport);
|
|
131
|
+
}
|
|
132
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
133
|
+
const child = node.namedChild(i);
|
|
134
|
+
if (child?.type === 'export_clause') {
|
|
135
|
+
this.extractExportClauseSymbols(child, addExport);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
extractDeclarationExports(node, addExport) {
|
|
140
|
+
const nameNode = node.childForFieldName('name');
|
|
141
|
+
if (nameNode) {
|
|
142
|
+
addExport(nameNode.text);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (node.type === 'lexical_declaration' || node.type === 'variable_declaration') {
|
|
146
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
147
|
+
const child = node.namedChild(i);
|
|
148
|
+
if (child?.type === 'variable_declarator') {
|
|
149
|
+
const varName = child.childForFieldName('name');
|
|
150
|
+
if (varName) {
|
|
151
|
+
addExport(varName.text);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
extractExportClauseSymbols(node, addExport) {
|
|
158
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
159
|
+
const child = node.namedChild(i);
|
|
160
|
+
if (child?.type === 'export_specifier') {
|
|
161
|
+
const aliasNode = child.childForFieldName('alias');
|
|
162
|
+
const nameNode = child.childForFieldName('name');
|
|
163
|
+
const exported = aliasNode?.text || nameNode?.text;
|
|
164
|
+
if (exported) {
|
|
165
|
+
addExport(exported);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
// CommonJS export extraction
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
isModuleExports(node) {
|
|
174
|
+
return (node.type === 'member_expression' &&
|
|
175
|
+
node.childForFieldName('object')?.text === 'module' &&
|
|
176
|
+
node.childForFieldName('property')?.text === 'exports');
|
|
177
|
+
}
|
|
178
|
+
extractCJSExportSymbols(node, addExport) {
|
|
179
|
+
const expr = node.namedChild(0);
|
|
180
|
+
if (expr?.type !== 'assignment_expression')
|
|
181
|
+
return;
|
|
182
|
+
const left = expr.childForFieldName('left');
|
|
183
|
+
const right = expr.childForFieldName('right');
|
|
184
|
+
if (!left || !right)
|
|
185
|
+
return;
|
|
186
|
+
// module.exports = ...
|
|
187
|
+
if (this.isModuleExports(left)) {
|
|
188
|
+
this.extractModuleExportsValue(right, addExport);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
// exports.foo = ... or module.exports.bar = ...
|
|
192
|
+
if (left.type !== 'member_expression')
|
|
193
|
+
return;
|
|
194
|
+
const objectNode = left.childForFieldName('object');
|
|
195
|
+
const prop = left.childForFieldName('property');
|
|
196
|
+
if (!prop)
|
|
197
|
+
return;
|
|
198
|
+
const isExportsProperty = objectNode?.text === 'exports' || (objectNode != null && this.isModuleExports(objectNode));
|
|
199
|
+
if (isExportsProperty)
|
|
200
|
+
addExport(prop.text);
|
|
201
|
+
}
|
|
202
|
+
extractModuleExportsValue(node, addExport) {
|
|
203
|
+
// module.exports = { foo, bar, baz: val }
|
|
204
|
+
if (node.type === 'object') {
|
|
205
|
+
this.extractObjectExportProperties(node, addExport);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
// module.exports = function name() {} or module.exports = class Name {}
|
|
209
|
+
if (node.type === 'function_expression' || node.type === 'class') {
|
|
210
|
+
addExport('default');
|
|
211
|
+
const nameNode = node.childForFieldName('name');
|
|
212
|
+
if (nameNode)
|
|
213
|
+
addExport(nameNode.text);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
// module.exports = identifier or anything else
|
|
217
|
+
addExport('default');
|
|
218
|
+
}
|
|
219
|
+
extractObjectExportProperties(node, addExport) {
|
|
220
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
221
|
+
const prop = node.namedChild(i);
|
|
222
|
+
if (!prop)
|
|
223
|
+
continue;
|
|
224
|
+
if (prop.type === 'shorthand_property_identifier') {
|
|
225
|
+
addExport(prop.text);
|
|
226
|
+
}
|
|
227
|
+
else if (prop.type === 'pair') {
|
|
228
|
+
const key = prop.childForFieldName('key');
|
|
229
|
+
if (key)
|
|
230
|
+
addExport(key.text);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* TypeScript uses the same export extraction as JavaScript
|
|
237
|
+
*/
|
|
238
|
+
export class TypeScriptExportExtractor extends JavaScriptExportExtractor {
|
|
239
|
+
}
|
|
240
|
+
// =============================================================================
|
|
241
|
+
// IMPORT EXTRACTORS
|
|
242
|
+
// =============================================================================
|
|
243
|
+
/**
|
|
244
|
+
* JavaScript/TypeScript import extractor
|
|
245
|
+
*
|
|
246
|
+
* Handles ES module imports:
|
|
247
|
+
* - Named imports: import { foo, bar } from './module'
|
|
248
|
+
* - Default imports: import foo from './module'
|
|
249
|
+
* - Namespace imports: import * as utils from './module'
|
|
250
|
+
* - Re-exports: export { foo } from './module'
|
|
251
|
+
*
|
|
252
|
+
* Handles CommonJS require():
|
|
253
|
+
* - const x = require('module')
|
|
254
|
+
* - const { a, b } = require('module')
|
|
255
|
+
* - const { a: alias } = require('module')
|
|
256
|
+
* - require('./side-effect')
|
|
257
|
+
*/
|
|
258
|
+
export class JavaScriptImportExtractor {
|
|
259
|
+
importNodeTypes = [
|
|
260
|
+
'import_statement',
|
|
261
|
+
'export_statement',
|
|
262
|
+
'lexical_declaration',
|
|
263
|
+
'variable_declaration',
|
|
264
|
+
'expression_statement',
|
|
265
|
+
];
|
|
266
|
+
extractImportPath(node) {
|
|
267
|
+
const sourceNode = node.childForFieldName('source');
|
|
268
|
+
if (sourceNode)
|
|
269
|
+
return sourceNode.text.replace(/['"]/g, '');
|
|
270
|
+
// Handle CommonJS require() in variable/lexical declarations
|
|
271
|
+
if (node.type === 'lexical_declaration' || node.type === 'variable_declaration') {
|
|
272
|
+
const requireInfo = this.processRequireDeclaration(node);
|
|
273
|
+
if (requireInfo?.importPath)
|
|
274
|
+
return requireInfo.importPath;
|
|
275
|
+
}
|
|
276
|
+
return this.extractRequirePath(node);
|
|
277
|
+
}
|
|
278
|
+
processImportSymbols(node) {
|
|
279
|
+
if (node.type === 'import_statement') {
|
|
280
|
+
return this.processImportStatement(node);
|
|
281
|
+
}
|
|
282
|
+
if (node.type === 'export_statement') {
|
|
283
|
+
return this.processReExportStatement(node);
|
|
284
|
+
}
|
|
285
|
+
if (node.type === 'lexical_declaration' || node.type === 'variable_declaration') {
|
|
286
|
+
return this.processRequireDeclaration(node);
|
|
287
|
+
}
|
|
288
|
+
if (node.type === 'expression_statement') {
|
|
289
|
+
return this.processBareRequire(node);
|
|
290
|
+
}
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
processImportStatement(node) {
|
|
294
|
+
const sourceNode = node.childForFieldName('source');
|
|
295
|
+
if (!sourceNode)
|
|
296
|
+
return null;
|
|
297
|
+
const importPath = sourceNode.text.replace(/['"]/g, '');
|
|
298
|
+
const symbols = this.extractImportStatementSymbols(node);
|
|
299
|
+
return symbols.length > 0 ? { importPath, symbols } : null;
|
|
300
|
+
}
|
|
301
|
+
processReExportStatement(node) {
|
|
302
|
+
const sourceNode = node.childForFieldName('source');
|
|
303
|
+
if (!sourceNode)
|
|
304
|
+
return null;
|
|
305
|
+
const importPath = sourceNode.text.replace(/['"]/g, '');
|
|
306
|
+
const symbols = [];
|
|
307
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
308
|
+
const child = node.namedChild(i);
|
|
309
|
+
if (child?.type === 'export_clause') {
|
|
310
|
+
symbols.push(...this.extractExportClauseSymbols(child));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return symbols.length > 0 ? { importPath, symbols } : null;
|
|
314
|
+
}
|
|
315
|
+
extractImportStatementSymbols(node) {
|
|
316
|
+
const symbols = [];
|
|
317
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
318
|
+
const child = node.namedChild(i);
|
|
319
|
+
if (!child)
|
|
320
|
+
continue;
|
|
321
|
+
switch (child.type) {
|
|
322
|
+
case 'identifier':
|
|
323
|
+
symbols.push(child.text);
|
|
324
|
+
break;
|
|
325
|
+
case 'import_clause':
|
|
326
|
+
this.extractImportClauseSymbols(child, symbols);
|
|
327
|
+
break;
|
|
328
|
+
case 'named_imports':
|
|
329
|
+
this.extractNamedImportSymbols(child, symbols);
|
|
330
|
+
break;
|
|
331
|
+
case 'namespace_import':
|
|
332
|
+
this.extractNamespaceImportSymbol(child, symbols);
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return symbols;
|
|
337
|
+
}
|
|
338
|
+
extractImportClauseSymbols(node, symbols) {
|
|
339
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
340
|
+
const child = node.namedChild(i);
|
|
341
|
+
if (!child)
|
|
342
|
+
continue;
|
|
343
|
+
if (child.type === 'identifier') {
|
|
344
|
+
symbols.push(child.text);
|
|
345
|
+
}
|
|
346
|
+
else if (child.type === 'named_imports') {
|
|
347
|
+
this.extractNamedImportSymbols(child, symbols);
|
|
348
|
+
}
|
|
349
|
+
else if (child.type === 'namespace_import') {
|
|
350
|
+
this.extractNamespaceImportSymbol(child, symbols);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
extractNamespaceImportSymbol(node, symbols) {
|
|
355
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
356
|
+
const child = node.namedChild(i);
|
|
357
|
+
if (child?.type === 'identifier') {
|
|
358
|
+
symbols.push(`* as ${child.text}`);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
extractNamedImportSymbols(node, symbols) {
|
|
364
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
365
|
+
const child = node.namedChild(i);
|
|
366
|
+
if (!child)
|
|
367
|
+
continue;
|
|
368
|
+
switch (child.type) {
|
|
369
|
+
case 'import_specifier': {
|
|
370
|
+
const aliasNode = child.childForFieldName('alias');
|
|
371
|
+
const nameNode = child.childForFieldName('name');
|
|
372
|
+
const symbol = aliasNode?.text || nameNode?.text || child.text;
|
|
373
|
+
if (symbol && !symbol.includes('{') && !symbol.includes('}')) {
|
|
374
|
+
symbols.push(symbol);
|
|
375
|
+
}
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
case 'identifier':
|
|
379
|
+
symbols.push(child.text);
|
|
380
|
+
break;
|
|
381
|
+
case 'named_imports':
|
|
382
|
+
this.extractNamedImportSymbols(child, symbols);
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Extract original symbol names from an export_clause (for re-exports).
|
|
389
|
+
* Uses original name (not alias) since it maps to the source module's exports.
|
|
390
|
+
*/
|
|
391
|
+
extractExportClauseSymbols(clause) {
|
|
392
|
+
const symbols = [];
|
|
393
|
+
for (let j = 0; j < clause.namedChildCount; j++) {
|
|
394
|
+
const specifier = clause.namedChild(j);
|
|
395
|
+
if (specifier?.type !== 'export_specifier')
|
|
396
|
+
continue;
|
|
397
|
+
const nameNode = specifier.childForFieldName('name');
|
|
398
|
+
const symbol = nameNode?.text || specifier.text;
|
|
399
|
+
if (symbol && !symbol.includes('{') && !symbol.includes('}')) {
|
|
400
|
+
symbols.push(symbol);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return symbols;
|
|
404
|
+
}
|
|
405
|
+
// ---------------------------------------------------------------------------
|
|
406
|
+
// CommonJS require() extraction
|
|
407
|
+
// ---------------------------------------------------------------------------
|
|
408
|
+
findRequireCall(node) {
|
|
409
|
+
// Only match require() as a direct call_expression — not nested inside other expressions
|
|
410
|
+
if (node.type === 'call_expression' && node.childForFieldName('function')?.text === 'require') {
|
|
411
|
+
return node;
|
|
412
|
+
}
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
getRequirePathFromCall(callNode) {
|
|
416
|
+
const args = callNode.childForFieldName('arguments');
|
|
417
|
+
if (!args)
|
|
418
|
+
return null;
|
|
419
|
+
const firstArg = args.namedChild(0);
|
|
420
|
+
if (!firstArg || firstArg.type !== 'string')
|
|
421
|
+
return null;
|
|
422
|
+
return firstArg.text.replace(/['"]/g, '');
|
|
423
|
+
}
|
|
424
|
+
extractRequirePath(node) {
|
|
425
|
+
// Check the node itself, then its direct named children
|
|
426
|
+
const call = this.findRequireCall(node) ?? this.findRequireCallInChildren(node);
|
|
427
|
+
if (!call)
|
|
428
|
+
return null;
|
|
429
|
+
return this.getRequirePathFromCall(call);
|
|
430
|
+
}
|
|
431
|
+
findRequireCallInChildren(node) {
|
|
432
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
433
|
+
const child = node.namedChild(i);
|
|
434
|
+
if (child) {
|
|
435
|
+
const found = this.findRequireCall(child);
|
|
436
|
+
if (found)
|
|
437
|
+
return found;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
442
|
+
processRequireDeclaration(node) {
|
|
443
|
+
// Find the variable_declarator with a require() value
|
|
444
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
445
|
+
const declarator = node.namedChild(i);
|
|
446
|
+
if (declarator?.type !== 'variable_declarator')
|
|
447
|
+
continue;
|
|
448
|
+
const value = declarator.childForFieldName('value');
|
|
449
|
+
if (!value)
|
|
450
|
+
continue;
|
|
451
|
+
const call = this.findRequireCall(value);
|
|
452
|
+
if (!call)
|
|
453
|
+
continue;
|
|
454
|
+
const importPath = this.getRequirePathFromCall(call);
|
|
455
|
+
if (!importPath)
|
|
456
|
+
continue;
|
|
457
|
+
const nameNode = declarator.childForFieldName('name');
|
|
458
|
+
if (!nameNode)
|
|
459
|
+
continue;
|
|
460
|
+
const symbols = this.extractRequireBindingSymbols(nameNode);
|
|
461
|
+
if (symbols.length > 0)
|
|
462
|
+
return { importPath, symbols };
|
|
463
|
+
}
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
extractRequireBindingSymbols(nameNode) {
|
|
467
|
+
// const express = require('express')
|
|
468
|
+
if (nameNode.type === 'identifier')
|
|
469
|
+
return [nameNode.text];
|
|
470
|
+
// const { Router, json } = require('express')
|
|
471
|
+
if (nameNode.type === 'object_pattern')
|
|
472
|
+
return this.extractObjectPatternSymbols(nameNode);
|
|
473
|
+
return [];
|
|
474
|
+
}
|
|
475
|
+
extractObjectPatternSymbols(node) {
|
|
476
|
+
const symbols = [];
|
|
477
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
478
|
+
const prop = node.namedChild(i);
|
|
479
|
+
if (!prop)
|
|
480
|
+
continue;
|
|
481
|
+
if (prop.type === 'shorthand_property_identifier_pattern') {
|
|
482
|
+
symbols.push(prop.text);
|
|
483
|
+
}
|
|
484
|
+
else if (prop.type === 'pair_pattern') {
|
|
485
|
+
// const { Router: MyRouter } = require('express')
|
|
486
|
+
const value = prop.childForFieldName('value');
|
|
487
|
+
if (value)
|
|
488
|
+
symbols.push(value.text);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return symbols;
|
|
492
|
+
}
|
|
493
|
+
processBareRequire(node) {
|
|
494
|
+
// require('./polyfill') — side-effect import, no symbols
|
|
495
|
+
const importPath = this.extractRequirePath(node);
|
|
496
|
+
if (!importPath)
|
|
497
|
+
return null;
|
|
498
|
+
return { importPath, symbols: [] };
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* TypeScript uses the same import extraction as JavaScript
|
|
503
|
+
*/
|
|
504
|
+
export class TypeScriptImportExtractor extends JavaScriptImportExtractor {
|
|
505
|
+
}
|
|
506
|
+
// =============================================================================
|
|
507
|
+
// SYMBOL EXTRACTORS
|
|
508
|
+
// =============================================================================
|
|
509
|
+
/**
|
|
510
|
+
* JavaScript/TypeScript symbol extractor
|
|
511
|
+
*
|
|
512
|
+
* Extracts symbol info from function declarations, arrow functions,
|
|
513
|
+
* method definitions, class declarations, and interface declarations.
|
|
514
|
+
*
|
|
515
|
+
* Also handles call site extraction for call_expression and new_expression.
|
|
516
|
+
*/
|
|
517
|
+
export class JavaScriptSymbolExtractor {
|
|
518
|
+
symbolNodeTypes = [
|
|
519
|
+
'function_declaration',
|
|
520
|
+
'function',
|
|
521
|
+
'arrow_function',
|
|
522
|
+
'function_expression',
|
|
523
|
+
'method_definition',
|
|
524
|
+
'class_declaration',
|
|
525
|
+
'interface_declaration',
|
|
526
|
+
];
|
|
527
|
+
extractSymbol(node, content, parentClass) {
|
|
528
|
+
switch (node.type) {
|
|
529
|
+
case 'function_declaration':
|
|
530
|
+
case 'function':
|
|
531
|
+
return this.extractFunctionInfo(node, content, parentClass);
|
|
532
|
+
case 'arrow_function':
|
|
533
|
+
case 'function_expression':
|
|
534
|
+
return this.extractArrowFunctionInfo(node, content, parentClass);
|
|
535
|
+
case 'method_definition':
|
|
536
|
+
return this.extractMethodInfo(node, content, parentClass);
|
|
537
|
+
case 'class_declaration':
|
|
538
|
+
return this.extractClassInfo(node);
|
|
539
|
+
case 'interface_declaration':
|
|
540
|
+
return this.extractInterfaceInfo(node);
|
|
541
|
+
default:
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
extractCallSite(node) {
|
|
546
|
+
const line = node.startPosition.row + 1;
|
|
547
|
+
if (node.type === 'call_expression') {
|
|
548
|
+
const functionNode = node.childForFieldName('function');
|
|
549
|
+
if (!functionNode)
|
|
550
|
+
return null;
|
|
551
|
+
const symbol = this.resolveSymbol(functionNode);
|
|
552
|
+
return symbol ? { symbol, line, key: `${symbol}:${line}` } : null;
|
|
553
|
+
}
|
|
554
|
+
if (node.type === 'new_expression') {
|
|
555
|
+
const ctorNode = node.childForFieldName('constructor');
|
|
556
|
+
if (!ctorNode)
|
|
557
|
+
return null;
|
|
558
|
+
const symbol = this.resolveSymbol(ctorNode);
|
|
559
|
+
return symbol ? { symbol, line, key: `${symbol}:${line}` } : null;
|
|
560
|
+
}
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
563
|
+
resolveSymbol(node) {
|
|
564
|
+
if (node.type === 'identifier')
|
|
565
|
+
return node.text;
|
|
566
|
+
if (node.type === 'member_expression') {
|
|
567
|
+
const propertyNode = node.childForFieldName('property');
|
|
568
|
+
if (propertyNode?.type === 'property_identifier')
|
|
569
|
+
return propertyNode.text;
|
|
570
|
+
}
|
|
571
|
+
return null;
|
|
572
|
+
}
|
|
573
|
+
extractFunctionInfo(node, content, parentClass) {
|
|
574
|
+
const nameNode = node.childForFieldName('name');
|
|
575
|
+
if (!nameNode)
|
|
576
|
+
return null;
|
|
577
|
+
return {
|
|
578
|
+
name: nameNode.text,
|
|
579
|
+
type: parentClass ? 'method' : 'function',
|
|
580
|
+
startLine: node.startPosition.row + 1,
|
|
581
|
+
endLine: node.endPosition.row + 1,
|
|
582
|
+
parentClass,
|
|
583
|
+
signature: extractSignature(node, content),
|
|
584
|
+
parameters: extractParameters(node, content),
|
|
585
|
+
returnType: extractReturnType(node, content),
|
|
586
|
+
complexity: calculateComplexity(node),
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
extractArrowFunctionInfo(node, content, parentClass) {
|
|
590
|
+
const parent = node.parent;
|
|
591
|
+
let name = 'anonymous';
|
|
592
|
+
if (parent?.type === 'variable_declarator') {
|
|
593
|
+
const nameNode = parent.childForFieldName('name');
|
|
594
|
+
name = nameNode?.text || 'anonymous';
|
|
595
|
+
}
|
|
596
|
+
return {
|
|
597
|
+
name,
|
|
598
|
+
type: parentClass ? 'method' : 'function',
|
|
599
|
+
startLine: node.startPosition.row + 1,
|
|
600
|
+
endLine: node.endPosition.row + 1,
|
|
601
|
+
parentClass,
|
|
602
|
+
signature: extractSignature(node, content),
|
|
603
|
+
parameters: extractParameters(node, content),
|
|
604
|
+
complexity: calculateComplexity(node),
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
extractMethodInfo(node, content, parentClass) {
|
|
608
|
+
const nameNode = node.childForFieldName('name');
|
|
609
|
+
if (!nameNode)
|
|
610
|
+
return null;
|
|
611
|
+
return {
|
|
612
|
+
name: nameNode.text,
|
|
613
|
+
type: 'method',
|
|
614
|
+
startLine: node.startPosition.row + 1,
|
|
615
|
+
endLine: node.endPosition.row + 1,
|
|
616
|
+
parentClass,
|
|
617
|
+
signature: extractSignature(node, content),
|
|
618
|
+
parameters: extractParameters(node, content),
|
|
619
|
+
returnType: extractReturnType(node, content),
|
|
620
|
+
complexity: calculateComplexity(node),
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
extractClassInfo(node) {
|
|
624
|
+
const nameNode = node.childForFieldName('name');
|
|
625
|
+
if (!nameNode)
|
|
626
|
+
return null;
|
|
627
|
+
return {
|
|
628
|
+
name: nameNode.text,
|
|
629
|
+
type: 'class',
|
|
630
|
+
startLine: node.startPosition.row + 1,
|
|
631
|
+
endLine: node.endPosition.row + 1,
|
|
632
|
+
signature: `class ${nameNode.text}`,
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
extractInterfaceInfo(node) {
|
|
636
|
+
const nameNode = node.childForFieldName('name');
|
|
637
|
+
if (!nameNode)
|
|
638
|
+
return null;
|
|
639
|
+
return {
|
|
640
|
+
name: nameNode.text,
|
|
641
|
+
type: 'interface',
|
|
642
|
+
startLine: node.startPosition.row + 1,
|
|
643
|
+
endLine: node.endPosition.row + 1,
|
|
644
|
+
signature: `interface ${nameNode.text}`,
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* TypeScript uses the same symbol extraction as JavaScript
|
|
650
|
+
*/
|
|
651
|
+
export class TypeScriptSymbolExtractor extends JavaScriptSymbolExtractor {
|
|
652
|
+
}
|
|
653
|
+
// =============================================================================
|
|
654
|
+
// LANGUAGE DEFINITION
|
|
655
|
+
// =============================================================================
|
|
656
|
+
export const javascriptDefinition = {
|
|
657
|
+
id: 'javascript',
|
|
658
|
+
extensions: ['js', 'jsx', 'mjs', 'cjs'],
|
|
659
|
+
grammar: JavaScript,
|
|
660
|
+
traverser: new JavaScriptTraverser(),
|
|
661
|
+
exportExtractor: new JavaScriptExportExtractor(),
|
|
662
|
+
importExtractor: new JavaScriptImportExtractor(),
|
|
663
|
+
symbolExtractor: new JavaScriptSymbolExtractor(),
|
|
664
|
+
complexity: {
|
|
665
|
+
decisionPoints: [
|
|
666
|
+
'if_statement',
|
|
667
|
+
'while_statement',
|
|
668
|
+
'for_statement',
|
|
669
|
+
'switch_case',
|
|
670
|
+
'catch_clause',
|
|
671
|
+
'ternary_expression',
|
|
672
|
+
'binary_expression',
|
|
673
|
+
'do_statement',
|
|
674
|
+
'for_in_statement',
|
|
675
|
+
'for_of_statement',
|
|
676
|
+
],
|
|
677
|
+
nestingTypes: [
|
|
678
|
+
'if_statement',
|
|
679
|
+
'for_statement',
|
|
680
|
+
'while_statement',
|
|
681
|
+
'switch_statement',
|
|
682
|
+
'catch_clause',
|
|
683
|
+
'do_statement',
|
|
684
|
+
'for_in_statement',
|
|
685
|
+
'for_of_statement',
|
|
686
|
+
],
|
|
687
|
+
nonNestingTypes: ['else_clause', 'ternary_expression'],
|
|
688
|
+
lambdaTypes: ['arrow_function', 'function_expression'],
|
|
689
|
+
operatorSymbols: new Set([
|
|
690
|
+
'+',
|
|
691
|
+
'-',
|
|
692
|
+
'*',
|
|
693
|
+
'/',
|
|
694
|
+
'%',
|
|
695
|
+
'**',
|
|
696
|
+
'==',
|
|
697
|
+
'===',
|
|
698
|
+
'!=',
|
|
699
|
+
'!==',
|
|
700
|
+
'<',
|
|
701
|
+
'>',
|
|
702
|
+
'<=',
|
|
703
|
+
'>=',
|
|
704
|
+
'&&',
|
|
705
|
+
'||',
|
|
706
|
+
'!',
|
|
707
|
+
'??',
|
|
708
|
+
'=',
|
|
709
|
+
'+=',
|
|
710
|
+
'-=',
|
|
711
|
+
'*=',
|
|
712
|
+
'/=',
|
|
713
|
+
'%=',
|
|
714
|
+
'**=',
|
|
715
|
+
'&&=',
|
|
716
|
+
'||=',
|
|
717
|
+
'??=',
|
|
718
|
+
'&',
|
|
719
|
+
'|',
|
|
720
|
+
'^',
|
|
721
|
+
'~',
|
|
722
|
+
'<<',
|
|
723
|
+
'>>',
|
|
724
|
+
'>>>',
|
|
725
|
+
'&=',
|
|
726
|
+
'|=',
|
|
727
|
+
'^=',
|
|
728
|
+
'<<=',
|
|
729
|
+
'>>=',
|
|
730
|
+
'>>>=',
|
|
731
|
+
'?',
|
|
732
|
+
':',
|
|
733
|
+
'.',
|
|
734
|
+
'?.',
|
|
735
|
+
'++',
|
|
736
|
+
'--',
|
|
737
|
+
'...',
|
|
738
|
+
'=>',
|
|
739
|
+
'(',
|
|
740
|
+
')',
|
|
741
|
+
'[',
|
|
742
|
+
']',
|
|
743
|
+
'{',
|
|
744
|
+
'}',
|
|
745
|
+
]),
|
|
746
|
+
operatorKeywords: new Set([
|
|
747
|
+
'if',
|
|
748
|
+
'else',
|
|
749
|
+
'for',
|
|
750
|
+
'while',
|
|
751
|
+
'do',
|
|
752
|
+
'switch',
|
|
753
|
+
'case',
|
|
754
|
+
'default',
|
|
755
|
+
'return',
|
|
756
|
+
'throw',
|
|
757
|
+
'try',
|
|
758
|
+
'catch',
|
|
759
|
+
'finally',
|
|
760
|
+
'new',
|
|
761
|
+
'delete',
|
|
762
|
+
'typeof',
|
|
763
|
+
'instanceof',
|
|
764
|
+
'in',
|
|
765
|
+
'of',
|
|
766
|
+
'await',
|
|
767
|
+
'yield',
|
|
768
|
+
'break',
|
|
769
|
+
'continue',
|
|
770
|
+
'const',
|
|
771
|
+
'let',
|
|
772
|
+
'var',
|
|
773
|
+
'function',
|
|
774
|
+
'class',
|
|
775
|
+
'extends',
|
|
776
|
+
'implements',
|
|
777
|
+
'import',
|
|
778
|
+
'export',
|
|
779
|
+
'from',
|
|
780
|
+
'as',
|
|
781
|
+
]),
|
|
782
|
+
},
|
|
783
|
+
symbols: {
|
|
784
|
+
callExpressionTypes: ['call_expression', 'new_expression'],
|
|
785
|
+
},
|
|
786
|
+
};
|
|
787
|
+
//# sourceMappingURL=javascript.js.map
|