@veewo/gitnexus 1.3.11 → 1.4.6-rc
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/README.md +37 -80
- package/dist/benchmark/agent-context/tool-runner.js +2 -2
- package/dist/benchmark/neonspark-candidates.js +3 -3
- package/dist/benchmark/tool-runner.js +2 -2
- package/dist/cli/ai-context.d.ts +2 -1
- package/dist/cli/ai-context.js +16 -12
- package/dist/cli/analyze.d.ts +2 -0
- package/dist/cli/analyze.js +68 -48
- package/dist/cli/augment.js +1 -1
- package/dist/cli/eval-server.d.ts +8 -1
- package/dist/cli/eval-server.js +30 -13
- package/dist/cli/index.js +28 -82
- package/dist/cli/lazy-action.d.ts +6 -0
- package/dist/cli/lazy-action.js +18 -0
- package/dist/cli/mcp.js +3 -1
- package/dist/cli/setup.js +87 -48
- package/dist/cli/setup.test.js +18 -13
- package/dist/cli/skill-gen.d.ts +26 -0
- package/dist/cli/skill-gen.js +549 -0
- package/dist/cli/status.js +13 -4
- package/dist/cli/tool.d.ts +3 -2
- package/dist/cli/tool.js +50 -16
- package/dist/cli/wiki.js +8 -4
- package/dist/config/ignore-service.d.ts +25 -0
- package/dist/config/ignore-service.js +76 -0
- package/dist/config/supported-languages.d.ts +4 -1
- package/dist/config/supported-languages.js +3 -2
- package/dist/core/augmentation/engine.js +94 -67
- package/dist/core/embeddings/embedder.d.ts +1 -1
- package/dist/core/embeddings/embedder.js +1 -1
- package/dist/core/embeddings/embedding-pipeline.d.ts +3 -3
- package/dist/core/embeddings/embedding-pipeline.js +52 -25
- package/dist/core/embeddings/types.d.ts +1 -1
- package/dist/core/graph/types.d.ts +7 -2
- package/dist/core/ingestion/ast-cache.js +3 -2
- package/dist/core/ingestion/call-processor.d.ts +8 -6
- package/dist/core/ingestion/call-processor.js +468 -206
- package/dist/core/ingestion/call-routing.d.ts +53 -0
- package/dist/core/ingestion/call-routing.js +108 -0
- package/dist/core/ingestion/constants.d.ts +16 -0
- package/dist/core/ingestion/constants.js +16 -0
- package/dist/core/ingestion/entry-point-scoring.d.ts +2 -1
- package/dist/core/ingestion/entry-point-scoring.js +116 -23
- package/dist/core/ingestion/export-detection.d.ts +18 -0
- package/dist/core/ingestion/export-detection.js +231 -0
- package/dist/core/ingestion/filesystem-walker.js +4 -3
- package/dist/core/ingestion/framework-detection.d.ts +19 -4
- package/dist/core/ingestion/framework-detection.js +182 -6
- package/dist/core/ingestion/heritage-processor.d.ts +13 -5
- package/dist/core/ingestion/heritage-processor.js +109 -55
- package/dist/core/ingestion/import-processor.d.ts +16 -20
- package/dist/core/ingestion/import-processor.js +199 -579
- package/dist/core/ingestion/language-config.d.ts +46 -0
- package/dist/core/ingestion/language-config.js +167 -0
- package/dist/core/ingestion/mro-processor.d.ts +45 -0
- package/dist/core/ingestion/mro-processor.js +369 -0
- package/dist/core/ingestion/named-binding-extraction.d.ts +61 -0
- package/dist/core/ingestion/named-binding-extraction.js +363 -0
- package/dist/core/ingestion/parsing-processor.d.ts +4 -1
- package/dist/core/ingestion/parsing-processor.js +107 -109
- package/dist/core/ingestion/pipeline.d.ts +6 -3
- package/dist/core/ingestion/pipeline.js +208 -114
- package/dist/core/ingestion/process-processor.js +8 -2
- package/dist/core/ingestion/resolution-context.d.ts +53 -0
- package/dist/core/ingestion/resolution-context.js +132 -0
- package/dist/core/ingestion/resolvers/csharp.d.ts +22 -0
- package/dist/core/ingestion/resolvers/csharp.js +109 -0
- package/dist/core/ingestion/resolvers/go.d.ts +19 -0
- package/dist/core/ingestion/resolvers/go.js +42 -0
- package/dist/core/ingestion/resolvers/index.d.ts +18 -0
- package/dist/core/ingestion/resolvers/index.js +13 -0
- package/dist/core/ingestion/resolvers/jvm.d.ts +23 -0
- package/dist/core/ingestion/resolvers/jvm.js +87 -0
- package/dist/core/ingestion/resolvers/php.d.ts +15 -0
- package/dist/core/ingestion/resolvers/php.js +35 -0
- package/dist/core/ingestion/resolvers/python.d.ts +19 -0
- package/dist/core/ingestion/resolvers/python.js +52 -0
- package/dist/core/ingestion/resolvers/ruby.d.ts +12 -0
- package/dist/core/ingestion/resolvers/ruby.js +15 -0
- package/dist/core/ingestion/resolvers/rust.d.ts +15 -0
- package/dist/core/ingestion/resolvers/rust.js +73 -0
- package/dist/core/ingestion/resolvers/standard.d.ts +28 -0
- package/dist/core/ingestion/resolvers/standard.js +123 -0
- package/dist/core/ingestion/resolvers/utils.d.ts +33 -0
- package/dist/core/ingestion/resolvers/utils.js +122 -0
- package/dist/core/ingestion/symbol-table.d.ts +21 -1
- package/dist/core/ingestion/symbol-table.js +40 -12
- package/dist/core/ingestion/tree-sitter-queries.d.ts +13 -10
- package/dist/core/ingestion/tree-sitter-queries.js +297 -7
- package/dist/core/ingestion/type-env.d.ts +49 -0
- package/dist/core/ingestion/type-env.js +611 -0
- package/dist/core/ingestion/type-extractors/c-cpp.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +385 -0
- package/dist/core/ingestion/type-extractors/csharp.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/csharp.js +383 -0
- package/dist/core/ingestion/type-extractors/go.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/go.js +467 -0
- package/dist/core/ingestion/type-extractors/index.d.ts +22 -0
- package/dist/core/ingestion/type-extractors/index.js +31 -0
- package/dist/core/ingestion/type-extractors/jvm.d.ts +3 -0
- package/dist/core/ingestion/type-extractors/jvm.js +681 -0
- package/dist/core/ingestion/type-extractors/php.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/php.js +549 -0
- package/dist/core/ingestion/type-extractors/python.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/python.js +406 -0
- package/dist/core/ingestion/type-extractors/ruby.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/ruby.js +389 -0
- package/dist/core/ingestion/type-extractors/rust.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/rust.js +449 -0
- package/dist/core/ingestion/type-extractors/shared.d.ts +133 -0
- package/dist/core/ingestion/type-extractors/shared.js +703 -0
- package/dist/core/ingestion/type-extractors/swift.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/swift.js +137 -0
- package/dist/core/ingestion/type-extractors/types.d.ts +127 -0
- package/dist/core/ingestion/type-extractors/typescript.d.ts +2 -0
- package/dist/core/ingestion/type-extractors/typescript.js +494 -0
- package/dist/core/ingestion/utils.d.ts +103 -0
- package/dist/core/ingestion/utils.js +1085 -4
- package/dist/core/ingestion/workers/parse-worker.d.ts +51 -4
- package/dist/core/ingestion/workers/parse-worker.js +634 -222
- package/dist/core/ingestion/workers/worker-pool.js +8 -0
- package/dist/core/{kuzu → lbug}/csv-generator.d.ts +12 -10
- package/dist/core/{kuzu → lbug}/csv-generator.js +82 -101
- package/dist/core/{kuzu/kuzu-adapter.d.ts → lbug/lbug-adapter.d.ts} +20 -25
- package/dist/core/{kuzu/kuzu-adapter.js → lbug/lbug-adapter.js} +150 -122
- package/dist/core/{kuzu → lbug}/schema.d.ts +4 -4
- package/dist/core/{kuzu → lbug}/schema.js +23 -22
- package/dist/core/lbug/schema.test.d.ts +1 -0
- package/dist/core/search/bm25-index.d.ts +4 -4
- package/dist/core/search/bm25-index.js +12 -11
- package/dist/core/search/hybrid-search.d.ts +2 -2
- package/dist/core/search/hybrid-search.js +6 -6
- package/dist/core/tree-sitter/parser-loader.d.ts +1 -0
- package/dist/core/tree-sitter/parser-loader.js +19 -0
- package/dist/core/wiki/generator.d.ts +2 -2
- package/dist/core/wiki/generator.js +6 -6
- package/dist/core/wiki/graph-queries.d.ts +4 -4
- package/dist/core/wiki/graph-queries.js +7 -7
- package/dist/mcp/compatible-stdio-transport.d.ts +25 -0
- package/dist/mcp/compatible-stdio-transport.js +200 -0
- package/dist/mcp/core/{kuzu-adapter.d.ts → lbug-adapter.d.ts} +11 -10
- package/dist/mcp/core/lbug-adapter.js +327 -0
- package/dist/mcp/local/local-backend.d.ts +21 -16
- package/dist/mcp/local/local-backend.js +306 -706
- package/dist/mcp/local/unity-parity-seed-loader.d.ts +6 -1
- package/dist/mcp/local/unity-parity-seed-loader.js +119 -9
- package/dist/mcp/local/unity-parity-seed-loader.test.js +95 -7
- package/dist/mcp/resources.js +2 -2
- package/dist/mcp/server.js +28 -13
- package/dist/mcp/staleness.js +2 -2
- package/dist/mcp/tools.js +12 -3
- package/dist/server/api.js +12 -12
- package/dist/server/mcp-http.d.ts +1 -1
- package/dist/server/mcp-http.js +1 -1
- package/dist/storage/git.js +4 -1
- package/dist/storage/repo-manager.d.ts +20 -2
- package/dist/storage/repo-manager.js +74 -4
- package/dist/types/pipeline.d.ts +1 -1
- package/hooks/claude/gitnexus-hook.cjs +149 -46
- package/hooks/claude/pre-tool-use.sh +2 -1
- package/hooks/claude/session-start.sh +0 -0
- package/package.json +20 -4
- package/scripts/patch-tree-sitter-swift.cjs +74 -0
- package/skills/gitnexus-cli.md +8 -8
- package/skills/gitnexus-debugging.md +1 -1
- package/skills/gitnexus-exploring.md +1 -1
- package/skills/gitnexus-guide.md +1 -1
- package/skills/gitnexus-impact-analysis.md +1 -1
- package/skills/gitnexus-pr-review.md +163 -0
- package/skills/gitnexus-refactoring.md +1 -1
- package/dist/cli/claude-hooks.d.ts +0 -22
- package/dist/cli/claude-hooks.js +0 -97
- package/dist/mcp/core/kuzu-adapter.js +0 -231
- /package/dist/core/{kuzu/csv-generator.test.d.ts → ingestion/type-extractors/types.js} +0 -0
- /package/dist/core/{kuzu/relationship-pair-buckets.test.d.ts → lbug/csv-generator.test.d.ts} +0 -0
- /package/dist/core/{kuzu → lbug}/csv-generator.test.js +0 -0
- /package/dist/core/{kuzu → lbug}/relationship-pair-buckets.d.ts +0 -0
- /package/dist/core/{kuzu → lbug}/relationship-pair-buckets.js +0 -0
- /package/dist/core/{kuzu/schema.test.d.ts → lbug/relationship-pair-buckets.test.d.ts} +0 -0
- /package/dist/core/{kuzu → lbug}/relationship-pair-buckets.test.js +0 -0
- /package/dist/core/{kuzu → lbug}/schema.test.js +0 -0
|
@@ -10,10 +10,33 @@ import CSharp from 'tree-sitter-c-sharp';
|
|
|
10
10
|
import Go from 'tree-sitter-go';
|
|
11
11
|
import Rust from 'tree-sitter-rust';
|
|
12
12
|
import PHP from 'tree-sitter-php';
|
|
13
|
+
import Ruby from 'tree-sitter-ruby';
|
|
14
|
+
import { createRequire } from 'node:module';
|
|
13
15
|
import { SupportedLanguages } from '../../../config/supported-languages.js';
|
|
14
16
|
import { LANGUAGE_QUERIES } from '../tree-sitter-queries.js';
|
|
15
|
-
import {
|
|
17
|
+
import { getTreeSitterBufferSize, TREE_SITTER_MAX_BUFFER } from '../constants.js';
|
|
18
|
+
// tree-sitter-swift is an optionalDependency — may not be installed
|
|
19
|
+
const _require = createRequire(import.meta.url);
|
|
20
|
+
let Swift = null;
|
|
21
|
+
try {
|
|
22
|
+
Swift = _require('tree-sitter-swift');
|
|
23
|
+
}
|
|
24
|
+
catch { }
|
|
25
|
+
// tree-sitter-kotlin is an optionalDependency — may not be installed
|
|
26
|
+
let Kotlin = null;
|
|
27
|
+
try {
|
|
28
|
+
Kotlin = _require('tree-sitter-kotlin');
|
|
29
|
+
}
|
|
30
|
+
catch { }
|
|
31
|
+
import { getLanguageFromFilename, FUNCTION_NODE_TYPES, extractFunctionName, isBuiltInOrNoise, getDefinitionNodeFromCaptures, findEnclosingClassId, extractMethodSignature, countCallArguments, inferCallForm, extractReceiverName, extractReceiverNode, CALL_EXPRESSION_TYPES, extractCallChain, } from '../utils.js';
|
|
32
|
+
import { buildTypeEnv } from '../type-env.js';
|
|
33
|
+
import { isNodeExported } from '../export-detection.js';
|
|
34
|
+
import { detectFrameworkFromAST } from '../framework-detection.js';
|
|
35
|
+
import { typeConfigs } from '../type-extractors/index.js';
|
|
16
36
|
import { generateId } from '../../../lib/utils.js';
|
|
37
|
+
import { extractNamedBindings } from '../named-binding-extraction.js';
|
|
38
|
+
import { appendKotlinWildcard } from '../resolvers/index.js';
|
|
39
|
+
import { callRouters } from '../call-routing.js';
|
|
17
40
|
// ============================================================================
|
|
18
41
|
// Worker-local parser + language map
|
|
19
42
|
// ============================================================================
|
|
@@ -29,7 +52,22 @@ const languageMap = {
|
|
|
29
52
|
[SupportedLanguages.CSharp]: CSharp,
|
|
30
53
|
[SupportedLanguages.Go]: Go,
|
|
31
54
|
[SupportedLanguages.Rust]: Rust,
|
|
55
|
+
...(Kotlin ? { [SupportedLanguages.Kotlin]: Kotlin } : {}),
|
|
32
56
|
[SupportedLanguages.PHP]: PHP.php_only,
|
|
57
|
+
[SupportedLanguages.Ruby]: Ruby,
|
|
58
|
+
...(Swift ? { [SupportedLanguages.Swift]: Swift } : {}),
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Check if a language grammar is available in this worker.
|
|
62
|
+
* Duplicated from parser-loader.ts because workers can't import from the main thread.
|
|
63
|
+
* Extra filePath parameter needed to distinguish .tsx from .ts (different grammars
|
|
64
|
+
* under the same SupportedLanguages.TypeScript key).
|
|
65
|
+
*/
|
|
66
|
+
const isLanguageAvailable = (language, filePath) => {
|
|
67
|
+
const key = language === SupportedLanguages.TypeScript && filePath.endsWith('.tsx')
|
|
68
|
+
? `${language}:tsx`
|
|
69
|
+
: language;
|
|
70
|
+
return key in languageMap && languageMap[key] != null;
|
|
33
71
|
};
|
|
34
72
|
const setLanguage = (language, filePath) => {
|
|
35
73
|
const key = language === SupportedLanguages.TypeScript && filePath.endsWith('.tsx')
|
|
@@ -40,148 +78,16 @@ const setLanguage = (language, filePath) => {
|
|
|
40
78
|
throw new Error(`Unsupported language: ${language}`);
|
|
41
79
|
parser.setLanguage(lang);
|
|
42
80
|
};
|
|
43
|
-
//
|
|
44
|
-
// Export detection (copied — needs AST parent traversal, can't cross threads)
|
|
45
|
-
// ============================================================================
|
|
46
|
-
const isNodeExported = (node, name, language) => {
|
|
47
|
-
let current = node;
|
|
48
|
-
switch (language) {
|
|
49
|
-
case 'javascript':
|
|
50
|
-
case 'typescript':
|
|
51
|
-
while (current) {
|
|
52
|
-
const type = current.type;
|
|
53
|
-
if (type === 'export_statement' ||
|
|
54
|
-
type === 'export_specifier' ||
|
|
55
|
-
type === 'lexical_declaration' && current.parent?.type === 'export_statement') {
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
if (current.text?.startsWith('export ')) {
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
current = current.parent;
|
|
62
|
-
}
|
|
63
|
-
return false;
|
|
64
|
-
case 'python':
|
|
65
|
-
return !name.startsWith('_');
|
|
66
|
-
case 'java':
|
|
67
|
-
while (current) {
|
|
68
|
-
if (current.parent) {
|
|
69
|
-
const parent = current.parent;
|
|
70
|
-
for (let i = 0; i < parent.childCount; i++) {
|
|
71
|
-
const child = parent.child(i);
|
|
72
|
-
if (child?.type === 'modifiers' && child.text?.includes('public')) {
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (parent.type === 'method_declaration' || parent.type === 'constructor_declaration') {
|
|
77
|
-
if (parent.text?.trimStart().startsWith('public')) {
|
|
78
|
-
return true;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
current = current.parent;
|
|
83
|
-
}
|
|
84
|
-
return false;
|
|
85
|
-
case 'csharp':
|
|
86
|
-
while (current) {
|
|
87
|
-
if (current.type === 'modifier' || current.type === 'modifiers') {
|
|
88
|
-
if (current.text?.includes('public'))
|
|
89
|
-
return true;
|
|
90
|
-
}
|
|
91
|
-
current = current.parent;
|
|
92
|
-
}
|
|
93
|
-
return false;
|
|
94
|
-
case 'go':
|
|
95
|
-
if (name.length === 0)
|
|
96
|
-
return false;
|
|
97
|
-
const first = name[0];
|
|
98
|
-
return first === first.toUpperCase() && first !== first.toLowerCase();
|
|
99
|
-
case 'rust':
|
|
100
|
-
while (current) {
|
|
101
|
-
if (current.type === 'visibility_modifier') {
|
|
102
|
-
if (current.text?.includes('pub'))
|
|
103
|
-
return true;
|
|
104
|
-
}
|
|
105
|
-
current = current.parent;
|
|
106
|
-
}
|
|
107
|
-
return false;
|
|
108
|
-
case 'c':
|
|
109
|
-
case 'cpp':
|
|
110
|
-
return false;
|
|
111
|
-
case 'php':
|
|
112
|
-
// Top-level classes/interfaces/traits are always accessible
|
|
113
|
-
// Methods/properties are exported only if they have 'public' modifier
|
|
114
|
-
while (current) {
|
|
115
|
-
if (current.type === 'class_declaration' ||
|
|
116
|
-
current.type === 'interface_declaration' ||
|
|
117
|
-
current.type === 'trait_declaration' ||
|
|
118
|
-
current.type === 'enum_declaration') {
|
|
119
|
-
return true;
|
|
120
|
-
}
|
|
121
|
-
if (current.type === 'visibility_modifier') {
|
|
122
|
-
return current.text === 'public';
|
|
123
|
-
}
|
|
124
|
-
current = current.parent;
|
|
125
|
-
}
|
|
126
|
-
// Top-level functions (no parent class) are globally accessible
|
|
127
|
-
return true;
|
|
128
|
-
default:
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
};
|
|
81
|
+
// isNodeExported imported from ../export-detection.js (shared module)
|
|
132
82
|
// ============================================================================
|
|
133
83
|
// Enclosing function detection (for call extraction)
|
|
134
84
|
// ============================================================================
|
|
135
|
-
const FUNCTION_NODE_TYPES = new Set([
|
|
136
|
-
'function_declaration', 'arrow_function', 'function_expression',
|
|
137
|
-
'method_definition', 'generator_function_declaration',
|
|
138
|
-
'function_definition', 'async_function_declaration', 'async_arrow_function',
|
|
139
|
-
'method_declaration', 'constructor_declaration',
|
|
140
|
-
'local_function_statement', 'function_item', 'impl_item',
|
|
141
|
-
'anonymous_function_creation_expression', // PHP anonymous functions
|
|
142
|
-
]);
|
|
143
85
|
/** Walk up AST to find enclosing function, return its generateId or null for top-level */
|
|
144
86
|
const findEnclosingFunctionId = (node, filePath) => {
|
|
145
87
|
let current = node.parent;
|
|
146
88
|
while (current) {
|
|
147
89
|
if (FUNCTION_NODE_TYPES.has(current.type)) {
|
|
148
|
-
|
|
149
|
-
let label = 'Function';
|
|
150
|
-
if (['function_declaration', 'function_definition', 'async_function_declaration',
|
|
151
|
-
'generator_function_declaration', 'function_item'].includes(current.type)) {
|
|
152
|
-
const nameNode = current.childForFieldName?.('name') ||
|
|
153
|
-
current.children?.find((c) => c.type === 'identifier' || c.type === 'property_identifier');
|
|
154
|
-
funcName = nameNode?.text;
|
|
155
|
-
}
|
|
156
|
-
else if (current.type === 'impl_item') {
|
|
157
|
-
const funcItem = current.children?.find((c) => c.type === 'function_item');
|
|
158
|
-
if (funcItem) {
|
|
159
|
-
const nameNode = funcItem.childForFieldName?.('name') ||
|
|
160
|
-
funcItem.children?.find((c) => c.type === 'identifier');
|
|
161
|
-
funcName = nameNode?.text;
|
|
162
|
-
label = 'Method';
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
else if (current.type === 'method_definition') {
|
|
166
|
-
const nameNode = current.childForFieldName?.('name') ||
|
|
167
|
-
current.children?.find((c) => c.type === 'property_identifier');
|
|
168
|
-
funcName = nameNode?.text;
|
|
169
|
-
label = 'Method';
|
|
170
|
-
}
|
|
171
|
-
else if (current.type === 'method_declaration' || current.type === 'constructor_declaration') {
|
|
172
|
-
const nameNode = current.childForFieldName?.('name') ||
|
|
173
|
-
current.children?.find((c) => c.type === 'identifier');
|
|
174
|
-
funcName = nameNode?.text;
|
|
175
|
-
label = 'Method';
|
|
176
|
-
}
|
|
177
|
-
else if (current.type === 'arrow_function' || current.type === 'function_expression') {
|
|
178
|
-
const parent = current.parent;
|
|
179
|
-
if (parent?.type === 'variable_declarator') {
|
|
180
|
-
const nameNode = parent.childForFieldName?.('name') ||
|
|
181
|
-
parent.children?.find((c) => c.type === 'identifier');
|
|
182
|
-
funcName = nameNode?.text;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
90
|
+
const { funcName, label } = extractFunctionName(current);
|
|
185
91
|
if (funcName) {
|
|
186
92
|
return generateId(label, `${filePath}:${funcName}`);
|
|
187
93
|
}
|
|
@@ -190,70 +96,6 @@ const findEnclosingFunctionId = (node, filePath) => {
|
|
|
190
96
|
}
|
|
191
97
|
return null;
|
|
192
98
|
};
|
|
193
|
-
const BUILT_INS = new Set([
|
|
194
|
-
// JavaScript/TypeScript
|
|
195
|
-
'console', 'log', 'warn', 'error', 'info', 'debug',
|
|
196
|
-
'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval',
|
|
197
|
-
'parseInt', 'parseFloat', 'isNaN', 'isFinite',
|
|
198
|
-
'encodeURI', 'decodeURI', 'encodeURIComponent', 'decodeURIComponent',
|
|
199
|
-
'JSON', 'parse', 'stringify',
|
|
200
|
-
'Object', 'Array', 'String', 'Number', 'Boolean', 'Symbol', 'BigInt',
|
|
201
|
-
'Map', 'Set', 'WeakMap', 'WeakSet',
|
|
202
|
-
'Promise', 'resolve', 'reject', 'then', 'catch', 'finally',
|
|
203
|
-
'Math', 'Date', 'RegExp', 'Error',
|
|
204
|
-
'require', 'import', 'export', 'fetch', 'Response', 'Request',
|
|
205
|
-
'useState', 'useEffect', 'useCallback', 'useMemo', 'useRef', 'useContext',
|
|
206
|
-
'useReducer', 'useLayoutEffect', 'useImperativeHandle', 'useDebugValue',
|
|
207
|
-
'createElement', 'createContext', 'createRef', 'forwardRef', 'memo', 'lazy',
|
|
208
|
-
'map', 'filter', 'reduce', 'forEach', 'find', 'findIndex', 'some', 'every',
|
|
209
|
-
'includes', 'indexOf', 'slice', 'splice', 'concat', 'join', 'split',
|
|
210
|
-
'push', 'pop', 'shift', 'unshift', 'sort', 'reverse',
|
|
211
|
-
'keys', 'values', 'entries', 'assign', 'freeze', 'seal',
|
|
212
|
-
'hasOwnProperty', 'toString', 'valueOf',
|
|
213
|
-
// Python
|
|
214
|
-
'print', 'len', 'range', 'str', 'int', 'float', 'list', 'dict', 'set', 'tuple',
|
|
215
|
-
'open', 'read', 'write', 'close', 'append', 'extend', 'update',
|
|
216
|
-
'super', 'type', 'isinstance', 'issubclass', 'getattr', 'setattr', 'hasattr',
|
|
217
|
-
'enumerate', 'zip', 'sorted', 'reversed', 'min', 'max', 'sum', 'abs',
|
|
218
|
-
// C/C++ standard library
|
|
219
|
-
'printf', 'fprintf', 'sprintf', 'snprintf', 'vprintf', 'vfprintf', 'vsprintf', 'vsnprintf',
|
|
220
|
-
'scanf', 'fscanf', 'sscanf',
|
|
221
|
-
'malloc', 'calloc', 'realloc', 'free', 'memcpy', 'memmove', 'memset', 'memcmp',
|
|
222
|
-
'strlen', 'strcpy', 'strncpy', 'strcat', 'strncat', 'strcmp', 'strncmp', 'strstr', 'strchr', 'strrchr',
|
|
223
|
-
'atoi', 'atol', 'atof', 'strtol', 'strtoul', 'strtoll', 'strtoull', 'strtod',
|
|
224
|
-
'sizeof', 'offsetof', 'typeof',
|
|
225
|
-
'assert', 'abort', 'exit', '_exit',
|
|
226
|
-
'fopen', 'fclose', 'fread', 'fwrite', 'fseek', 'ftell', 'rewind', 'fflush', 'fgets', 'fputs',
|
|
227
|
-
// Linux kernel common macros/helpers (not real call targets)
|
|
228
|
-
'likely', 'unlikely', 'BUG', 'BUG_ON', 'WARN', 'WARN_ON', 'WARN_ONCE',
|
|
229
|
-
'IS_ERR', 'PTR_ERR', 'ERR_PTR', 'IS_ERR_OR_NULL',
|
|
230
|
-
'ARRAY_SIZE', 'container_of', 'list_for_each_entry', 'list_for_each_entry_safe',
|
|
231
|
-
'min', 'max', 'clamp', 'abs', 'swap',
|
|
232
|
-
'pr_info', 'pr_warn', 'pr_err', 'pr_debug', 'pr_notice', 'pr_crit', 'pr_emerg',
|
|
233
|
-
'printk', 'dev_info', 'dev_warn', 'dev_err', 'dev_dbg',
|
|
234
|
-
'GFP_KERNEL', 'GFP_ATOMIC',
|
|
235
|
-
'spin_lock', 'spin_unlock', 'spin_lock_irqsave', 'spin_unlock_irqrestore',
|
|
236
|
-
'mutex_lock', 'mutex_unlock', 'mutex_init',
|
|
237
|
-
'kfree', 'kmalloc', 'kzalloc', 'kcalloc', 'krealloc', 'kvmalloc', 'kvfree',
|
|
238
|
-
'get', 'put',
|
|
239
|
-
// PHP built-ins
|
|
240
|
-
'echo', 'isset', 'empty', 'unset', 'list', 'array', 'compact', 'extract',
|
|
241
|
-
'count', 'strlen', 'strpos', 'strrpos', 'substr', 'strtolower', 'strtoupper', 'trim',
|
|
242
|
-
'ltrim', 'rtrim', 'str_replace', 'str_contains', 'str_starts_with', 'str_ends_with',
|
|
243
|
-
'sprintf', 'vsprintf', 'printf', 'number_format',
|
|
244
|
-
'array_map', 'array_filter', 'array_reduce', 'array_push', 'array_pop', 'array_shift',
|
|
245
|
-
'array_unshift', 'array_slice', 'array_splice', 'array_merge', 'array_keys', 'array_values',
|
|
246
|
-
'array_key_exists', 'in_array', 'array_search', 'array_unique', 'usort', 'rsort',
|
|
247
|
-
'json_encode', 'json_decode', 'serialize', 'unserialize',
|
|
248
|
-
'intval', 'floatval', 'strval', 'boolval', 'is_null', 'is_string', 'is_int', 'is_array',
|
|
249
|
-
'is_object', 'is_numeric', 'is_bool', 'is_float',
|
|
250
|
-
'var_dump', 'print_r', 'var_export',
|
|
251
|
-
'date', 'time', 'strtotime', 'mktime', 'microtime',
|
|
252
|
-
'file_exists', 'file_get_contents', 'file_put_contents', 'is_file', 'is_dir',
|
|
253
|
-
'preg_match', 'preg_match_all', 'preg_replace', 'preg_split',
|
|
254
|
-
'header', 'session_start', 'session_destroy', 'ob_start', 'ob_end_clean', 'ob_get_clean',
|
|
255
|
-
'dd', 'dump',
|
|
256
|
-
]);
|
|
257
99
|
// ============================================================================
|
|
258
100
|
// Label detection from capture map
|
|
259
101
|
// ============================================================================
|
|
@@ -309,6 +151,7 @@ const getLabelFromCaptures = (captureMap) => {
|
|
|
309
151
|
return 'Template';
|
|
310
152
|
return 'CodeElement';
|
|
311
153
|
};
|
|
154
|
+
// DEFINITION_CAPTURE_KEYS and getDefinitionNodeFromCaptures imported from ../utils.js
|
|
312
155
|
// ============================================================================
|
|
313
156
|
// Process a batch of files
|
|
314
157
|
// ============================================================================
|
|
@@ -320,6 +163,9 @@ const processBatch = (files, onProgress) => {
|
|
|
320
163
|
imports: [],
|
|
321
164
|
calls: [],
|
|
322
165
|
heritage: [],
|
|
166
|
+
routes: [],
|
|
167
|
+
constructorBindings: [],
|
|
168
|
+
skippedLanguages: {},
|
|
323
169
|
fileCount: 0,
|
|
324
170
|
};
|
|
325
171
|
// Group by language to minimize setLanguage calls
|
|
@@ -367,13 +213,33 @@ const processBatch = (files, onProgress) => {
|
|
|
367
213
|
}
|
|
368
214
|
// Process regular files for this language
|
|
369
215
|
if (regularFiles.length > 0) {
|
|
370
|
-
|
|
371
|
-
|
|
216
|
+
if (isLanguageAvailable(language, regularFiles[0].path)) {
|
|
217
|
+
try {
|
|
218
|
+
setLanguage(language, regularFiles[0].path);
|
|
219
|
+
processFileGroup(regularFiles, language, queryString, result, onFileProcessed);
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
// parser unavailable — skip this language group
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
result.skippedLanguages[language] = (result.skippedLanguages[language] || 0) + regularFiles.length;
|
|
227
|
+
}
|
|
372
228
|
}
|
|
373
229
|
// Process tsx files separately (different grammar)
|
|
374
230
|
if (tsxFiles.length > 0) {
|
|
375
|
-
|
|
376
|
-
|
|
231
|
+
if (isLanguageAvailable(language, tsxFiles[0].path)) {
|
|
232
|
+
try {
|
|
233
|
+
setLanguage(language, tsxFiles[0].path);
|
|
234
|
+
processFileGroup(tsxFiles, language, queryString, result, onFileProcessed);
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
// parser unavailable — skip this language group
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
result.skippedLanguages[language] = (result.skippedLanguages[language] || 0) + tsxFiles.length;
|
|
242
|
+
}
|
|
377
243
|
}
|
|
378
244
|
}
|
|
379
245
|
return result;
|
|
@@ -482,33 +348,396 @@ function extractEloquentRelationDescription(methodNode) {
|
|
|
482
348
|
return relType;
|
|
483
349
|
return null;
|
|
484
350
|
}
|
|
351
|
+
const ROUTE_HTTP_METHODS = new Set([
|
|
352
|
+
'get', 'post', 'put', 'patch', 'delete', 'options', 'any', 'match',
|
|
353
|
+
]);
|
|
354
|
+
const ROUTE_RESOURCE_METHODS = new Set(['resource', 'apiResource']);
|
|
355
|
+
const RESOURCE_ACTIONS = ['index', 'create', 'store', 'show', 'edit', 'update', 'destroy'];
|
|
356
|
+
const API_RESOURCE_ACTIONS = ['index', 'store', 'show', 'update', 'destroy'];
|
|
357
|
+
/** Check if node is a scoped_call_expression with object 'Route' */
|
|
358
|
+
function isRouteStaticCall(node) {
|
|
359
|
+
if (node.type !== 'scoped_call_expression')
|
|
360
|
+
return false;
|
|
361
|
+
const obj = node.childForFieldName?.('object') ?? node.children?.[0];
|
|
362
|
+
return obj?.text === 'Route';
|
|
363
|
+
}
|
|
364
|
+
/** Get the method name from a scoped_call_expression or member_call_expression */
|
|
365
|
+
function getCallMethodName(node) {
|
|
366
|
+
const nameNode = node.childForFieldName?.('name') ??
|
|
367
|
+
node.children?.find((c) => c.type === 'name');
|
|
368
|
+
return nameNode?.text ?? null;
|
|
369
|
+
}
|
|
370
|
+
/** Get the arguments node from a call expression */
|
|
371
|
+
function getArguments(node) {
|
|
372
|
+
return node.children?.find((c) => c.type === 'arguments') ?? null;
|
|
373
|
+
}
|
|
374
|
+
/** Find the closure body inside arguments */
|
|
375
|
+
function findClosureBody(argsNode) {
|
|
376
|
+
if (!argsNode)
|
|
377
|
+
return null;
|
|
378
|
+
for (const child of argsNode.children ?? []) {
|
|
379
|
+
if (child.type === 'argument') {
|
|
380
|
+
for (const inner of child.children ?? []) {
|
|
381
|
+
if (inner.type === 'anonymous_function' ||
|
|
382
|
+
inner.type === 'arrow_function') {
|
|
383
|
+
return inner.childForFieldName?.('body') ??
|
|
384
|
+
inner.children?.find((c) => c.type === 'compound_statement');
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
if (child.type === 'anonymous_function' ||
|
|
389
|
+
child.type === 'arrow_function') {
|
|
390
|
+
return child.childForFieldName?.('body') ??
|
|
391
|
+
child.children?.find((c) => c.type === 'compound_statement');
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
/** Extract first string argument from arguments node */
|
|
397
|
+
function extractFirstStringArg(argsNode) {
|
|
398
|
+
if (!argsNode)
|
|
399
|
+
return null;
|
|
400
|
+
for (const child of argsNode.children ?? []) {
|
|
401
|
+
const target = child.type === 'argument' ? child.children?.[0] : child;
|
|
402
|
+
if (!target)
|
|
403
|
+
continue;
|
|
404
|
+
if (target.type === 'string' || target.type === 'encapsed_string') {
|
|
405
|
+
return extractStringContent(target);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
/** Extract middleware from arguments — handles string or array */
|
|
411
|
+
function extractMiddlewareArg(argsNode) {
|
|
412
|
+
if (!argsNode)
|
|
413
|
+
return [];
|
|
414
|
+
for (const child of argsNode.children ?? []) {
|
|
415
|
+
const target = child.type === 'argument' ? child.children?.[0] : child;
|
|
416
|
+
if (!target)
|
|
417
|
+
continue;
|
|
418
|
+
if (target.type === 'string' || target.type === 'encapsed_string') {
|
|
419
|
+
const val = extractStringContent(target);
|
|
420
|
+
return val ? [val] : [];
|
|
421
|
+
}
|
|
422
|
+
if (target.type === 'array_creation_expression') {
|
|
423
|
+
const items = [];
|
|
424
|
+
for (const el of target.children ?? []) {
|
|
425
|
+
if (el.type === 'array_element_initializer') {
|
|
426
|
+
const str = el.children?.find((c) => c.type === 'string' || c.type === 'encapsed_string');
|
|
427
|
+
const val = str ? extractStringContent(str) : null;
|
|
428
|
+
if (val)
|
|
429
|
+
items.push(val);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return items;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return [];
|
|
436
|
+
}
|
|
437
|
+
/** Extract Controller::class from arguments */
|
|
438
|
+
function extractClassArg(argsNode) {
|
|
439
|
+
if (!argsNode)
|
|
440
|
+
return null;
|
|
441
|
+
for (const child of argsNode.children ?? []) {
|
|
442
|
+
const target = child.type === 'argument' ? child.children?.[0] : child;
|
|
443
|
+
if (target?.type === 'class_constant_access_expression') {
|
|
444
|
+
return target.children?.find((c) => c.type === 'name')?.text ?? null;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
/** Extract controller class name from arguments: [Controller::class, 'method'] or 'Controller@method' */
|
|
450
|
+
function extractControllerTarget(argsNode) {
|
|
451
|
+
if (!argsNode)
|
|
452
|
+
return { controller: null, method: null };
|
|
453
|
+
const args = [];
|
|
454
|
+
for (const child of argsNode.children ?? []) {
|
|
455
|
+
if (child.type === 'argument')
|
|
456
|
+
args.push(child.children?.[0]);
|
|
457
|
+
else if (child.type !== '(' && child.type !== ')' && child.type !== ',')
|
|
458
|
+
args.push(child);
|
|
459
|
+
}
|
|
460
|
+
// Second arg is the handler
|
|
461
|
+
const handlerNode = args[1];
|
|
462
|
+
if (!handlerNode)
|
|
463
|
+
return { controller: null, method: null };
|
|
464
|
+
// Array syntax: [UserController::class, 'index']
|
|
465
|
+
if (handlerNode.type === 'array_creation_expression') {
|
|
466
|
+
let controller = null;
|
|
467
|
+
let method = null;
|
|
468
|
+
const elements = [];
|
|
469
|
+
for (const el of handlerNode.children ?? []) {
|
|
470
|
+
if (el.type === 'array_element_initializer')
|
|
471
|
+
elements.push(el);
|
|
472
|
+
}
|
|
473
|
+
if (elements[0]) {
|
|
474
|
+
const classAccess = findDescendant(elements[0], 'class_constant_access_expression');
|
|
475
|
+
if (classAccess) {
|
|
476
|
+
controller = classAccess.children?.find((c) => c.type === 'name')?.text ?? null;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
if (elements[1]) {
|
|
480
|
+
const str = findDescendant(elements[1], 'string');
|
|
481
|
+
method = str ? extractStringContent(str) : null;
|
|
482
|
+
}
|
|
483
|
+
return { controller, method };
|
|
484
|
+
}
|
|
485
|
+
// String syntax: 'UserController@index'
|
|
486
|
+
if (handlerNode.type === 'string' || handlerNode.type === 'encapsed_string') {
|
|
487
|
+
const text = extractStringContent(handlerNode);
|
|
488
|
+
if (text?.includes('@')) {
|
|
489
|
+
const [controller, method] = text.split('@');
|
|
490
|
+
return { controller, method };
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
// Class reference: UserController::class (invokable controller)
|
|
494
|
+
if (handlerNode.type === 'class_constant_access_expression') {
|
|
495
|
+
const controller = handlerNode.children?.find((c) => c.type === 'name')?.text ?? null;
|
|
496
|
+
return { controller, method: '__invoke' };
|
|
497
|
+
}
|
|
498
|
+
return { controller: null, method: null };
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Unwrap a chained call like Route::middleware('auth')->prefix('api')->group(fn)
|
|
502
|
+
*/
|
|
503
|
+
function unwrapRouteChain(node) {
|
|
504
|
+
if (node.type !== 'member_call_expression')
|
|
505
|
+
return null;
|
|
506
|
+
const terminalMethod = getCallMethodName(node);
|
|
507
|
+
if (!terminalMethod)
|
|
508
|
+
return null;
|
|
509
|
+
const terminalArgs = getArguments(node);
|
|
510
|
+
const attributes = [];
|
|
511
|
+
let current = node.children?.[0];
|
|
512
|
+
while (current) {
|
|
513
|
+
if (current.type === 'member_call_expression') {
|
|
514
|
+
const method = getCallMethodName(current);
|
|
515
|
+
const args = getArguments(current);
|
|
516
|
+
if (method)
|
|
517
|
+
attributes.unshift({ method, argsNode: args });
|
|
518
|
+
current = current.children?.[0];
|
|
519
|
+
}
|
|
520
|
+
else if (current.type === 'scoped_call_expression') {
|
|
521
|
+
const obj = current.childForFieldName?.('object') ?? current.children?.[0];
|
|
522
|
+
if (obj?.text !== 'Route')
|
|
523
|
+
return null;
|
|
524
|
+
const method = getCallMethodName(current);
|
|
525
|
+
const args = getArguments(current);
|
|
526
|
+
if (method)
|
|
527
|
+
attributes.unshift({ method, argsNode: args });
|
|
528
|
+
return { isRouteFacade: true, terminalMethod, attributes, terminalArgs, node };
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
break;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
return null;
|
|
535
|
+
}
|
|
536
|
+
/** Parse Route::group(['middleware' => ..., 'prefix' => ...], fn) array syntax */
|
|
537
|
+
function parseArrayGroupArgs(argsNode) {
|
|
538
|
+
const ctx = { middleware: [], prefix: null, controller: null };
|
|
539
|
+
if (!argsNode)
|
|
540
|
+
return ctx;
|
|
541
|
+
for (const child of argsNode.children ?? []) {
|
|
542
|
+
const target = child.type === 'argument' ? child.children?.[0] : child;
|
|
543
|
+
if (target?.type === 'array_creation_expression') {
|
|
544
|
+
for (const el of target.children ?? []) {
|
|
545
|
+
if (el.type !== 'array_element_initializer')
|
|
546
|
+
continue;
|
|
547
|
+
const children = el.children ?? [];
|
|
548
|
+
const arrowIdx = children.findIndex((c) => c.type === '=>');
|
|
549
|
+
if (arrowIdx === -1)
|
|
550
|
+
continue;
|
|
551
|
+
const key = extractStringContent(children[arrowIdx - 1]);
|
|
552
|
+
const val = children[arrowIdx + 1];
|
|
553
|
+
if (key === 'middleware') {
|
|
554
|
+
if (val?.type === 'string') {
|
|
555
|
+
const s = extractStringContent(val);
|
|
556
|
+
if (s)
|
|
557
|
+
ctx.middleware.push(s);
|
|
558
|
+
}
|
|
559
|
+
else if (val?.type === 'array_creation_expression') {
|
|
560
|
+
for (const item of val.children ?? []) {
|
|
561
|
+
if (item.type === 'array_element_initializer') {
|
|
562
|
+
const str = item.children?.find((c) => c.type === 'string');
|
|
563
|
+
const s = str ? extractStringContent(str) : null;
|
|
564
|
+
if (s)
|
|
565
|
+
ctx.middleware.push(s);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
else if (key === 'prefix') {
|
|
571
|
+
ctx.prefix = extractStringContent(val) ?? null;
|
|
572
|
+
}
|
|
573
|
+
else if (key === 'controller') {
|
|
574
|
+
if (val?.type === 'class_constant_access_expression') {
|
|
575
|
+
ctx.controller = val.children?.find((c) => c.type === 'name')?.text ?? null;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return ctx;
|
|
582
|
+
}
|
|
583
|
+
function extractLaravelRoutes(tree, filePath) {
|
|
584
|
+
const routes = [];
|
|
585
|
+
function resolveStack(stack) {
|
|
586
|
+
const middleware = [];
|
|
587
|
+
let prefix = null;
|
|
588
|
+
let controller = null;
|
|
589
|
+
for (const ctx of stack) {
|
|
590
|
+
middleware.push(...ctx.middleware);
|
|
591
|
+
if (ctx.prefix)
|
|
592
|
+
prefix = prefix ? `${prefix}/${ctx.prefix}`.replace(/\/+/g, '/') : ctx.prefix;
|
|
593
|
+
if (ctx.controller)
|
|
594
|
+
controller = ctx.controller;
|
|
595
|
+
}
|
|
596
|
+
return { middleware, prefix, controller };
|
|
597
|
+
}
|
|
598
|
+
function emitRoute(httpMethod, argsNode, lineNumber, groupStack, chainAttrs) {
|
|
599
|
+
const effective = resolveStack(groupStack);
|
|
600
|
+
for (const attr of chainAttrs) {
|
|
601
|
+
if (attr.method === 'middleware')
|
|
602
|
+
effective.middleware.push(...extractMiddlewareArg(attr.argsNode));
|
|
603
|
+
if (attr.method === 'prefix') {
|
|
604
|
+
const p = extractFirstStringArg(attr.argsNode);
|
|
605
|
+
if (p)
|
|
606
|
+
effective.prefix = effective.prefix ? `${effective.prefix}/${p}` : p;
|
|
607
|
+
}
|
|
608
|
+
if (attr.method === 'controller') {
|
|
609
|
+
const cls = extractClassArg(attr.argsNode);
|
|
610
|
+
if (cls)
|
|
611
|
+
effective.controller = cls;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
const routePath = extractFirstStringArg(argsNode);
|
|
615
|
+
if (ROUTE_RESOURCE_METHODS.has(httpMethod)) {
|
|
616
|
+
const target = extractControllerTarget(argsNode);
|
|
617
|
+
const actions = httpMethod === 'apiResource' ? API_RESOURCE_ACTIONS : RESOURCE_ACTIONS;
|
|
618
|
+
for (const action of actions) {
|
|
619
|
+
routes.push({
|
|
620
|
+
filePath, httpMethod, routePath,
|
|
621
|
+
controllerName: target.controller ?? effective.controller,
|
|
622
|
+
methodName: action,
|
|
623
|
+
middleware: [...effective.middleware],
|
|
624
|
+
prefix: effective.prefix,
|
|
625
|
+
lineNumber,
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
const target = extractControllerTarget(argsNode);
|
|
631
|
+
routes.push({
|
|
632
|
+
filePath, httpMethod, routePath,
|
|
633
|
+
controllerName: target.controller ?? effective.controller,
|
|
634
|
+
methodName: target.method,
|
|
635
|
+
middleware: [...effective.middleware],
|
|
636
|
+
prefix: effective.prefix,
|
|
637
|
+
lineNumber,
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
function walk(node, groupStack) {
|
|
642
|
+
// Case 1: Simple Route::get(...), Route::post(...), etc.
|
|
643
|
+
if (isRouteStaticCall(node)) {
|
|
644
|
+
const method = getCallMethodName(node);
|
|
645
|
+
if (method && (ROUTE_HTTP_METHODS.has(method) || ROUTE_RESOURCE_METHODS.has(method))) {
|
|
646
|
+
emitRoute(method, getArguments(node), node.startPosition.row, groupStack, []);
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
if (method === 'group') {
|
|
650
|
+
const argsNode = getArguments(node);
|
|
651
|
+
const groupCtx = parseArrayGroupArgs(argsNode);
|
|
652
|
+
const body = findClosureBody(argsNode);
|
|
653
|
+
if (body) {
|
|
654
|
+
groupStack.push(groupCtx);
|
|
655
|
+
walkChildren(body, groupStack);
|
|
656
|
+
groupStack.pop();
|
|
657
|
+
}
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
// Case 2: Fluent chain — Route::middleware(...)->group(...) or Route::middleware(...)->get(...)
|
|
662
|
+
const chain = unwrapRouteChain(node);
|
|
663
|
+
if (chain) {
|
|
664
|
+
if (chain.terminalMethod === 'group') {
|
|
665
|
+
const groupCtx = { middleware: [], prefix: null, controller: null };
|
|
666
|
+
for (const attr of chain.attributes) {
|
|
667
|
+
if (attr.method === 'middleware')
|
|
668
|
+
groupCtx.middleware.push(...extractMiddlewareArg(attr.argsNode));
|
|
669
|
+
if (attr.method === 'prefix')
|
|
670
|
+
groupCtx.prefix = extractFirstStringArg(attr.argsNode);
|
|
671
|
+
if (attr.method === 'controller')
|
|
672
|
+
groupCtx.controller = extractClassArg(attr.argsNode);
|
|
673
|
+
}
|
|
674
|
+
const body = findClosureBody(chain.terminalArgs);
|
|
675
|
+
if (body) {
|
|
676
|
+
groupStack.push(groupCtx);
|
|
677
|
+
walkChildren(body, groupStack);
|
|
678
|
+
groupStack.pop();
|
|
679
|
+
}
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
if (ROUTE_HTTP_METHODS.has(chain.terminalMethod) || ROUTE_RESOURCE_METHODS.has(chain.terminalMethod)) {
|
|
683
|
+
emitRoute(chain.terminalMethod, chain.terminalArgs, node.startPosition.row, groupStack, chain.attributes);
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
// Default: recurse into children
|
|
688
|
+
walkChildren(node, groupStack);
|
|
689
|
+
}
|
|
690
|
+
function walkChildren(node, groupStack) {
|
|
691
|
+
for (const child of node.children ?? []) {
|
|
692
|
+
walk(child, groupStack);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
walk(tree.rootNode, []);
|
|
696
|
+
return routes;
|
|
697
|
+
}
|
|
485
698
|
const processFileGroup = (files, language, queryString, result, onFileProcessed) => {
|
|
486
699
|
let query;
|
|
487
700
|
try {
|
|
488
701
|
const lang = parser.getLanguage();
|
|
489
702
|
query = new Parser.Query(lang, queryString);
|
|
490
703
|
}
|
|
491
|
-
catch {
|
|
704
|
+
catch (err) {
|
|
705
|
+
const message = `Query compilation failed for ${language}: ${err instanceof Error ? err.message : String(err)}`;
|
|
706
|
+
if (parentPort) {
|
|
707
|
+
parentPort.postMessage({ type: 'warning', message });
|
|
708
|
+
}
|
|
709
|
+
else {
|
|
710
|
+
console.warn(message);
|
|
711
|
+
}
|
|
492
712
|
return;
|
|
493
713
|
}
|
|
494
714
|
for (const file of files) {
|
|
495
|
-
// Skip
|
|
496
|
-
if (file.content.length >
|
|
715
|
+
// Skip files larger than the max tree-sitter buffer (32 MB)
|
|
716
|
+
if (file.content.length > TREE_SITTER_MAX_BUFFER)
|
|
497
717
|
continue;
|
|
498
718
|
let tree;
|
|
499
719
|
try {
|
|
500
|
-
tree = parser.parse(file.content, undefined, { bufferSize:
|
|
720
|
+
tree = parser.parse(file.content, undefined, { bufferSize: getTreeSitterBufferSize(file.content.length) });
|
|
501
721
|
}
|
|
502
|
-
catch {
|
|
722
|
+
catch (err) {
|
|
723
|
+
console.warn(`Failed to parse file ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
503
724
|
continue;
|
|
504
725
|
}
|
|
505
726
|
result.fileCount++;
|
|
506
727
|
onFileProcessed?.();
|
|
728
|
+
// Build per-file type environment + constructor bindings in a single AST walk.
|
|
729
|
+
// Constructor bindings are verified against the SymbolTable in processCallsFromExtracted.
|
|
730
|
+
const typeEnv = buildTypeEnv(tree, language);
|
|
731
|
+
const callRouter = callRouters[language];
|
|
732
|
+
if (typeEnv.constructorBindings.length > 0) {
|
|
733
|
+
result.constructorBindings.push({ filePath: file.path, bindings: [...typeEnv.constructorBindings] });
|
|
734
|
+
}
|
|
507
735
|
let matches;
|
|
508
736
|
try {
|
|
509
737
|
matches = query.matches(tree.rootNode);
|
|
510
738
|
}
|
|
511
|
-
catch {
|
|
739
|
+
catch (err) {
|
|
740
|
+
console.warn(`Query execution failed for ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
512
741
|
continue;
|
|
513
742
|
}
|
|
514
743
|
for (const match of matches) {
|
|
@@ -518,11 +747,15 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
518
747
|
}
|
|
519
748
|
// Extract import paths before skipping
|
|
520
749
|
if (captureMap['import'] && captureMap['import.source']) {
|
|
521
|
-
const rawImportPath =
|
|
750
|
+
const rawImportPath = language === SupportedLanguages.Kotlin
|
|
751
|
+
? appendKotlinWildcard(captureMap['import.source'].text.replace(/['"<>]/g, ''), captureMap['import'])
|
|
752
|
+
: captureMap['import.source'].text.replace(/['"<>]/g, '');
|
|
753
|
+
const namedBindings = extractNamedBindings(captureMap['import'], language);
|
|
522
754
|
result.imports.push({
|
|
523
755
|
filePath: file.path,
|
|
524
756
|
rawImportPath,
|
|
525
757
|
language: language,
|
|
758
|
+
...(namedBindings ? { namedBindings } : {}),
|
|
526
759
|
});
|
|
527
760
|
continue;
|
|
528
761
|
}
|
|
@@ -531,11 +764,122 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
531
764
|
const callNameNode = captureMap['call.name'];
|
|
532
765
|
if (callNameNode) {
|
|
533
766
|
const calledName = callNameNode.text;
|
|
534
|
-
|
|
767
|
+
// Dispatch: route language-specific calls (heritage, properties, imports)
|
|
768
|
+
const routed = callRouter(calledName, captureMap['call']);
|
|
769
|
+
if (routed) {
|
|
770
|
+
if (routed.kind === 'skip')
|
|
771
|
+
continue;
|
|
772
|
+
if (routed.kind === 'import') {
|
|
773
|
+
result.imports.push({
|
|
774
|
+
filePath: file.path,
|
|
775
|
+
rawImportPath: routed.importPath,
|
|
776
|
+
language,
|
|
777
|
+
});
|
|
778
|
+
continue;
|
|
779
|
+
}
|
|
780
|
+
if (routed.kind === 'heritage') {
|
|
781
|
+
for (const item of routed.items) {
|
|
782
|
+
result.heritage.push({
|
|
783
|
+
filePath: file.path,
|
|
784
|
+
className: item.enclosingClass,
|
|
785
|
+
parentName: item.mixinName,
|
|
786
|
+
kind: item.heritageKind,
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
if (routed.kind === 'properties') {
|
|
792
|
+
const propEnclosingClassId = findEnclosingClassId(captureMap['call'], file.path);
|
|
793
|
+
for (const item of routed.items) {
|
|
794
|
+
const nodeId = generateId('Property', `${file.path}:${item.propName}`);
|
|
795
|
+
result.nodes.push({
|
|
796
|
+
id: nodeId,
|
|
797
|
+
label: 'Property',
|
|
798
|
+
properties: {
|
|
799
|
+
name: item.propName,
|
|
800
|
+
filePath: file.path,
|
|
801
|
+
startLine: item.startLine,
|
|
802
|
+
endLine: item.endLine,
|
|
803
|
+
language,
|
|
804
|
+
isExported: true,
|
|
805
|
+
description: item.accessorType,
|
|
806
|
+
},
|
|
807
|
+
});
|
|
808
|
+
result.symbols.push({
|
|
809
|
+
filePath: file.path,
|
|
810
|
+
name: item.propName,
|
|
811
|
+
nodeId,
|
|
812
|
+
type: 'Property',
|
|
813
|
+
...(propEnclosingClassId ? { ownerId: propEnclosingClassId } : {}),
|
|
814
|
+
});
|
|
815
|
+
const fileId = generateId('File', file.path);
|
|
816
|
+
const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
|
|
817
|
+
result.relationships.push({
|
|
818
|
+
id: relId,
|
|
819
|
+
sourceId: fileId,
|
|
820
|
+
targetId: nodeId,
|
|
821
|
+
type: 'DEFINES',
|
|
822
|
+
confidence: 1.0,
|
|
823
|
+
reason: '',
|
|
824
|
+
});
|
|
825
|
+
if (propEnclosingClassId) {
|
|
826
|
+
result.relationships.push({
|
|
827
|
+
id: generateId('HAS_METHOD', `${propEnclosingClassId}->${nodeId}`),
|
|
828
|
+
sourceId: propEnclosingClassId,
|
|
829
|
+
targetId: nodeId,
|
|
830
|
+
type: 'HAS_METHOD',
|
|
831
|
+
confidence: 1.0,
|
|
832
|
+
reason: '',
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
continue;
|
|
837
|
+
}
|
|
838
|
+
// kind === 'call' — fall through to normal call processing below
|
|
839
|
+
}
|
|
840
|
+
if (!isBuiltInOrNoise(calledName)) {
|
|
535
841
|
const callNode = captureMap['call'];
|
|
536
842
|
const sourceId = findEnclosingFunctionId(callNode, file.path)
|
|
537
843
|
|| generateId('File', file.path);
|
|
538
|
-
|
|
844
|
+
const callForm = inferCallForm(callNode, callNameNode);
|
|
845
|
+
let receiverName = callForm === 'member' ? extractReceiverName(callNameNode) : undefined;
|
|
846
|
+
let receiverTypeName = receiverName ? typeEnv.lookup(receiverName, callNode) : undefined;
|
|
847
|
+
let receiverCallChain;
|
|
848
|
+
// When the receiver is a call_expression (e.g. svc.getUser().save()),
|
|
849
|
+
// extractReceiverName returns undefined because it refuses complex expressions.
|
|
850
|
+
// Instead, walk the receiver node to build a call chain for deferred resolution.
|
|
851
|
+
// We capture the base receiver name so processCallsFromExtracted can look it up
|
|
852
|
+
// from constructor bindings. receiverTypeName is intentionally left unset here —
|
|
853
|
+
// the chain resolver in processCallsFromExtracted needs the base type as input and
|
|
854
|
+
// produces the final receiver type as output.
|
|
855
|
+
if (callForm === 'member' && receiverName === undefined && !receiverTypeName) {
|
|
856
|
+
const receiverNode = extractReceiverNode(callNameNode);
|
|
857
|
+
if (receiverNode && CALL_EXPRESSION_TYPES.has(receiverNode.type)) {
|
|
858
|
+
const extracted = extractCallChain(receiverNode);
|
|
859
|
+
if (extracted) {
|
|
860
|
+
receiverCallChain = extracted.chain;
|
|
861
|
+
// Set receiverName to the base object so Step 1 in processCallsFromExtracted
|
|
862
|
+
// can resolve it via constructor bindings to a base type for the chain.
|
|
863
|
+
receiverName = extracted.baseReceiverName;
|
|
864
|
+
// Also try the type environment immediately (covers explicitly-typed locals
|
|
865
|
+
// and annotated parameters like `fn process(svc: &UserService)`).
|
|
866
|
+
// This sets a base type that chain resolution (Step 2) will use as input.
|
|
867
|
+
if (receiverName) {
|
|
868
|
+
receiverTypeName = typeEnv.lookup(receiverName, callNode);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
result.calls.push({
|
|
874
|
+
filePath: file.path,
|
|
875
|
+
calledName,
|
|
876
|
+
sourceId,
|
|
877
|
+
argCount: countCallArguments(callNode),
|
|
878
|
+
...(callForm !== undefined ? { callForm } : {}),
|
|
879
|
+
...(receiverName !== undefined ? { receiverName } : {}),
|
|
880
|
+
...(receiverTypeName !== undefined ? { receiverTypeName } : {}),
|
|
881
|
+
...(receiverCallChain !== undefined ? { receiverCallChain } : {}),
|
|
882
|
+
});
|
|
539
883
|
}
|
|
540
884
|
}
|
|
541
885
|
continue;
|
|
@@ -543,12 +887,21 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
543
887
|
// Extract heritage (extends/implements)
|
|
544
888
|
if (captureMap['heritage.class']) {
|
|
545
889
|
if (captureMap['heritage.extends']) {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
890
|
+
// Go struct embedding: the query matches ALL field_declarations with
|
|
891
|
+
// type_identifier, but only anonymous fields (no name) are embedded.
|
|
892
|
+
// Named fields like `Breed string` also match — skip them.
|
|
893
|
+
const extendsNode = captureMap['heritage.extends'];
|
|
894
|
+
const fieldDecl = extendsNode.parent;
|
|
895
|
+
const isNamedField = fieldDecl?.type === 'field_declaration'
|
|
896
|
+
&& fieldDecl.childForFieldName('name');
|
|
897
|
+
if (!isNamedField) {
|
|
898
|
+
result.heritage.push({
|
|
899
|
+
filePath: file.path,
|
|
900
|
+
className: captureMap['heritage.class'].text,
|
|
901
|
+
parentName: captureMap['heritage.extends'].text,
|
|
902
|
+
kind: 'extends',
|
|
903
|
+
});
|
|
904
|
+
}
|
|
552
905
|
}
|
|
553
906
|
if (captureMap['heritage.implements']) {
|
|
554
907
|
result.heritage.push({
|
|
@@ -574,7 +927,12 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
574
927
|
if (!nodeLabel)
|
|
575
928
|
continue;
|
|
576
929
|
const nameNode = captureMap['name'];
|
|
577
|
-
|
|
930
|
+
// Synthesize name for constructors without explicit @name capture (e.g. Swift init)
|
|
931
|
+
if (!nameNode && nodeLabel !== 'Constructor')
|
|
932
|
+
continue;
|
|
933
|
+
const nodeName = nameNode ? nameNode.text : 'init';
|
|
934
|
+
const definitionNode = getDefinitionNodeFromCaptures(captureMap);
|
|
935
|
+
const startLine = definitionNode ? definitionNode.startPosition.row : (nameNode ? nameNode.startPosition.row : 0);
|
|
578
936
|
const nodeId = generateId(nodeLabel, `${file.path}:${nodeName}`);
|
|
579
937
|
let description;
|
|
580
938
|
if (language === SupportedLanguages.PHP) {
|
|
@@ -585,6 +943,26 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
585
943
|
description = extractEloquentRelationDescription(captureMap['definition.method']) ?? undefined;
|
|
586
944
|
}
|
|
587
945
|
}
|
|
946
|
+
const frameworkHint = definitionNode
|
|
947
|
+
? detectFrameworkFromAST(language, (definitionNode.text || '').slice(0, 300))
|
|
948
|
+
: null;
|
|
949
|
+
let parameterCount;
|
|
950
|
+
let returnType;
|
|
951
|
+
if (nodeLabel === 'Function' || nodeLabel === 'Method' || nodeLabel === 'Constructor') {
|
|
952
|
+
const sig = extractMethodSignature(definitionNode);
|
|
953
|
+
parameterCount = sig.parameterCount;
|
|
954
|
+
returnType = sig.returnType;
|
|
955
|
+
// Language-specific return type fallback (e.g. Ruby YARD @return [Type])
|
|
956
|
+
// Also upgrades uninformative AST types like PHP `array` with PHPDoc `@return User[]`
|
|
957
|
+
if ((!returnType || returnType === 'array' || returnType === 'iterable') && definitionNode) {
|
|
958
|
+
const tc = typeConfigs[language];
|
|
959
|
+
if (tc?.extractReturnType) {
|
|
960
|
+
const docReturn = tc.extractReturnType(definitionNode);
|
|
961
|
+
if (docReturn)
|
|
962
|
+
returnType = docReturn;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
588
966
|
result.nodes.push({
|
|
589
967
|
id: nodeId,
|
|
590
968
|
label: nodeLabel,
|
|
@@ -594,15 +972,28 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
594
972
|
startLine: nameNode.startPosition.row + 1,
|
|
595
973
|
endLine: nameNode.endPosition.row + 1,
|
|
596
974
|
language: language,
|
|
597
|
-
isExported: isNodeExported(nameNode, nodeName, language),
|
|
975
|
+
isExported: isNodeExported(nameNode || definitionNode, nodeName, language),
|
|
976
|
+
...(frameworkHint ? {
|
|
977
|
+
astFrameworkMultiplier: frameworkHint.entryPointMultiplier,
|
|
978
|
+
astFrameworkReason: frameworkHint.reason,
|
|
979
|
+
} : {}),
|
|
598
980
|
...(description !== undefined ? { description } : {}),
|
|
981
|
+
...(parameterCount !== undefined ? { parameterCount } : {}),
|
|
982
|
+
...(returnType !== undefined ? { returnType } : {}),
|
|
599
983
|
},
|
|
600
984
|
});
|
|
985
|
+
// Compute enclosing class for Method/Constructor/Property/Function — used for both ownerId and HAS_METHOD
|
|
986
|
+
// Function is included because Kotlin/Rust/Python capture class methods as Function nodes
|
|
987
|
+
const needsOwner = nodeLabel === 'Method' || nodeLabel === 'Constructor' || nodeLabel === 'Property' || nodeLabel === 'Function';
|
|
988
|
+
const enclosingClassId = needsOwner ? findEnclosingClassId(nameNode || definitionNode, file.path) : null;
|
|
601
989
|
result.symbols.push({
|
|
602
990
|
filePath: file.path,
|
|
603
991
|
name: nodeName,
|
|
604
992
|
nodeId,
|
|
605
993
|
type: nodeLabel,
|
|
994
|
+
...(parameterCount !== undefined ? { parameterCount } : {}),
|
|
995
|
+
...(returnType !== undefined ? { returnType } : {}),
|
|
996
|
+
...(enclosingClassId ? { ownerId: enclosingClassId } : {}),
|
|
606
997
|
});
|
|
607
998
|
const fileId = generateId('File', file.path);
|
|
608
999
|
const relId = generateId('DEFINES', `${fileId}->${nodeId}`);
|
|
@@ -614,6 +1005,22 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
614
1005
|
confidence: 1.0,
|
|
615
1006
|
reason: '',
|
|
616
1007
|
});
|
|
1008
|
+
// ── HAS_METHOD: link method/constructor/property to enclosing class ──
|
|
1009
|
+
if (enclosingClassId) {
|
|
1010
|
+
result.relationships.push({
|
|
1011
|
+
id: generateId('HAS_METHOD', `${enclosingClassId}->${nodeId}`),
|
|
1012
|
+
sourceId: enclosingClassId,
|
|
1013
|
+
targetId: nodeId,
|
|
1014
|
+
type: 'HAS_METHOD',
|
|
1015
|
+
confidence: 1.0,
|
|
1016
|
+
reason: '',
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
// Extract Laravel routes from route files via procedural AST walk
|
|
1021
|
+
if (language === SupportedLanguages.PHP && (file.path.includes('/routes/') || file.path.startsWith('routes/')) && file.path.endsWith('.php')) {
|
|
1022
|
+
const extractedRoutes = extractLaravelRoutes(tree, file.path);
|
|
1023
|
+
result.routes.push(...extractedRoutes);
|
|
617
1024
|
}
|
|
618
1025
|
}
|
|
619
1026
|
};
|
|
@@ -623,7 +1030,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
623
1030
|
/** Accumulated result across sub-batches */
|
|
624
1031
|
let accumulated = {
|
|
625
1032
|
nodes: [], relationships: [], symbols: [],
|
|
626
|
-
imports: [], calls: [], heritage: [], fileCount: 0,
|
|
1033
|
+
imports: [], calls: [], heritage: [], routes: [], constructorBindings: [], skippedLanguages: {}, fileCount: 0,
|
|
627
1034
|
};
|
|
628
1035
|
let cumulativeProcessed = 0;
|
|
629
1036
|
const mergeResult = (target, src) => {
|
|
@@ -633,6 +1040,11 @@ const mergeResult = (target, src) => {
|
|
|
633
1040
|
target.imports.push(...src.imports);
|
|
634
1041
|
target.calls.push(...src.calls);
|
|
635
1042
|
target.heritage.push(...src.heritage);
|
|
1043
|
+
target.routes.push(...src.routes);
|
|
1044
|
+
target.constructorBindings.push(...src.constructorBindings);
|
|
1045
|
+
for (const [lang, count] of Object.entries(src.skippedLanguages)) {
|
|
1046
|
+
target.skippedLanguages[lang] = (target.skippedLanguages[lang] || 0) + count;
|
|
1047
|
+
}
|
|
636
1048
|
target.fileCount += src.fileCount;
|
|
637
1049
|
};
|
|
638
1050
|
parentPort.on('message', (msg) => {
|
|
@@ -652,7 +1064,7 @@ parentPort.on('message', (msg) => {
|
|
|
652
1064
|
if (msg && msg.type === 'flush') {
|
|
653
1065
|
parentPort.postMessage({ type: 'result', data: accumulated });
|
|
654
1066
|
// Reset for potential reuse
|
|
655
|
-
accumulated = { nodes: [], relationships: [], symbols: [], imports: [], calls: [], heritage: [], fileCount: 0 };
|
|
1067
|
+
accumulated = { nodes: [], relationships: [], symbols: [], imports: [], calls: [], heritage: [], routes: [], constructorBindings: [], skippedLanguages: {}, fileCount: 0 };
|
|
656
1068
|
cumulativeProcessed = 0;
|
|
657
1069
|
return;
|
|
658
1070
|
}
|