@opentrace/components 0.1.1-rc.47 → 0.1.1-rc.62
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 +306 -0
- package/bin/copy-wasm.js +125 -0
- package/dist/opentrace-components.cjs +354 -246
- package/dist/opentrace-components.cjs.map +1 -1
- package/dist/opentrace-components.js +355 -247
- package/dist/opentrace-components.js.map +1 -1
- package/dist/pipeline-wasm.cjs +58 -0
- package/dist/pipeline-wasm.cjs.map +1 -0
- package/dist/pipeline-wasm.js +57 -0
- package/dist/pipeline-wasm.js.map +1 -0
- package/dist/pipeline.cjs +3481 -0
- package/dist/pipeline.cjs.map +1 -0
- package/dist/pipeline.js +3481 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/src/GraphCanvas.d.ts +5 -0
- package/dist/src/GraphCanvas.d.ts.map +1 -1
- package/dist/src/colors/communityColors.d.ts.map +1 -1
- package/dist/src/config/graphLayout.d.ts +9 -9
- package/dist/src/config/graphLayout.d.ts.map +1 -1
- package/dist/src/graph/LayoutPipeline.d.ts +4 -6
- package/dist/src/graph/LayoutPipeline.d.ts.map +1 -1
- package/dist/src/graph/spacingWorker.d.ts +4 -0
- package/dist/src/graph/spacingWorker.d.ts.map +1 -1
- package/dist/src/graph/useGraphFilters.d.ts.map +1 -1
- package/dist/src/graph/useGraphInstance.d.ts.map +1 -1
- package/dist/src/graph/useGraphVisuals.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/pipeline/__tests__/cross-repo.test.d.ts +2 -0
- package/dist/src/pipeline/__tests__/cross-repo.test.d.ts.map +1 -0
- package/dist/src/pipeline/__tests__/fixture.test.d.ts +2 -0
- package/dist/src/pipeline/__tests__/fixture.test.d.ts.map +1 -0
- package/dist/src/pipeline/__tests__/github.test.d.ts +2 -0
- package/dist/src/pipeline/__tests__/github.test.d.ts.map +1 -0
- package/dist/src/pipeline/__tests__/helpers.d.ts +16 -0
- package/dist/src/pipeline/__tests__/helpers.d.ts.map +1 -0
- package/dist/src/pipeline/__tests__/loading.test.d.ts +2 -0
- package/dist/src/pipeline/__tests__/loading.test.d.ts.map +1 -0
- package/dist/src/pipeline/__tests__/parsing.test.d.ts +2 -0
- package/dist/src/pipeline/__tests__/parsing.test.d.ts.map +1 -0
- package/dist/src/pipeline/__tests__/pipeline.test.d.ts +2 -0
- package/dist/src/pipeline/__tests__/pipeline.test.d.ts.map +1 -0
- package/dist/src/pipeline/index.d.ts +19 -0
- package/dist/src/pipeline/index.d.ts.map +1 -0
- package/dist/src/pipeline/parser/callResolver.d.ts +52 -0
- package/dist/src/pipeline/parser/callResolver.d.ts.map +1 -0
- package/dist/src/pipeline/parser/extractors/generic.d.ts +15 -0
- package/dist/src/pipeline/parser/extractors/generic.d.ts.map +1 -0
- package/dist/src/pipeline/parser/extractors/go.d.ts +8 -0
- package/dist/src/pipeline/parser/extractors/go.d.ts.map +1 -0
- package/dist/src/pipeline/parser/extractors/python.d.ts +8 -0
- package/dist/src/pipeline/parser/extractors/python.d.ts.map +1 -0
- package/dist/src/pipeline/parser/extractors/typescript.d.ts +8 -0
- package/dist/src/pipeline/parser/extractors/typescript.d.ts.map +1 -0
- package/dist/src/pipeline/parser/importAnalyzer.d.ts +25 -0
- package/dist/src/pipeline/parser/importAnalyzer.d.ts.map +1 -0
- package/dist/src/pipeline/parser/manifestParser.d.ts +42 -0
- package/dist/src/pipeline/parser/manifestParser.d.ts.map +1 -0
- package/dist/src/pipeline/pipeline.d.ts +10 -0
- package/dist/src/pipeline/pipeline.d.ts.map +1 -0
- package/dist/src/pipeline/stages/loading.d.ts +6 -0
- package/dist/src/pipeline/stages/loading.d.ts.map +1 -0
- package/dist/src/pipeline/stages/parsing.d.ts +20 -0
- package/dist/src/pipeline/stages/parsing.d.ts.map +1 -0
- package/dist/src/pipeline/stages/processing.d.ts +15 -0
- package/dist/src/pipeline/stages/processing.d.ts.map +1 -0
- package/dist/src/pipeline/stages/resolving.d.ts +11 -0
- package/dist/src/pipeline/stages/resolving.d.ts.map +1 -0
- package/dist/src/pipeline/stages/saving.d.ts +7 -0
- package/dist/src/pipeline/stages/saving.d.ts.map +1 -0
- package/dist/src/pipeline/stages/scanning.d.ts +7 -0
- package/dist/src/pipeline/stages/scanning.d.ts.map +1 -0
- package/dist/src/pipeline/stages/summarizing.d.ts +16 -0
- package/dist/src/pipeline/stages/summarizing.d.ts.map +1 -0
- package/dist/src/pipeline/store/memory.d.ts +8 -0
- package/dist/src/pipeline/store/memory.d.ts.map +1 -0
- package/dist/src/pipeline/summarizer/templateSummarizer.d.ts +40 -0
- package/dist/src/pipeline/summarizer/templateSummarizer.d.ts.map +1 -0
- package/dist/src/pipeline/summarizer/types.d.ts +62 -0
- package/dist/src/pipeline/summarizer/types.d.ts.map +1 -0
- package/dist/src/pipeline/types.d.ts +119 -0
- package/dist/src/pipeline/types.d.ts.map +1 -0
- package/dist/src/pipeline/wasm.d.ts +14 -0
- package/dist/src/pipeline/wasm.d.ts.map +1 -0
- package/dist/src/sigma/useSelectionPulse.d.ts +3 -0
- package/dist/src/sigma/useSelectionPulse.d.ts.map +1 -0
- package/dist/{useHighlights-DbMfb0-p.js → useHighlights-CmOAWaLE.js} +60 -53
- package/dist/{useHighlights-DbMfb0-p.js.map → useHighlights-CmOAWaLE.js.map} +1 -1
- package/dist/{useHighlights-fRWg-A_c.cjs → useHighlights-zx7DM4V0.cjs} +60 -53
- package/dist/{useHighlights-fRWg-A_c.cjs.map → useHighlights-zx7DM4V0.cjs.map} +1 -1
- package/dist/utils.cjs +1 -1
- package/dist/utils.js +1 -1
- package/package.json +32 -4
- package/public/wasm/tree-sitter-bash.wasm +0 -0
- package/public/wasm/tree-sitter-c.wasm +0 -0
- package/public/wasm/tree-sitter-c_sharp.wasm +0 -0
- package/public/wasm/tree-sitter-cpp.wasm +0 -0
- package/public/wasm/tree-sitter-go.wasm +0 -0
- package/public/wasm/tree-sitter-java.wasm +0 -0
- package/public/wasm/tree-sitter-json.wasm +0 -0
- package/public/wasm/tree-sitter-kotlin.wasm +0 -0
- package/public/wasm/tree-sitter-python.wasm +0 -0
- package/public/wasm/tree-sitter-ruby.wasm +0 -0
- package/public/wasm/tree-sitter-rust.wasm +0 -0
- package/public/wasm/tree-sitter-swift.wasm +0 -0
- package/public/wasm/tree-sitter-toml.wasm +0 -0
- package/public/wasm/tree-sitter-tsx.wasm +0 -0
- package/public/wasm/tree-sitter-typescript.wasm +0 -0
- package/public/wasm/web-tree-sitter.wasm +0 -0
- package/dist/assets/spacingWorker-hXLGHyRg.js +0 -123
- package/dist/assets/spacingWorker-hXLGHyRg.js.map +0 -1
|
@@ -0,0 +1,3481 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const LANGUAGE_MAP = {
|
|
4
|
+
".py": "python",
|
|
5
|
+
".ts": "typescript",
|
|
6
|
+
".tsx": "typescript",
|
|
7
|
+
".js": "javascript",
|
|
8
|
+
".jsx": "javascript",
|
|
9
|
+
".go": "go",
|
|
10
|
+
".rs": "rust",
|
|
11
|
+
".java": "java",
|
|
12
|
+
".kt": "kotlin",
|
|
13
|
+
".rb": "ruby",
|
|
14
|
+
".c": "c",
|
|
15
|
+
".cpp": "cpp",
|
|
16
|
+
".h": "c",
|
|
17
|
+
".hpp": "cpp",
|
|
18
|
+
".cs": "csharp",
|
|
19
|
+
".swift": "swift"
|
|
20
|
+
};
|
|
21
|
+
function getExtension(path) {
|
|
22
|
+
const dot = path.lastIndexOf(".");
|
|
23
|
+
if (dot === -1 || dot === path.length - 1) return "";
|
|
24
|
+
return path.slice(dot);
|
|
25
|
+
}
|
|
26
|
+
function parentDir$1(path) {
|
|
27
|
+
const slash = path.lastIndexOf("/");
|
|
28
|
+
if (slash <= 0) return "";
|
|
29
|
+
return path.slice(0, slash);
|
|
30
|
+
}
|
|
31
|
+
function detectLanguage(ext) {
|
|
32
|
+
return LANGUAGE_MAP[ext] ?? null;
|
|
33
|
+
}
|
|
34
|
+
function ensureDirChain(repoId, dirPath, dirNodes, rels) {
|
|
35
|
+
if (!dirPath || dirNodes.has(dirPath)) return;
|
|
36
|
+
const parent = parentDir$1(dirPath);
|
|
37
|
+
ensureDirChain(repoId, parent, dirNodes, rels);
|
|
38
|
+
const dirId = `${repoId}/${dirPath}`;
|
|
39
|
+
const name = dirPath.includes("/") ? dirPath.slice(dirPath.lastIndexOf("/") + 1) : dirPath;
|
|
40
|
+
dirNodes.set(dirPath, {
|
|
41
|
+
id: dirId,
|
|
42
|
+
type: "Directory",
|
|
43
|
+
name,
|
|
44
|
+
properties: { path: dirPath }
|
|
45
|
+
});
|
|
46
|
+
const targetId = parent ? `${repoId}/${parent}` : repoId;
|
|
47
|
+
rels.push({
|
|
48
|
+
id: `${dirId}->DEFINED_IN->${targetId}`,
|
|
49
|
+
type: "DEFINED_IN",
|
|
50
|
+
source_id: dirId,
|
|
51
|
+
target_id: targetId
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const MANIFEST_BASENAMES = /* @__PURE__ */ new Set([
|
|
55
|
+
"package.json",
|
|
56
|
+
"go.mod",
|
|
57
|
+
"requirements.txt",
|
|
58
|
+
"pyproject.toml",
|
|
59
|
+
"Cargo.toml"
|
|
60
|
+
]);
|
|
61
|
+
const LOCK_BASENAMES = /* @__PURE__ */ new Set([
|
|
62
|
+
"package-lock.json",
|
|
63
|
+
"yarn.lock",
|
|
64
|
+
"pnpm-lock.yaml",
|
|
65
|
+
"go.sum",
|
|
66
|
+
"poetry.lock",
|
|
67
|
+
"Cargo.lock",
|
|
68
|
+
"uv.lock"
|
|
69
|
+
]);
|
|
70
|
+
function isManifestFile(path) {
|
|
71
|
+
const basename = path.split("/").pop() ?? "";
|
|
72
|
+
if (LOCK_BASENAMES.has(basename)) return false;
|
|
73
|
+
return MANIFEST_BASENAMES.has(basename);
|
|
74
|
+
}
|
|
75
|
+
function parseManifest(path, content) {
|
|
76
|
+
const basename = path.split("/").pop() ?? "";
|
|
77
|
+
switch (basename) {
|
|
78
|
+
case "package.json":
|
|
79
|
+
return parsePackageJson(content, path);
|
|
80
|
+
case "go.mod":
|
|
81
|
+
return parseGoMod(content, path);
|
|
82
|
+
case "requirements.txt":
|
|
83
|
+
return parseRequirementsTxt(content, path);
|
|
84
|
+
case "pyproject.toml":
|
|
85
|
+
return parsePyprojectToml(content, path);
|
|
86
|
+
case "Cargo.toml":
|
|
87
|
+
return parseCargoToml(content, path);
|
|
88
|
+
default:
|
|
89
|
+
return {
|
|
90
|
+
dependencies: [],
|
|
91
|
+
errors: [`Unsupported manifest: ${basename}`]
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function packageId(registry, name) {
|
|
96
|
+
return `pkg:${registry}:${name}`;
|
|
97
|
+
}
|
|
98
|
+
function packageSourceUrl(registry, name) {
|
|
99
|
+
switch (registry) {
|
|
100
|
+
case "npm":
|
|
101
|
+
return `https://www.npmjs.com/package/${name}`;
|
|
102
|
+
case "pypi":
|
|
103
|
+
return `https://pypi.org/project/${name}/`;
|
|
104
|
+
case "go":
|
|
105
|
+
return `https://pkg.go.dev/${name}`;
|
|
106
|
+
case "crates":
|
|
107
|
+
return `https://crates.io/crates/${name}`;
|
|
108
|
+
case "rubygems":
|
|
109
|
+
return `https://rubygems.org/gems/${name}`;
|
|
110
|
+
default:
|
|
111
|
+
return void 0;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function parsePackageJson(content, source) {
|
|
115
|
+
const errors = [];
|
|
116
|
+
const deps = [];
|
|
117
|
+
let parsed;
|
|
118
|
+
try {
|
|
119
|
+
parsed = JSON.parse(content);
|
|
120
|
+
} catch (e) {
|
|
121
|
+
return { dependencies: [], errors: [`Failed to parse ${source}: ${e}`] };
|
|
122
|
+
}
|
|
123
|
+
const sections = [
|
|
124
|
+
["dependencies", "runtime"],
|
|
125
|
+
["devDependencies", "dev"],
|
|
126
|
+
["peerDependencies", "peer"],
|
|
127
|
+
["optionalDependencies", "optional"]
|
|
128
|
+
];
|
|
129
|
+
for (const [key, depType] of sections) {
|
|
130
|
+
const section = parsed[key];
|
|
131
|
+
if (!section || typeof section !== "object") continue;
|
|
132
|
+
for (const [name, version] of Object.entries(
|
|
133
|
+
section
|
|
134
|
+
)) {
|
|
135
|
+
deps.push({
|
|
136
|
+
name,
|
|
137
|
+
version: typeof version === "string" ? version : "",
|
|
138
|
+
registry: "npm",
|
|
139
|
+
source,
|
|
140
|
+
dependencyType: depType
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return { dependencies: deps, errors };
|
|
145
|
+
}
|
|
146
|
+
function parseGoMod(content, source) {
|
|
147
|
+
const deps = [];
|
|
148
|
+
const errors = [];
|
|
149
|
+
const blockRe = /require\s*\(([\s\S]*?)\)/g;
|
|
150
|
+
let match;
|
|
151
|
+
while ((match = blockRe.exec(content)) !== null) {
|
|
152
|
+
const block = match[1];
|
|
153
|
+
for (const line of block.split("\n")) {
|
|
154
|
+
const dep = parseGoRequireLine(line, source);
|
|
155
|
+
if (dep) deps.push(dep);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const singleRe = /^require\s+(\S+)\s+(\S+)(.*)$/gm;
|
|
159
|
+
while ((match = singleRe.exec(content)) !== null) {
|
|
160
|
+
const depType = match[3]?.includes("// indirect") ? "indirect" : "runtime";
|
|
161
|
+
deps.push({
|
|
162
|
+
name: match[1],
|
|
163
|
+
version: match[2],
|
|
164
|
+
registry: "go",
|
|
165
|
+
source,
|
|
166
|
+
dependencyType: depType
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
return { dependencies: deps, errors };
|
|
170
|
+
}
|
|
171
|
+
function parseGoRequireLine(line, source) {
|
|
172
|
+
const trimmed = line.trim();
|
|
173
|
+
if (!trimmed || trimmed.startsWith("//")) return null;
|
|
174
|
+
const parts = trimmed.split(/\s+/);
|
|
175
|
+
if (parts.length < 2) return null;
|
|
176
|
+
const depType = trimmed.includes("// indirect") ? "indirect" : "runtime";
|
|
177
|
+
return {
|
|
178
|
+
name: parts[0],
|
|
179
|
+
version: parts[1],
|
|
180
|
+
registry: "go",
|
|
181
|
+
source,
|
|
182
|
+
dependencyType: depType
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function parseRequirementsTxt(content, source) {
|
|
186
|
+
const deps = [];
|
|
187
|
+
const errors = [];
|
|
188
|
+
for (const rawLine of content.split("\n")) {
|
|
189
|
+
const line = rawLine.trim();
|
|
190
|
+
if (!line || line.startsWith("#") || line.startsWith("-") || line.startsWith("http"))
|
|
191
|
+
continue;
|
|
192
|
+
const match = line.match(
|
|
193
|
+
/^([A-Za-z0-9_.-]+)(?:\[.*?\])?\s*([<>=!~]+\s*\S+)?/
|
|
194
|
+
);
|
|
195
|
+
if (!match) continue;
|
|
196
|
+
const rawName = match[1];
|
|
197
|
+
const version = match[2]?.trim() ?? "*";
|
|
198
|
+
deps.push({
|
|
199
|
+
name: normalizePyName(rawName),
|
|
200
|
+
version,
|
|
201
|
+
registry: "pypi",
|
|
202
|
+
source,
|
|
203
|
+
dependencyType: "runtime"
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return { dependencies: deps, errors };
|
|
207
|
+
}
|
|
208
|
+
function parsePyprojectToml(content, source) {
|
|
209
|
+
const deps = [];
|
|
210
|
+
const errors = [];
|
|
211
|
+
const lines = content.split("\n");
|
|
212
|
+
let i = 0;
|
|
213
|
+
let currentSection = "";
|
|
214
|
+
while (i < lines.length) {
|
|
215
|
+
const line = lines[i].trim();
|
|
216
|
+
if (line.startsWith("[")) {
|
|
217
|
+
currentSection = line;
|
|
218
|
+
i++;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
if (currentSection === "[project]" && line.startsWith("dependencies")) {
|
|
222
|
+
const eqIdx = line.indexOf("=");
|
|
223
|
+
if (eqIdx === -1) {
|
|
224
|
+
i++;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const afterEq = line.slice(eqIdx + 1).trim();
|
|
228
|
+
if (afterEq.startsWith("[")) {
|
|
229
|
+
const arrayContent = collectTomlArray(lines, i, eqIdx + 1);
|
|
230
|
+
for (const item of parseTomlStringArray(arrayContent)) {
|
|
231
|
+
const dep = parsePep508(item, source);
|
|
232
|
+
if (dep) deps.push(dep);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
i++;
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (currentSection === "[tool.poetry.dependencies]") {
|
|
239
|
+
const eqIdx = line.indexOf("=");
|
|
240
|
+
if (eqIdx > 0 && !line.startsWith("#") && !line.startsWith("[")) {
|
|
241
|
+
const name = line.slice(0, eqIdx).trim();
|
|
242
|
+
if (name === "python") {
|
|
243
|
+
i++;
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
const value = line.slice(eqIdx + 1).trim();
|
|
247
|
+
const version = value.replace(/^["']|["']$/g, "");
|
|
248
|
+
deps.push({
|
|
249
|
+
name: normalizePyName(name),
|
|
250
|
+
version,
|
|
251
|
+
registry: "pypi",
|
|
252
|
+
source,
|
|
253
|
+
dependencyType: "runtime"
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (currentSection.startsWith("[project.optional-dependencies")) {
|
|
258
|
+
if (line.includes("=") && !line.startsWith("#")) {
|
|
259
|
+
const eqIdx = line.indexOf("=");
|
|
260
|
+
const afterEq = line.slice(eqIdx + 1).trim();
|
|
261
|
+
if (afterEq.startsWith("[")) {
|
|
262
|
+
const arrayContent = collectTomlArray(lines, i, eqIdx + 1);
|
|
263
|
+
for (const item of parseTomlStringArray(arrayContent)) {
|
|
264
|
+
const dep = parsePep508(item, source, "optional");
|
|
265
|
+
if (dep) deps.push(dep);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
i++;
|
|
271
|
+
}
|
|
272
|
+
return { dependencies: deps, errors };
|
|
273
|
+
}
|
|
274
|
+
function collectTomlArray(lines, startLine, charOffset) {
|
|
275
|
+
let result = lines[startLine].slice(charOffset).trim();
|
|
276
|
+
if (bracketsClosed(result)) return result;
|
|
277
|
+
for (let i = startLine + 1; i < lines.length; i++) {
|
|
278
|
+
result += "\n" + lines[i];
|
|
279
|
+
if (bracketsClosed(result)) break;
|
|
280
|
+
}
|
|
281
|
+
return result;
|
|
282
|
+
}
|
|
283
|
+
function bracketsClosed(s) {
|
|
284
|
+
let depth = 0;
|
|
285
|
+
for (const ch of s) {
|
|
286
|
+
if (ch === "[") depth++;
|
|
287
|
+
else if (ch === "]") depth--;
|
|
288
|
+
if (depth === 0 && s.indexOf("[") !== -1) return true;
|
|
289
|
+
}
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
function parseTomlStringArray(raw) {
|
|
293
|
+
const items = [];
|
|
294
|
+
const re = /["']([^"']+)["']/g;
|
|
295
|
+
let m;
|
|
296
|
+
while ((m = re.exec(raw)) !== null) {
|
|
297
|
+
items.push(m[1]);
|
|
298
|
+
}
|
|
299
|
+
return items;
|
|
300
|
+
}
|
|
301
|
+
function parsePep508(spec, source, depType = "runtime") {
|
|
302
|
+
const trimmed = spec.trim();
|
|
303
|
+
if (!trimmed) return null;
|
|
304
|
+
const match = trimmed.match(
|
|
305
|
+
/^([A-Za-z0-9_.-]+)(?:\[.*?\])?\s*([<>=!~]+.*)?$/
|
|
306
|
+
);
|
|
307
|
+
if (!match) return null;
|
|
308
|
+
return {
|
|
309
|
+
name: normalizePyName(match[1]),
|
|
310
|
+
version: match[2]?.trim() ?? "*",
|
|
311
|
+
registry: "pypi",
|
|
312
|
+
source,
|
|
313
|
+
dependencyType: depType
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
function normalizePyName(name) {
|
|
317
|
+
return name.toLowerCase().replace(/_/g, "-");
|
|
318
|
+
}
|
|
319
|
+
function parseCargoToml(content, source) {
|
|
320
|
+
const deps = [];
|
|
321
|
+
const errors = [];
|
|
322
|
+
const lines = content.split("\n");
|
|
323
|
+
let currentSection = "";
|
|
324
|
+
const depSections = {
|
|
325
|
+
"[dependencies]": "runtime",
|
|
326
|
+
"[dev-dependencies]": "dev",
|
|
327
|
+
"[build-dependencies]": "dev"
|
|
328
|
+
};
|
|
329
|
+
for (const rawLine of lines) {
|
|
330
|
+
const line = rawLine.trim();
|
|
331
|
+
if (line.startsWith("[")) {
|
|
332
|
+
currentSection = line;
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (line.startsWith("#") || !line) continue;
|
|
336
|
+
const depType = depSections[currentSection];
|
|
337
|
+
if (!depType) continue;
|
|
338
|
+
const eqIdx = line.indexOf("=");
|
|
339
|
+
if (eqIdx <= 0) continue;
|
|
340
|
+
const name = line.slice(0, eqIdx).trim();
|
|
341
|
+
const value = line.slice(eqIdx + 1).trim();
|
|
342
|
+
let version = "*";
|
|
343
|
+
if (value.startsWith('"') || value.startsWith("'")) {
|
|
344
|
+
version = value.replace(/^["']|["']$/g, "");
|
|
345
|
+
} else if (value.startsWith("{")) {
|
|
346
|
+
const verMatch = value.match(/version\s*=\s*["']([^"']+)["']/);
|
|
347
|
+
if (verMatch) version = verMatch[1];
|
|
348
|
+
}
|
|
349
|
+
deps.push({
|
|
350
|
+
name,
|
|
351
|
+
version,
|
|
352
|
+
registry: "crates",
|
|
353
|
+
source,
|
|
354
|
+
dependencyType: depType
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
return { dependencies: deps, errors };
|
|
358
|
+
}
|
|
359
|
+
function npmPackageName(specifier) {
|
|
360
|
+
if (specifier.startsWith("@")) {
|
|
361
|
+
const parts = specifier.split("/");
|
|
362
|
+
return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : specifier;
|
|
363
|
+
}
|
|
364
|
+
return specifier.split("/")[0];
|
|
365
|
+
}
|
|
366
|
+
const PARSEABLE_LANGUAGES = /* @__PURE__ */ new Set([
|
|
367
|
+
"python",
|
|
368
|
+
"typescript",
|
|
369
|
+
"javascript",
|
|
370
|
+
"go",
|
|
371
|
+
"rust",
|
|
372
|
+
"java",
|
|
373
|
+
"kotlin",
|
|
374
|
+
"csharp",
|
|
375
|
+
"c",
|
|
376
|
+
"cpp",
|
|
377
|
+
"ruby",
|
|
378
|
+
"swift"
|
|
379
|
+
]);
|
|
380
|
+
function* execute$3(input, ctx) {
|
|
381
|
+
const { repo } = input;
|
|
382
|
+
const repoId = `${repo.owner}/${repo.repo}`;
|
|
383
|
+
yield {
|
|
384
|
+
kind: "stage_start",
|
|
385
|
+
phase: "scanning",
|
|
386
|
+
message: `Scanning ${repo.files.length} files`
|
|
387
|
+
};
|
|
388
|
+
const repoProps = { ref: repo.ref };
|
|
389
|
+
if (repo.url) repoProps.source_uri = repo.url;
|
|
390
|
+
if (repo.provider) repoProps.provider = repo.provider;
|
|
391
|
+
const repoNode = {
|
|
392
|
+
id: repoId,
|
|
393
|
+
type: "Repository",
|
|
394
|
+
name: repoId,
|
|
395
|
+
properties: repoProps
|
|
396
|
+
};
|
|
397
|
+
const dirNodes = /* @__PURE__ */ new Map();
|
|
398
|
+
const fileNodes = [];
|
|
399
|
+
const structureRels = [];
|
|
400
|
+
const parseableFiles = [];
|
|
401
|
+
const packageNodes = /* @__PURE__ */ new Map();
|
|
402
|
+
const dependencyRels = [];
|
|
403
|
+
let goModulePath;
|
|
404
|
+
const knownPaths = /* @__PURE__ */ new Set();
|
|
405
|
+
const pathToFileId = /* @__PURE__ */ new Map();
|
|
406
|
+
const total = repo.files.length;
|
|
407
|
+
for (let i = 0; i < repo.files.length; i++) {
|
|
408
|
+
if (ctx.cancelled) break;
|
|
409
|
+
const file = repo.files[i];
|
|
410
|
+
const ext = getExtension(file.path);
|
|
411
|
+
const language = detectLanguage(ext);
|
|
412
|
+
const fileId = `${repoId}/${file.path}`;
|
|
413
|
+
const fileName = file.path.includes("/") ? file.path.slice(file.path.lastIndexOf("/") + 1) : file.path;
|
|
414
|
+
const fileProps = {
|
|
415
|
+
path: file.path,
|
|
416
|
+
extension: ext
|
|
417
|
+
};
|
|
418
|
+
if (language) fileProps.language = language;
|
|
419
|
+
if (repo.url) {
|
|
420
|
+
fileProps.source_uri = `${repo.url}/blob/${repo.ref}/${file.path}`;
|
|
421
|
+
}
|
|
422
|
+
fileNodes.push({
|
|
423
|
+
id: fileId,
|
|
424
|
+
type: "File",
|
|
425
|
+
name: fileName,
|
|
426
|
+
properties: fileProps
|
|
427
|
+
});
|
|
428
|
+
const dir = parentDir$1(file.path);
|
|
429
|
+
ensureDirChain(repoId, dir, dirNodes, structureRels);
|
|
430
|
+
const parentId = dir ? `${repoId}/${dir}` : repoId;
|
|
431
|
+
structureRels.push({
|
|
432
|
+
id: `${fileId}->DEFINED_IN->${parentId}`,
|
|
433
|
+
type: "DEFINED_IN",
|
|
434
|
+
source_id: fileId,
|
|
435
|
+
target_id: parentId
|
|
436
|
+
});
|
|
437
|
+
if (language && PARSEABLE_LANGUAGES.has(language)) {
|
|
438
|
+
parseableFiles.push(file);
|
|
439
|
+
}
|
|
440
|
+
knownPaths.add(file.path);
|
|
441
|
+
pathToFileId.set(file.path, fileId);
|
|
442
|
+
if (isManifestFile(file.path)) {
|
|
443
|
+
const manifestResult = parseManifest(file.path, file.content);
|
|
444
|
+
if (file.path.endsWith("go.mod") || file.path.includes("/go.mod")) {
|
|
445
|
+
const moduleMatch = file.content.match(/^module\s+(\S+)/m);
|
|
446
|
+
if (moduleMatch) goModulePath = moduleMatch[1];
|
|
447
|
+
}
|
|
448
|
+
for (const dep of manifestResult.dependencies) {
|
|
449
|
+
const pkgId = packageId(dep.registry, dep.name);
|
|
450
|
+
if (!packageNodes.has(pkgId)) {
|
|
451
|
+
const props = {
|
|
452
|
+
registry: dep.registry,
|
|
453
|
+
version: dep.version
|
|
454
|
+
};
|
|
455
|
+
const sourceUrl = packageSourceUrl(dep.registry, dep.name);
|
|
456
|
+
if (sourceUrl) props.source_uri = sourceUrl;
|
|
457
|
+
packageNodes.set(pkgId, {
|
|
458
|
+
id: pkgId,
|
|
459
|
+
type: "Package",
|
|
460
|
+
name: dep.name,
|
|
461
|
+
properties: props
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
dependencyRels.push({
|
|
465
|
+
id: `${repoId}->DEPENDS_ON->${pkgId}`,
|
|
466
|
+
type: "DEPENDS_ON",
|
|
467
|
+
source_id: repoId,
|
|
468
|
+
target_id: pkgId,
|
|
469
|
+
properties: {
|
|
470
|
+
version: dep.version,
|
|
471
|
+
dependency_type: dep.dependencyType,
|
|
472
|
+
source: dep.source
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
yield {
|
|
478
|
+
kind: "stage_progress",
|
|
479
|
+
phase: "scanning",
|
|
480
|
+
message: `Scanning ${file.path}`,
|
|
481
|
+
detail: { current: i + 1, total, fileName: file.path }
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
const structureNodes = [
|
|
485
|
+
repoNode,
|
|
486
|
+
...dirNodes.values(),
|
|
487
|
+
...fileNodes,
|
|
488
|
+
...packageNodes.values()
|
|
489
|
+
];
|
|
490
|
+
yield {
|
|
491
|
+
kind: "stage_stop",
|
|
492
|
+
phase: "scanning",
|
|
493
|
+
message: `Scanned ${fileNodes.length} files, ${dirNodes.size} directories, ${packageNodes.size} packages`,
|
|
494
|
+
nodes: structureNodes,
|
|
495
|
+
relationships: [...structureRels, ...dependencyRels]
|
|
496
|
+
};
|
|
497
|
+
return {
|
|
498
|
+
repo,
|
|
499
|
+
repoId,
|
|
500
|
+
repoNode,
|
|
501
|
+
dirNodes,
|
|
502
|
+
fileNodes,
|
|
503
|
+
structureRels,
|
|
504
|
+
parseableFiles,
|
|
505
|
+
packageNodes,
|
|
506
|
+
dependencyRels,
|
|
507
|
+
goModulePath,
|
|
508
|
+
knownPaths,
|
|
509
|
+
pathToFileId
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
function extractDocstring(bodyNode) {
|
|
513
|
+
if (!bodyNode) return void 0;
|
|
514
|
+
const first = bodyNode.namedChildren[0];
|
|
515
|
+
if (!first || first.type !== "expression_statement") return void 0;
|
|
516
|
+
const strNode = first.namedChildren[0];
|
|
517
|
+
if (!strNode || strNode.type !== "string") return void 0;
|
|
518
|
+
const raw = strNode.text;
|
|
519
|
+
const stripped = raw.replace(/^("""|''')\s?/, "").replace(/\s?("""|''')$/, "");
|
|
520
|
+
return stripped.trim() || void 0;
|
|
521
|
+
}
|
|
522
|
+
function extractPython(rootNode) {
|
|
523
|
+
const symbols = walkNode$2(rootNode);
|
|
524
|
+
return { symbols, language: "python", rootNode };
|
|
525
|
+
}
|
|
526
|
+
function walkNode$2(node) {
|
|
527
|
+
const symbols = [];
|
|
528
|
+
for (const child of node.children) {
|
|
529
|
+
if (child.type === "class_definition") {
|
|
530
|
+
const sym = extractClass$2(child);
|
|
531
|
+
if (sym) symbols.push(sym);
|
|
532
|
+
} else if (child.type === "function_definition") {
|
|
533
|
+
const sym = extractFunction$3(child);
|
|
534
|
+
if (sym) symbols.push(sym);
|
|
535
|
+
} else if (child.type === "decorated_definition") {
|
|
536
|
+
for (const sub of child.children) {
|
|
537
|
+
if (sub.type === "class_definition") {
|
|
538
|
+
const sym = extractClass$2(sub);
|
|
539
|
+
if (sym) symbols.push(sym);
|
|
540
|
+
} else if (sub.type === "function_definition") {
|
|
541
|
+
const sym = extractFunction$3(sub);
|
|
542
|
+
if (sym) symbols.push(sym);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
return symbols;
|
|
548
|
+
}
|
|
549
|
+
function extractClass$2(node) {
|
|
550
|
+
const nameNode = node.childForFieldName("name");
|
|
551
|
+
if (!nameNode) return null;
|
|
552
|
+
const bodyNode = node.childForFieldName("body");
|
|
553
|
+
const children = walkClassBody$1(node);
|
|
554
|
+
const superclasses = extractBaseClasses(node);
|
|
555
|
+
const docs = extractDocstring(bodyNode);
|
|
556
|
+
return {
|
|
557
|
+
name: nameNode.text,
|
|
558
|
+
kind: "class",
|
|
559
|
+
startLine: node.startPosition.row + 1,
|
|
560
|
+
endLine: node.endPosition.row + 1,
|
|
561
|
+
signature: null,
|
|
562
|
+
children,
|
|
563
|
+
calls: [],
|
|
564
|
+
receiverVar: null,
|
|
565
|
+
receiverType: null,
|
|
566
|
+
paramTypes: null,
|
|
567
|
+
superclasses: superclasses.length > 0 ? superclasses : void 0,
|
|
568
|
+
docs
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
function extractBaseClasses(node) {
|
|
572
|
+
const bases = [];
|
|
573
|
+
const argList = node.childForFieldName("superclasses");
|
|
574
|
+
if (!argList) return bases;
|
|
575
|
+
for (const child of argList.children) {
|
|
576
|
+
if (child.type === "identifier") {
|
|
577
|
+
bases.push(child.text);
|
|
578
|
+
} else if (child.type === "attribute") {
|
|
579
|
+
bases.push(child.text);
|
|
580
|
+
} else if (child.type === "keyword_argument") ;
|
|
581
|
+
}
|
|
582
|
+
return bases;
|
|
583
|
+
}
|
|
584
|
+
function walkClassBody$1(node) {
|
|
585
|
+
const body = node.childForFieldName("body");
|
|
586
|
+
if (!body) return [];
|
|
587
|
+
const methods = [];
|
|
588
|
+
for (const child of body.children) {
|
|
589
|
+
if (child.type === "function_definition") {
|
|
590
|
+
const sym = extractFunction$3(child);
|
|
591
|
+
if (sym) methods.push(sym);
|
|
592
|
+
} else if (child.type === "decorated_definition") {
|
|
593
|
+
for (const sub of child.children) {
|
|
594
|
+
if (sub.type === "function_definition") {
|
|
595
|
+
const sym = extractFunction$3(sub);
|
|
596
|
+
if (sym) methods.push(sym);
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return methods;
|
|
602
|
+
}
|
|
603
|
+
function extractFunction$3(node) {
|
|
604
|
+
const nameNode = node.childForFieldName("name");
|
|
605
|
+
if (!nameNode) return null;
|
|
606
|
+
const paramsNode = node.childForFieldName("parameters");
|
|
607
|
+
const signature = paramsNode ? paramsNode.text : null;
|
|
608
|
+
const paramTypes = paramsNode ? extractParamTypes(paramsNode) : null;
|
|
609
|
+
const bodyNode = node.childForFieldName("body");
|
|
610
|
+
const calls = bodyNode ? collectCalls$2(bodyNode) : [];
|
|
611
|
+
const docs = extractDocstring(bodyNode);
|
|
612
|
+
return {
|
|
613
|
+
name: nameNode.text,
|
|
614
|
+
kind: "function",
|
|
615
|
+
startLine: node.startPosition.row + 1,
|
|
616
|
+
endLine: node.endPosition.row + 1,
|
|
617
|
+
signature,
|
|
618
|
+
children: [],
|
|
619
|
+
calls,
|
|
620
|
+
receiverVar: null,
|
|
621
|
+
receiverType: null,
|
|
622
|
+
paramTypes,
|
|
623
|
+
docs
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
function extractParamTypes(paramsNode) {
|
|
627
|
+
const types = {};
|
|
628
|
+
for (const child of paramsNode.children) {
|
|
629
|
+
if (child.type === "typed_parameter" || child.type === "typed_default_parameter") {
|
|
630
|
+
let nameNode = null;
|
|
631
|
+
for (const sub of child.children) {
|
|
632
|
+
if (sub.type === "identifier") {
|
|
633
|
+
nameNode = sub;
|
|
634
|
+
break;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
const typeNode = child.childForFieldName("type");
|
|
638
|
+
if (nameNode && typeNode) {
|
|
639
|
+
const paramName = nameNode.text;
|
|
640
|
+
if (paramName === "self" || paramName === "cls") continue;
|
|
641
|
+
const typeText = typeNode.text;
|
|
642
|
+
const leaf = typeText.includes(".") ? typeText.split(".").pop() : typeText;
|
|
643
|
+
types[paramName] = leaf;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
return Object.keys(types).length > 0 ? types : null;
|
|
648
|
+
}
|
|
649
|
+
function collectCalls$2(node) {
|
|
650
|
+
const calls = [];
|
|
651
|
+
for (const child of node.children) {
|
|
652
|
+
if (child.type === "call") {
|
|
653
|
+
const funcNode = child.childForFieldName("function");
|
|
654
|
+
if (funcNode && funcNode.type === "identifier") {
|
|
655
|
+
calls.push({ name: funcNode.text, receiver: null, kind: "bare" });
|
|
656
|
+
} else if (funcNode && funcNode.type === "attribute") {
|
|
657
|
+
const objNode = funcNode.childForFieldName("object");
|
|
658
|
+
const attrNode = funcNode.childForFieldName("attribute");
|
|
659
|
+
if (objNode && attrNode) {
|
|
660
|
+
calls.push({
|
|
661
|
+
name: attrNode.text,
|
|
662
|
+
receiver: objNode.text,
|
|
663
|
+
kind: "attribute"
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
calls.push(...collectCalls$2(child));
|
|
669
|
+
}
|
|
670
|
+
return calls;
|
|
671
|
+
}
|
|
672
|
+
const CLASS_TYPES = /* @__PURE__ */ new Set([
|
|
673
|
+
"class_declaration",
|
|
674
|
+
"abstract_class_declaration"
|
|
675
|
+
]);
|
|
676
|
+
const FUNCTION_TYPES = /* @__PURE__ */ new Set([
|
|
677
|
+
"function_declaration",
|
|
678
|
+
"generator_function_declaration"
|
|
679
|
+
]);
|
|
680
|
+
const METHOD_TYPES = /* @__PURE__ */ new Set(["method_definition", "public_field_definition"]);
|
|
681
|
+
function extractJSDoc(node) {
|
|
682
|
+
let prev = node.previousNamedSibling;
|
|
683
|
+
if ((!prev || prev.type !== "comment") && node.parent?.type === "export_statement") {
|
|
684
|
+
prev = node.parent.previousNamedSibling;
|
|
685
|
+
}
|
|
686
|
+
if (!prev || prev.type !== "comment") return void 0;
|
|
687
|
+
const text = prev.text;
|
|
688
|
+
if (!text.startsWith("/**")) return void 0;
|
|
689
|
+
const cleaned = text.replace(/^\/\*\*\s*/, "").replace(/\s*\*\/$/, "").split("\n").map((line) => line.replace(/^\s*\*\s?/, "")).join("\n").trim();
|
|
690
|
+
return cleaned || void 0;
|
|
691
|
+
}
|
|
692
|
+
function extractTypeScript(rootNode, language = "typescript") {
|
|
693
|
+
const symbols = walkNode$1(rootNode);
|
|
694
|
+
return { symbols, language, rootNode };
|
|
695
|
+
}
|
|
696
|
+
function walkNode$1(node) {
|
|
697
|
+
const symbols = [];
|
|
698
|
+
for (const child of node.children) {
|
|
699
|
+
if (CLASS_TYPES.has(child.type)) {
|
|
700
|
+
const sym = extractClass$1(child);
|
|
701
|
+
if (sym) symbols.push(sym);
|
|
702
|
+
} else if (FUNCTION_TYPES.has(child.type)) {
|
|
703
|
+
const sym = extractFunction$2(child);
|
|
704
|
+
if (sym) symbols.push(sym);
|
|
705
|
+
} else if (child.type === "lexical_declaration") {
|
|
706
|
+
symbols.push(...extractLexicalDeclaration(child));
|
|
707
|
+
} else if (child.type === "export_statement") {
|
|
708
|
+
symbols.push(...walkNode$1(child));
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
return symbols;
|
|
712
|
+
}
|
|
713
|
+
function extractClass$1(node) {
|
|
714
|
+
const nameNode = node.childForFieldName("name");
|
|
715
|
+
if (!nameNode) return null;
|
|
716
|
+
const children = walkClassBody(node);
|
|
717
|
+
const { superclasses, interfaces } = extractClassHeritage(node);
|
|
718
|
+
const docs = extractJSDoc(node);
|
|
719
|
+
return {
|
|
720
|
+
name: nameNode.text,
|
|
721
|
+
kind: "class",
|
|
722
|
+
startLine: node.startPosition.row + 1,
|
|
723
|
+
endLine: node.endPosition.row + 1,
|
|
724
|
+
signature: null,
|
|
725
|
+
children,
|
|
726
|
+
calls: [],
|
|
727
|
+
receiverVar: null,
|
|
728
|
+
receiverType: null,
|
|
729
|
+
paramTypes: null,
|
|
730
|
+
superclasses: superclasses.length > 0 ? superclasses : void 0,
|
|
731
|
+
interfaces: interfaces.length > 0 ? interfaces : void 0,
|
|
732
|
+
docs
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
function extractClassHeritage(node) {
|
|
736
|
+
const superclasses = [];
|
|
737
|
+
const interfaces = [];
|
|
738
|
+
for (const child of node.children) {
|
|
739
|
+
if (child.type === "class_heritage") {
|
|
740
|
+
for (const clause of child.children) {
|
|
741
|
+
if (clause.type === "extends_clause") {
|
|
742
|
+
superclasses.push(...collectHeritageNames(clause));
|
|
743
|
+
} else if (clause.type === "implements_clause") {
|
|
744
|
+
interfaces.push(...collectHeritageNames(clause));
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return { superclasses, interfaces };
|
|
750
|
+
}
|
|
751
|
+
function collectHeritageNames(clause) {
|
|
752
|
+
const names = [];
|
|
753
|
+
for (const child of clause.children) {
|
|
754
|
+
if (child.type === "identifier" || child.type === "type_identifier") {
|
|
755
|
+
names.push(child.text);
|
|
756
|
+
} else if (child.type === "generic_type") {
|
|
757
|
+
const nameNode = child.childForFieldName("name");
|
|
758
|
+
if (nameNode) names.push(nameNode.text);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
return names;
|
|
762
|
+
}
|
|
763
|
+
function walkClassBody(node) {
|
|
764
|
+
const body = node.childForFieldName("body");
|
|
765
|
+
if (!body) return [];
|
|
766
|
+
const methods = [];
|
|
767
|
+
for (const child of body.children) {
|
|
768
|
+
if (METHOD_TYPES.has(child.type)) {
|
|
769
|
+
const sym = extractMethod$1(child);
|
|
770
|
+
if (sym) methods.push(sym);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return methods;
|
|
774
|
+
}
|
|
775
|
+
function extractFunction$2(node) {
|
|
776
|
+
const nameNode = node.childForFieldName("name");
|
|
777
|
+
if (!nameNode) return null;
|
|
778
|
+
const paramsNode = node.childForFieldName("parameters");
|
|
779
|
+
const signature = paramsNode ? paramsNode.text : null;
|
|
780
|
+
const bodyNode = node.childForFieldName("body");
|
|
781
|
+
const calls = bodyNode ? collectCalls$1(bodyNode) : [];
|
|
782
|
+
const docs = extractJSDoc(node);
|
|
783
|
+
return {
|
|
784
|
+
name: nameNode.text,
|
|
785
|
+
kind: "function",
|
|
786
|
+
startLine: node.startPosition.row + 1,
|
|
787
|
+
endLine: node.endPosition.row + 1,
|
|
788
|
+
signature,
|
|
789
|
+
children: [],
|
|
790
|
+
calls,
|
|
791
|
+
receiverVar: null,
|
|
792
|
+
receiverType: null,
|
|
793
|
+
paramTypes: null,
|
|
794
|
+
docs
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
function extractMethod$1(node) {
|
|
798
|
+
const nameNode = node.childForFieldName("name");
|
|
799
|
+
if (!nameNode) return null;
|
|
800
|
+
const paramsNode = node.childForFieldName("parameters");
|
|
801
|
+
const signature = paramsNode ? paramsNode.text : null;
|
|
802
|
+
const bodyNode = node.childForFieldName("body");
|
|
803
|
+
const calls = bodyNode ? collectCalls$1(bodyNode) : [];
|
|
804
|
+
const docs = extractJSDoc(node);
|
|
805
|
+
return {
|
|
806
|
+
name: nameNode.text,
|
|
807
|
+
kind: "function",
|
|
808
|
+
startLine: node.startPosition.row + 1,
|
|
809
|
+
endLine: node.endPosition.row + 1,
|
|
810
|
+
signature,
|
|
811
|
+
children: [],
|
|
812
|
+
calls,
|
|
813
|
+
receiverVar: null,
|
|
814
|
+
receiverType: null,
|
|
815
|
+
paramTypes: null,
|
|
816
|
+
docs
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
function extractLexicalDeclaration(node) {
|
|
820
|
+
const symbols = [];
|
|
821
|
+
for (const child of node.children) {
|
|
822
|
+
if (child.type === "variable_declarator") {
|
|
823
|
+
const nameNode = child.childForFieldName("name");
|
|
824
|
+
const valueNode = child.childForFieldName("value");
|
|
825
|
+
if (!nameNode || !valueNode) continue;
|
|
826
|
+
const name = nameNode.text;
|
|
827
|
+
if (valueNode.type === "arrow_function" || valueNode.type === "function_expression") {
|
|
828
|
+
const sym = extractArrowFunction(name, node, valueNode);
|
|
829
|
+
if (sym) symbols.push(sym);
|
|
830
|
+
} else if (valueNode.type === "class") {
|
|
831
|
+
const sym = extractClassExpression(name, node, valueNode);
|
|
832
|
+
if (sym) symbols.push(sym);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
return symbols;
|
|
837
|
+
}
|
|
838
|
+
function extractArrowFunction(name, declNode, valueNode) {
|
|
839
|
+
const paramsNode = valueNode.childForFieldName("parameters");
|
|
840
|
+
const signature = paramsNode ? paramsNode.text : null;
|
|
841
|
+
const bodyNode = valueNode.childForFieldName("body");
|
|
842
|
+
const calls = bodyNode ? collectCalls$1(bodyNode) : [];
|
|
843
|
+
const docs = extractJSDoc(declNode);
|
|
844
|
+
return {
|
|
845
|
+
name,
|
|
846
|
+
kind: "function",
|
|
847
|
+
startLine: declNode.startPosition.row + 1,
|
|
848
|
+
endLine: declNode.endPosition.row + 1,
|
|
849
|
+
signature,
|
|
850
|
+
children: [],
|
|
851
|
+
calls,
|
|
852
|
+
receiverVar: null,
|
|
853
|
+
receiverType: null,
|
|
854
|
+
paramTypes: null,
|
|
855
|
+
docs
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
function extractClassExpression(name, declNode, valueNode) {
|
|
859
|
+
const children = walkClassBody(valueNode);
|
|
860
|
+
const { superclasses, interfaces } = extractClassHeritage(valueNode);
|
|
861
|
+
const docs = extractJSDoc(declNode);
|
|
862
|
+
return {
|
|
863
|
+
name,
|
|
864
|
+
kind: "class",
|
|
865
|
+
startLine: declNode.startPosition.row + 1,
|
|
866
|
+
endLine: declNode.endPosition.row + 1,
|
|
867
|
+
signature: null,
|
|
868
|
+
children,
|
|
869
|
+
calls: [],
|
|
870
|
+
receiverVar: null,
|
|
871
|
+
receiverType: null,
|
|
872
|
+
paramTypes: null,
|
|
873
|
+
superclasses: superclasses.length > 0 ? superclasses : void 0,
|
|
874
|
+
interfaces: interfaces.length > 0 ? interfaces : void 0,
|
|
875
|
+
docs
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
function collectCalls$1(node) {
|
|
879
|
+
const calls = [];
|
|
880
|
+
if (node.type === "call_expression") {
|
|
881
|
+
const funcNode = node.childForFieldName("function");
|
|
882
|
+
if (funcNode && funcNode.type === "identifier") {
|
|
883
|
+
calls.push({ name: funcNode.text, receiver: null, kind: "bare" });
|
|
884
|
+
} else if (funcNode && funcNode.type === "member_expression") {
|
|
885
|
+
const objNode = funcNode.childForFieldName("object");
|
|
886
|
+
const propNode = funcNode.childForFieldName("property");
|
|
887
|
+
if (objNode && propNode) {
|
|
888
|
+
calls.push({
|
|
889
|
+
name: propNode.text,
|
|
890
|
+
receiver: objNode.text,
|
|
891
|
+
kind: "attribute"
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
for (const child of node.children) {
|
|
897
|
+
calls.push(...collectCalls$1(child));
|
|
898
|
+
}
|
|
899
|
+
return calls;
|
|
900
|
+
}
|
|
901
|
+
function extractGoDoc(node) {
|
|
902
|
+
const comments = [];
|
|
903
|
+
let prev = node.previousNamedSibling;
|
|
904
|
+
while (prev && prev.type === "comment") {
|
|
905
|
+
comments.push(prev.text);
|
|
906
|
+
prev = prev.previousNamedSibling;
|
|
907
|
+
}
|
|
908
|
+
if (comments.length === 0) return void 0;
|
|
909
|
+
const cleaned = comments.reverse().map((line) => line.replace(/^\/\/\s?/, "")).join("\n").trim();
|
|
910
|
+
return cleaned || void 0;
|
|
911
|
+
}
|
|
912
|
+
function extractGo(rootNode) {
|
|
913
|
+
const symbols = walkNode(rootNode);
|
|
914
|
+
return { symbols, language: "go", rootNode };
|
|
915
|
+
}
|
|
916
|
+
function walkNode(node) {
|
|
917
|
+
const symbols = [];
|
|
918
|
+
for (const child of node.children) {
|
|
919
|
+
let sym = null;
|
|
920
|
+
if (child.type === "type_declaration") {
|
|
921
|
+
sym = extractTypeDecl(child);
|
|
922
|
+
} else if (child.type === "function_declaration") {
|
|
923
|
+
sym = extractFunction$1(child);
|
|
924
|
+
} else if (child.type === "method_declaration") {
|
|
925
|
+
sym = extractMethod(child);
|
|
926
|
+
}
|
|
927
|
+
if (sym) {
|
|
928
|
+
sym.docs = extractGoDoc(child);
|
|
929
|
+
symbols.push(sym);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
return symbols;
|
|
933
|
+
}
|
|
934
|
+
function extractTypeDecl(node) {
|
|
935
|
+
for (const child of node.children) {
|
|
936
|
+
if (child.type === "type_spec") {
|
|
937
|
+
const nameNode = child.childForFieldName("name");
|
|
938
|
+
if (!nameNode) continue;
|
|
939
|
+
const typeNode = child.childForFieldName("type");
|
|
940
|
+
if (typeNode && (typeNode.type === "struct_type" || typeNode.type === "interface_type")) {
|
|
941
|
+
const isInterface = typeNode.type === "interface_type";
|
|
942
|
+
const methods = isInterface ? extractInterfaceMethods(typeNode) : [];
|
|
943
|
+
const subtype = isInterface ? "interface" : "struct";
|
|
944
|
+
const embedded = isInterface ? extractEmbeddedInterfaces(typeNode) : extractEmbeddedStructs(typeNode);
|
|
945
|
+
return {
|
|
946
|
+
name: nameNode.text,
|
|
947
|
+
kind: "class",
|
|
948
|
+
startLine: node.startPosition.row + 1,
|
|
949
|
+
endLine: node.endPosition.row + 1,
|
|
950
|
+
signature: null,
|
|
951
|
+
children: methods,
|
|
952
|
+
calls: [],
|
|
953
|
+
receiverVar: null,
|
|
954
|
+
receiverType: null,
|
|
955
|
+
paramTypes: null,
|
|
956
|
+
subtype,
|
|
957
|
+
interfaces: isInterface && embedded.length > 0 ? embedded : void 0,
|
|
958
|
+
superclasses: !isInterface && embedded.length > 0 ? embedded : void 0
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
return null;
|
|
964
|
+
}
|
|
965
|
+
function extractEmbeddedInterfaces(node) {
|
|
966
|
+
const names = [];
|
|
967
|
+
for (const child of node.children) {
|
|
968
|
+
if (child.type === "type_elem") {
|
|
969
|
+
for (const sub of child.children) {
|
|
970
|
+
if (sub.type === "type_identifier") {
|
|
971
|
+
names.push(sub.text);
|
|
972
|
+
} else if (sub.type === "qualified_type") {
|
|
973
|
+
names.push(sub.text);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
return names;
|
|
979
|
+
}
|
|
980
|
+
function extractEmbeddedStructs(node) {
|
|
981
|
+
const names = [];
|
|
982
|
+
for (const child of node.children) {
|
|
983
|
+
if (child.type === "field_declaration_list") {
|
|
984
|
+
for (const field of child.children) {
|
|
985
|
+
if (field.type === "field_declaration") {
|
|
986
|
+
const nameNode = field.childForFieldName("name");
|
|
987
|
+
const typeNode = field.childForFieldName("type");
|
|
988
|
+
if (!nameNode && typeNode) {
|
|
989
|
+
if (typeNode.type === "type_identifier") {
|
|
990
|
+
names.push(typeNode.text);
|
|
991
|
+
} else if (typeNode.type === "pointer_type") {
|
|
992
|
+
for (const sub of typeNode.children) {
|
|
993
|
+
if (sub.type === "type_identifier") {
|
|
994
|
+
names.push(sub.text);
|
|
995
|
+
break;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
} else if (typeNode.type === "qualified_type") {
|
|
999
|
+
names.push(typeNode.text);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
return names;
|
|
1007
|
+
}
|
|
1008
|
+
function extractInterfaceMethods(node) {
|
|
1009
|
+
const methods = [];
|
|
1010
|
+
for (const child of node.children) {
|
|
1011
|
+
if (child.type === "method_elem") {
|
|
1012
|
+
const nameNode = child.childForFieldName("name");
|
|
1013
|
+
if (nameNode) {
|
|
1014
|
+
const paramsNode = child.childForFieldName("parameters");
|
|
1015
|
+
methods.push({
|
|
1016
|
+
name: nameNode.text,
|
|
1017
|
+
kind: "function",
|
|
1018
|
+
startLine: child.startPosition.row + 1,
|
|
1019
|
+
endLine: child.endPosition.row + 1,
|
|
1020
|
+
signature: paramsNode ? paramsNode.text : null,
|
|
1021
|
+
children: [],
|
|
1022
|
+
calls: [],
|
|
1023
|
+
receiverVar: null,
|
|
1024
|
+
receiverType: null,
|
|
1025
|
+
paramTypes: null
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
return methods;
|
|
1031
|
+
}
|
|
1032
|
+
function extractFunction$1(node) {
|
|
1033
|
+
const nameNode = node.childForFieldName("name");
|
|
1034
|
+
if (!nameNode) return null;
|
|
1035
|
+
const paramsNode = node.childForFieldName("parameters");
|
|
1036
|
+
const signature = paramsNode ? paramsNode.text : null;
|
|
1037
|
+
const bodyNode = node.childForFieldName("body");
|
|
1038
|
+
const calls = bodyNode ? collectCalls(bodyNode) : [];
|
|
1039
|
+
return {
|
|
1040
|
+
name: nameNode.text,
|
|
1041
|
+
kind: "function",
|
|
1042
|
+
startLine: node.startPosition.row + 1,
|
|
1043
|
+
endLine: node.endPosition.row + 1,
|
|
1044
|
+
signature,
|
|
1045
|
+
children: [],
|
|
1046
|
+
calls,
|
|
1047
|
+
receiverVar: null,
|
|
1048
|
+
receiverType: null,
|
|
1049
|
+
paramTypes: null
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
function extractMethod(node) {
|
|
1053
|
+
const nameNode = node.childForFieldName("name");
|
|
1054
|
+
if (!nameNode) return null;
|
|
1055
|
+
const paramsNode = node.childForFieldName("parameters");
|
|
1056
|
+
const receiverNode = node.childForFieldName("receiver");
|
|
1057
|
+
const parts = [];
|
|
1058
|
+
let receiverVar = null;
|
|
1059
|
+
let receiverType = null;
|
|
1060
|
+
if (receiverNode) {
|
|
1061
|
+
parts.push(receiverNode.text);
|
|
1062
|
+
[receiverVar, receiverType] = parseReceiver(receiverNode);
|
|
1063
|
+
}
|
|
1064
|
+
if (paramsNode) {
|
|
1065
|
+
parts.push(paramsNode.text);
|
|
1066
|
+
}
|
|
1067
|
+
const signature = parts.length > 0 ? parts.join(" ") : null;
|
|
1068
|
+
const bodyNode = node.childForFieldName("body");
|
|
1069
|
+
const calls = bodyNode ? collectCalls(bodyNode) : [];
|
|
1070
|
+
return {
|
|
1071
|
+
name: nameNode.text,
|
|
1072
|
+
kind: "function",
|
|
1073
|
+
startLine: node.startPosition.row + 1,
|
|
1074
|
+
endLine: node.endPosition.row + 1,
|
|
1075
|
+
signature,
|
|
1076
|
+
children: [],
|
|
1077
|
+
calls,
|
|
1078
|
+
receiverVar,
|
|
1079
|
+
receiverType,
|
|
1080
|
+
paramTypes: null
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
function parseReceiver(receiverNode) {
|
|
1084
|
+
for (const child of receiverNode.children) {
|
|
1085
|
+
if (child.type === "parameter_declaration") {
|
|
1086
|
+
let varName = null;
|
|
1087
|
+
let typeName = null;
|
|
1088
|
+
const nameChild = child.childForFieldName("name");
|
|
1089
|
+
if (nameChild) varName = nameChild.text;
|
|
1090
|
+
const typeChild = child.childForFieldName("type");
|
|
1091
|
+
if (typeChild) {
|
|
1092
|
+
if (typeChild.type === "pointer_type") {
|
|
1093
|
+
for (const sub of typeChild.children) {
|
|
1094
|
+
if (sub.type === "type_identifier") {
|
|
1095
|
+
typeName = sub.text;
|
|
1096
|
+
break;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
} else if (typeChild.type === "type_identifier") {
|
|
1100
|
+
typeName = typeChild.text;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
return [varName, typeName];
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
return [null, null];
|
|
1107
|
+
}
|
|
1108
|
+
function collectCalls(node) {
|
|
1109
|
+
const calls = [];
|
|
1110
|
+
for (const child of node.children) {
|
|
1111
|
+
if (child.type === "call_expression") {
|
|
1112
|
+
const funcNode = child.childForFieldName("function");
|
|
1113
|
+
if (funcNode && funcNode.type === "identifier") {
|
|
1114
|
+
calls.push({ name: funcNode.text, receiver: null, kind: "bare" });
|
|
1115
|
+
} else if (funcNode && funcNode.type === "selector_expression") {
|
|
1116
|
+
const operand = funcNode.childForFieldName("operand");
|
|
1117
|
+
const field = funcNode.childForFieldName("field");
|
|
1118
|
+
if (operand && field) {
|
|
1119
|
+
calls.push({
|
|
1120
|
+
name: field.text,
|
|
1121
|
+
receiver: operand.text,
|
|
1122
|
+
kind: "attribute"
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
calls.push(...collectCalls(child));
|
|
1128
|
+
}
|
|
1129
|
+
return calls;
|
|
1130
|
+
}
|
|
1131
|
+
const SUBTYPE_MAP = {
|
|
1132
|
+
// C/C++
|
|
1133
|
+
struct_specifier: "struct",
|
|
1134
|
+
enum_specifier: "enum",
|
|
1135
|
+
// C#
|
|
1136
|
+
struct_declaration: "struct",
|
|
1137
|
+
interface_declaration: "interface",
|
|
1138
|
+
enum_declaration: "enum",
|
|
1139
|
+
// Kotlin
|
|
1140
|
+
object_declaration: "object",
|
|
1141
|
+
// Ruby
|
|
1142
|
+
module: "module",
|
|
1143
|
+
// Rust
|
|
1144
|
+
struct_item: "struct",
|
|
1145
|
+
enum_item: "enum",
|
|
1146
|
+
trait_item: "trait",
|
|
1147
|
+
// Swift
|
|
1148
|
+
protocol_declaration: "protocol"
|
|
1149
|
+
};
|
|
1150
|
+
const LANGUAGE_CONFIGS = {
|
|
1151
|
+
c: {
|
|
1152
|
+
classTypes: /* @__PURE__ */ new Set(["struct_specifier", "enum_specifier"]),
|
|
1153
|
+
functionTypes: /* @__PURE__ */ new Set(["function_definition"]),
|
|
1154
|
+
containerTypes: /* @__PURE__ */ new Set()
|
|
1155
|
+
},
|
|
1156
|
+
cpp: {
|
|
1157
|
+
classTypes: /* @__PURE__ */ new Set([
|
|
1158
|
+
"class_specifier",
|
|
1159
|
+
"struct_specifier",
|
|
1160
|
+
"enum_specifier"
|
|
1161
|
+
]),
|
|
1162
|
+
functionTypes: /* @__PURE__ */ new Set(["function_definition"]),
|
|
1163
|
+
containerTypes: /* @__PURE__ */ new Set(["class_specifier", "namespace_definition"])
|
|
1164
|
+
},
|
|
1165
|
+
csharp: {
|
|
1166
|
+
classTypes: /* @__PURE__ */ new Set([
|
|
1167
|
+
"class_declaration",
|
|
1168
|
+
"interface_declaration",
|
|
1169
|
+
"struct_declaration",
|
|
1170
|
+
"enum_declaration"
|
|
1171
|
+
]),
|
|
1172
|
+
functionTypes: /* @__PURE__ */ new Set(["method_declaration", "local_function_statement"]),
|
|
1173
|
+
containerTypes: /* @__PURE__ */ new Set([
|
|
1174
|
+
"class_declaration",
|
|
1175
|
+
"interface_declaration",
|
|
1176
|
+
"namespace_declaration"
|
|
1177
|
+
])
|
|
1178
|
+
},
|
|
1179
|
+
java: {
|
|
1180
|
+
classTypes: /* @__PURE__ */ new Set([
|
|
1181
|
+
"class_declaration",
|
|
1182
|
+
"interface_declaration",
|
|
1183
|
+
"enum_declaration"
|
|
1184
|
+
]),
|
|
1185
|
+
functionTypes: /* @__PURE__ */ new Set(["method_declaration", "constructor_declaration"]),
|
|
1186
|
+
containerTypes: /* @__PURE__ */ new Set(["class_declaration", "interface_declaration"])
|
|
1187
|
+
},
|
|
1188
|
+
kotlin: {
|
|
1189
|
+
classTypes: /* @__PURE__ */ new Set(["class_declaration", "object_declaration"]),
|
|
1190
|
+
functionTypes: /* @__PURE__ */ new Set(["function_declaration"]),
|
|
1191
|
+
containerTypes: /* @__PURE__ */ new Set(["class_declaration", "object_declaration"])
|
|
1192
|
+
},
|
|
1193
|
+
ruby: {
|
|
1194
|
+
classTypes: /* @__PURE__ */ new Set(["class", "module"]),
|
|
1195
|
+
functionTypes: /* @__PURE__ */ new Set(["method", "singleton_method"]),
|
|
1196
|
+
containerTypes: /* @__PURE__ */ new Set(["class", "module"])
|
|
1197
|
+
},
|
|
1198
|
+
rust: {
|
|
1199
|
+
classTypes: /* @__PURE__ */ new Set(["struct_item", "enum_item", "trait_item"]),
|
|
1200
|
+
functionTypes: /* @__PURE__ */ new Set(["function_item", "function_signature_item"]),
|
|
1201
|
+
containerTypes: /* @__PURE__ */ new Set(["impl_item", "trait_item"])
|
|
1202
|
+
},
|
|
1203
|
+
swift: {
|
|
1204
|
+
classTypes: /* @__PURE__ */ new Set([
|
|
1205
|
+
"class_declaration",
|
|
1206
|
+
"protocol_declaration",
|
|
1207
|
+
"struct_declaration",
|
|
1208
|
+
"enum_declaration"
|
|
1209
|
+
]),
|
|
1210
|
+
functionTypes: /* @__PURE__ */ new Set(["function_declaration", "init_declaration"]),
|
|
1211
|
+
containerTypes: /* @__PURE__ */ new Set([
|
|
1212
|
+
"class_declaration",
|
|
1213
|
+
"protocol_declaration",
|
|
1214
|
+
"struct_declaration"
|
|
1215
|
+
])
|
|
1216
|
+
}
|
|
1217
|
+
};
|
|
1218
|
+
function extractPrecedingDoc(node) {
|
|
1219
|
+
const prev = node.previousNamedSibling;
|
|
1220
|
+
if (!prev) return void 0;
|
|
1221
|
+
if ((prev.type === "comment" || prev.type === "block_comment") && prev.text.startsWith("/**")) {
|
|
1222
|
+
const cleaned = prev.text.replace(/^\/\*\*\s*/, "").replace(/\s*\*\/$/, "").split("\n").map((line) => line.replace(/^\s*\*\s?/, "")).join("\n").trim();
|
|
1223
|
+
return cleaned || void 0;
|
|
1224
|
+
}
|
|
1225
|
+
if (prev.type === "comment" || prev.type === "line_comment") {
|
|
1226
|
+
const comments = [];
|
|
1227
|
+
let cur = prev;
|
|
1228
|
+
while (cur && (cur.type === "comment" || cur.type === "line_comment")) {
|
|
1229
|
+
comments.push(cur.text);
|
|
1230
|
+
cur = cur.previousNamedSibling;
|
|
1231
|
+
}
|
|
1232
|
+
const cleaned = comments.reverse().map((line) => line.replace(/^\/\/\/?\s?/, "")).join("\n").trim();
|
|
1233
|
+
return cleaned || void 0;
|
|
1234
|
+
}
|
|
1235
|
+
return void 0;
|
|
1236
|
+
}
|
|
1237
|
+
function extractGeneric(rootNode, language) {
|
|
1238
|
+
const config = LANGUAGE_CONFIGS[language];
|
|
1239
|
+
if (!config) {
|
|
1240
|
+
return { symbols: [], language, rootNode };
|
|
1241
|
+
}
|
|
1242
|
+
const symbols = walkTopLevel(rootNode, config);
|
|
1243
|
+
return { symbols, language, rootNode };
|
|
1244
|
+
}
|
|
1245
|
+
function walkTopLevel(node, config) {
|
|
1246
|
+
const symbols = [];
|
|
1247
|
+
for (const child of node.children) {
|
|
1248
|
+
const sym = extractNode(child, config);
|
|
1249
|
+
if (sym) {
|
|
1250
|
+
symbols.push(sym);
|
|
1251
|
+
} else if (config.containerTypes.has(child.type)) {
|
|
1252
|
+
const body = findBody(child);
|
|
1253
|
+
if (body) {
|
|
1254
|
+
symbols.push(...walkTopLevel(body, config));
|
|
1255
|
+
}
|
|
1256
|
+
} else {
|
|
1257
|
+
symbols.push(...walkTopLevel(child, config));
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
return symbols;
|
|
1261
|
+
}
|
|
1262
|
+
function extractNode(node, config) {
|
|
1263
|
+
let sym = null;
|
|
1264
|
+
if (config.classTypes.has(node.type)) {
|
|
1265
|
+
sym = extractClass(node, config);
|
|
1266
|
+
} else if (config.functionTypes.has(node.type)) {
|
|
1267
|
+
sym = extractFunction(node);
|
|
1268
|
+
}
|
|
1269
|
+
if (sym) {
|
|
1270
|
+
sym.docs = extractPrecedingDoc(node);
|
|
1271
|
+
}
|
|
1272
|
+
return sym;
|
|
1273
|
+
}
|
|
1274
|
+
function extractClass(node, config) {
|
|
1275
|
+
const name = extractName(node);
|
|
1276
|
+
if (!name) return null;
|
|
1277
|
+
const body = findBody(node);
|
|
1278
|
+
const children = body ? extractMethods(body, config) : [];
|
|
1279
|
+
const subtype = SUBTYPE_MAP[node.type];
|
|
1280
|
+
const { superclasses, interfaces } = extractInheritance(node);
|
|
1281
|
+
return {
|
|
1282
|
+
name,
|
|
1283
|
+
kind: "class",
|
|
1284
|
+
startLine: node.startPosition.row + 1,
|
|
1285
|
+
endLine: node.endPosition.row + 1,
|
|
1286
|
+
signature: null,
|
|
1287
|
+
children,
|
|
1288
|
+
calls: [],
|
|
1289
|
+
receiverVar: null,
|
|
1290
|
+
receiverType: null,
|
|
1291
|
+
paramTypes: null,
|
|
1292
|
+
subtype,
|
|
1293
|
+
superclasses: superclasses.length > 0 ? superclasses : void 0,
|
|
1294
|
+
interfaces: interfaces.length > 0 ? interfaces : void 0
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
function extractFunction(node) {
|
|
1298
|
+
const name = extractName(node);
|
|
1299
|
+
if (!name) return null;
|
|
1300
|
+
let paramsNode = node.childForFieldName("parameters");
|
|
1301
|
+
if (!paramsNode) {
|
|
1302
|
+
const decl = node.childForFieldName("declarator");
|
|
1303
|
+
if (decl?.type === "function_declarator") {
|
|
1304
|
+
paramsNode = decl.childForFieldName("parameters");
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
const signature = paramsNode ? paramsNode.text : null;
|
|
1308
|
+
return {
|
|
1309
|
+
name,
|
|
1310
|
+
kind: "function",
|
|
1311
|
+
startLine: node.startPosition.row + 1,
|
|
1312
|
+
endLine: node.endPosition.row + 1,
|
|
1313
|
+
signature,
|
|
1314
|
+
children: [],
|
|
1315
|
+
calls: [],
|
|
1316
|
+
receiverVar: null,
|
|
1317
|
+
receiverType: null,
|
|
1318
|
+
paramTypes: null
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
function extractInheritance(node) {
|
|
1322
|
+
const superclasses = [];
|
|
1323
|
+
const interfaces = [];
|
|
1324
|
+
const superclassField = node.childForFieldName("superclass");
|
|
1325
|
+
if (superclassField) {
|
|
1326
|
+
for (const child of superclassField.children) {
|
|
1327
|
+
if (child.type === "type_identifier" || child.type === "constant" || child.type === "scope_resolution") {
|
|
1328
|
+
superclasses.push(child.text);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
if (superclasses.length === 0) {
|
|
1332
|
+
const names = collectTypeIdentifiers(superclassField);
|
|
1333
|
+
superclasses.push(...names);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
const interfacesField = node.childForFieldName("interfaces");
|
|
1337
|
+
if (interfacesField) {
|
|
1338
|
+
interfaces.push(...collectTypeIdentifiers(interfacesField));
|
|
1339
|
+
}
|
|
1340
|
+
for (const child of node.children) {
|
|
1341
|
+
if (child.type === "base_class_clause" || child.type === "base_list" || child.type === "delegation_specifiers" || child.type === "type_inheritance_clause") {
|
|
1342
|
+
superclasses.push(...collectTypeIdentifiers(child));
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
return { superclasses, interfaces };
|
|
1346
|
+
}
|
|
1347
|
+
function collectTypeIdentifiers(node) {
|
|
1348
|
+
const names = [];
|
|
1349
|
+
for (const child of node.children) {
|
|
1350
|
+
if (child.type === "type_identifier" || child.type === "identifier" || child.type === "simple_identifier") {
|
|
1351
|
+
names.push(child.text);
|
|
1352
|
+
} else if (child.namedChildCount > 0) {
|
|
1353
|
+
names.push(...collectTypeIdentifiers(child));
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
return names;
|
|
1357
|
+
}
|
|
1358
|
+
function extractMethods(body, config) {
|
|
1359
|
+
const methods = [];
|
|
1360
|
+
for (const child of body.children) {
|
|
1361
|
+
if (config.functionTypes.has(child.type)) {
|
|
1362
|
+
const sym = extractFunction(child);
|
|
1363
|
+
if (sym) methods.push(sym);
|
|
1364
|
+
} else if (config.classTypes.has(child.type)) {
|
|
1365
|
+
const sym = extractClass(child, config);
|
|
1366
|
+
if (sym) methods.push(sym);
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
return methods;
|
|
1370
|
+
}
|
|
1371
|
+
function extractName(node) {
|
|
1372
|
+
const nameNode = node.childForFieldName("name");
|
|
1373
|
+
if (nameNode) {
|
|
1374
|
+
return nameNode.text;
|
|
1375
|
+
}
|
|
1376
|
+
const declarator = node.childForFieldName("declarator");
|
|
1377
|
+
if (declarator) {
|
|
1378
|
+
if (declarator.type === "function_declarator") {
|
|
1379
|
+
const inner = declarator.childForFieldName("declarator");
|
|
1380
|
+
if (inner) return inner.text;
|
|
1381
|
+
}
|
|
1382
|
+
if (declarator.type === "identifier" || declarator.type === "field_identifier") {
|
|
1383
|
+
return declarator.text;
|
|
1384
|
+
}
|
|
1385
|
+
const qName = declarator.childForFieldName("name");
|
|
1386
|
+
if (qName) return qName.text;
|
|
1387
|
+
return declarator.text;
|
|
1388
|
+
}
|
|
1389
|
+
for (const child of node.children) {
|
|
1390
|
+
if (child.type === "identifier" || child.type === "type_identifier" || child.type === "simple_identifier" || child.type === "constant") {
|
|
1391
|
+
return child.text;
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
return null;
|
|
1395
|
+
}
|
|
1396
|
+
function findBody(node) {
|
|
1397
|
+
const body = node.childForFieldName("body");
|
|
1398
|
+
if (body) return body;
|
|
1399
|
+
for (const child of node.children) {
|
|
1400
|
+
if (child.type === "field_declaration_list" || child.type === "declaration_list" || child.type === "class_body" || child.type === "enum_body" || child.type === "interface_body" || child.type === "block") {
|
|
1401
|
+
return child;
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
return null;
|
|
1405
|
+
}
|
|
1406
|
+
let parsers = /* @__PURE__ */ new Map();
|
|
1407
|
+
function initParsers(map) {
|
|
1408
|
+
parsers = map;
|
|
1409
|
+
}
|
|
1410
|
+
function getExtractor(language) {
|
|
1411
|
+
switch (language) {
|
|
1412
|
+
case "python":
|
|
1413
|
+
return extractPython;
|
|
1414
|
+
case "typescript":
|
|
1415
|
+
case "javascript":
|
|
1416
|
+
return (rootNode) => extractTypeScript(rootNode, language);
|
|
1417
|
+
case "go":
|
|
1418
|
+
return extractGo;
|
|
1419
|
+
case "c":
|
|
1420
|
+
case "cpp":
|
|
1421
|
+
case "csharp":
|
|
1422
|
+
case "java":
|
|
1423
|
+
case "kotlin":
|
|
1424
|
+
case "ruby":
|
|
1425
|
+
case "rust":
|
|
1426
|
+
case "swift":
|
|
1427
|
+
return (rootNode) => extractGeneric(rootNode, language);
|
|
1428
|
+
default:
|
|
1429
|
+
return null;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
function getParserForLanguage(language, ext) {
|
|
1433
|
+
if (language === "typescript" && ext === ".tsx") {
|
|
1434
|
+
return parsers.get("tsx") ?? parsers.get("typescript");
|
|
1435
|
+
}
|
|
1436
|
+
if (language === "javascript" && ext === ".jsx") {
|
|
1437
|
+
return parsers.get("jsx") ?? parsers.get("javascript");
|
|
1438
|
+
}
|
|
1439
|
+
return parsers.get(language);
|
|
1440
|
+
}
|
|
1441
|
+
function parseFile(filePath, fileContent, repoId) {
|
|
1442
|
+
const ext = getExtension(filePath);
|
|
1443
|
+
const language = detectLanguage(ext);
|
|
1444
|
+
if (!language) {
|
|
1445
|
+
return {
|
|
1446
|
+
nodes: [],
|
|
1447
|
+
relationships: [],
|
|
1448
|
+
classesExtracted: 0,
|
|
1449
|
+
functionsExtracted: 0
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
const parser = getParserForLanguage(language, ext);
|
|
1453
|
+
if (!parser) {
|
|
1454
|
+
return {
|
|
1455
|
+
nodes: [],
|
|
1456
|
+
relationships: [],
|
|
1457
|
+
classesExtracted: 0,
|
|
1458
|
+
functionsExtracted: 0
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1461
|
+
const extractor = getExtractor(language);
|
|
1462
|
+
if (!extractor) {
|
|
1463
|
+
return {
|
|
1464
|
+
nodes: [],
|
|
1465
|
+
relationships: [],
|
|
1466
|
+
classesExtracted: 0,
|
|
1467
|
+
functionsExtracted: 0
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
try {
|
|
1471
|
+
const tree = parser.parse(fileContent);
|
|
1472
|
+
if (!tree) {
|
|
1473
|
+
return {
|
|
1474
|
+
nodes: [],
|
|
1475
|
+
relationships: [],
|
|
1476
|
+
classesExtracted: 0,
|
|
1477
|
+
functionsExtracted: 0,
|
|
1478
|
+
error: `${filePath}: parse returned null`
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
const result = extractor(tree.rootNode);
|
|
1482
|
+
const fileId = `${repoId}/${filePath}`;
|
|
1483
|
+
const nodes = [];
|
|
1484
|
+
const rels = [];
|
|
1485
|
+
const stats = { classesExtracted: 0, functionsExtracted: 0 };
|
|
1486
|
+
for (const sym of result.symbols) {
|
|
1487
|
+
convertSymbol(sym, fileId, nodes, rels, stats);
|
|
1488
|
+
}
|
|
1489
|
+
return { nodes, relationships: rels, ...stats };
|
|
1490
|
+
} catch (err) {
|
|
1491
|
+
return {
|
|
1492
|
+
nodes: [],
|
|
1493
|
+
relationships: [],
|
|
1494
|
+
classesExtracted: 0,
|
|
1495
|
+
functionsExtracted: 0,
|
|
1496
|
+
error: `${filePath}: ${err instanceof Error ? err.message : String(err)}`
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
function processSymbol(symbol, parentId, language, registries, callInfos, nodes, rels, emittedNodeIds) {
|
|
1501
|
+
const fileId = parentId.split("::")[0];
|
|
1502
|
+
const namePart = symbol.receiverType ? `${symbol.receiverType}.${symbol.name}` : symbol.name;
|
|
1503
|
+
const nodeId = `${parentId}::${namePart}`;
|
|
1504
|
+
if (emittedNodeIds.has(nodeId)) {
|
|
1505
|
+
return null;
|
|
1506
|
+
}
|
|
1507
|
+
emittedNodeIds.add(nodeId);
|
|
1508
|
+
if (symbol.kind === "class") {
|
|
1509
|
+
const props = {
|
|
1510
|
+
language,
|
|
1511
|
+
start_line: symbol.startLine,
|
|
1512
|
+
end_line: symbol.endLine
|
|
1513
|
+
};
|
|
1514
|
+
if (symbol.signature) props.signature = symbol.signature;
|
|
1515
|
+
if (symbol.superclasses) props.superclasses = symbol.superclasses;
|
|
1516
|
+
if (symbol.interfaces) props.interfaces = symbol.interfaces;
|
|
1517
|
+
if (symbol.subtype) props.subtype = symbol.subtype;
|
|
1518
|
+
if (symbol.docs) props.docs = symbol.docs;
|
|
1519
|
+
nodes.push({
|
|
1520
|
+
id: nodeId,
|
|
1521
|
+
type: "Class",
|
|
1522
|
+
name: symbol.name,
|
|
1523
|
+
properties: props
|
|
1524
|
+
});
|
|
1525
|
+
rels.push({
|
|
1526
|
+
id: `${nodeId}->DEFINED_IN->${parentId}`,
|
|
1527
|
+
type: "DEFINED_IN",
|
|
1528
|
+
source_id: nodeId,
|
|
1529
|
+
target_id: parentId
|
|
1530
|
+
});
|
|
1531
|
+
const symbolNode = {
|
|
1532
|
+
id: nodeId,
|
|
1533
|
+
name: symbol.name,
|
|
1534
|
+
kind: "class",
|
|
1535
|
+
fileId,
|
|
1536
|
+
parentId,
|
|
1537
|
+
receiverVar: null,
|
|
1538
|
+
receiverType: null,
|
|
1539
|
+
paramTypes: null,
|
|
1540
|
+
children: []
|
|
1541
|
+
};
|
|
1542
|
+
addToRegistry(registries.nameRegistry, symbol.name, symbolNode);
|
|
1543
|
+
registries.fileRegistry.get(fileId)?.set(symbol.name, symbolNode);
|
|
1544
|
+
addToRegistry(registries.classRegistry, symbol.name, symbolNode);
|
|
1545
|
+
for (const child of symbol.children) {
|
|
1546
|
+
const childSymbolNode = processSymbol(
|
|
1547
|
+
child,
|
|
1548
|
+
nodeId,
|
|
1549
|
+
language,
|
|
1550
|
+
registries,
|
|
1551
|
+
callInfos,
|
|
1552
|
+
nodes,
|
|
1553
|
+
rels,
|
|
1554
|
+
emittedNodeIds
|
|
1555
|
+
);
|
|
1556
|
+
if (childSymbolNode) symbolNode.children.push(childSymbolNode);
|
|
1557
|
+
}
|
|
1558
|
+
return symbolNode;
|
|
1559
|
+
} else {
|
|
1560
|
+
const props = {
|
|
1561
|
+
language,
|
|
1562
|
+
start_line: symbol.startLine,
|
|
1563
|
+
end_line: symbol.endLine
|
|
1564
|
+
};
|
|
1565
|
+
if (symbol.signature) props.signature = symbol.signature;
|
|
1566
|
+
if (symbol.docs) props.docs = symbol.docs;
|
|
1567
|
+
nodes.push({
|
|
1568
|
+
id: nodeId,
|
|
1569
|
+
type: "Function",
|
|
1570
|
+
name: symbol.name,
|
|
1571
|
+
properties: props
|
|
1572
|
+
});
|
|
1573
|
+
rels.push({
|
|
1574
|
+
id: `${nodeId}->DEFINED_IN->${parentId}`,
|
|
1575
|
+
type: "DEFINED_IN",
|
|
1576
|
+
source_id: nodeId,
|
|
1577
|
+
target_id: parentId
|
|
1578
|
+
});
|
|
1579
|
+
const symbolNode = {
|
|
1580
|
+
id: nodeId,
|
|
1581
|
+
name: symbol.name,
|
|
1582
|
+
kind: "function",
|
|
1583
|
+
fileId,
|
|
1584
|
+
parentId,
|
|
1585
|
+
receiverVar: symbol.receiverVar,
|
|
1586
|
+
receiverType: symbol.receiverType,
|
|
1587
|
+
paramTypes: symbol.paramTypes,
|
|
1588
|
+
children: []
|
|
1589
|
+
};
|
|
1590
|
+
addToRegistry(registries.nameRegistry, symbol.name, symbolNode);
|
|
1591
|
+
registries.fileRegistry.get(fileId)?.set(symbol.name, symbolNode);
|
|
1592
|
+
if (symbol.calls.length > 0) {
|
|
1593
|
+
callInfos.push({
|
|
1594
|
+
callerNode: symbolNode,
|
|
1595
|
+
calls: symbol.calls,
|
|
1596
|
+
fileId
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1599
|
+
return symbolNode;
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
function addToRegistry(registry, name, node) {
|
|
1603
|
+
const existing = registry.get(name);
|
|
1604
|
+
if (existing) {
|
|
1605
|
+
existing.push(node);
|
|
1606
|
+
} else {
|
|
1607
|
+
registry.set(name, [node]);
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
function countSymbols(symbols) {
|
|
1611
|
+
let classes = 0;
|
|
1612
|
+
let functions = 0;
|
|
1613
|
+
for (const sym of symbols) {
|
|
1614
|
+
if (sym.kind === "class") {
|
|
1615
|
+
classes++;
|
|
1616
|
+
const childCounts = countSymbols(sym.children);
|
|
1617
|
+
classes += childCounts.classes;
|
|
1618
|
+
functions += childCounts.functions;
|
|
1619
|
+
} else {
|
|
1620
|
+
functions++;
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
return { classes, functions };
|
|
1624
|
+
}
|
|
1625
|
+
function convertSymbol(sym, parentId, nodes, rels, stats) {
|
|
1626
|
+
const namePart = sym.receiverType ? `${sym.receiverType}.${sym.name}` : sym.name;
|
|
1627
|
+
const symId = `${parentId}::${namePart}`;
|
|
1628
|
+
const nodeType = sym.kind === "class" ? "Class" : "Function";
|
|
1629
|
+
const props = {
|
|
1630
|
+
start_line: sym.startLine,
|
|
1631
|
+
end_line: sym.endLine
|
|
1632
|
+
};
|
|
1633
|
+
if (sym.signature) props.signature = sym.signature;
|
|
1634
|
+
if (sym.docs) props.docs = sym.docs;
|
|
1635
|
+
if (sym.superclasses) props.superclasses = sym.superclasses;
|
|
1636
|
+
if (sym.interfaces) props.interfaces = sym.interfaces;
|
|
1637
|
+
if (sym.subtype) props.subtype = sym.subtype;
|
|
1638
|
+
nodes.push({
|
|
1639
|
+
id: symId,
|
|
1640
|
+
type: nodeType,
|
|
1641
|
+
name: sym.name,
|
|
1642
|
+
properties: props
|
|
1643
|
+
});
|
|
1644
|
+
rels.push({
|
|
1645
|
+
id: `${symId}->DEFINED_IN->${parentId}`,
|
|
1646
|
+
type: "DEFINED_IN",
|
|
1647
|
+
source_id: symId,
|
|
1648
|
+
target_id: parentId
|
|
1649
|
+
});
|
|
1650
|
+
if (sym.kind === "class") {
|
|
1651
|
+
stats.classesExtracted++;
|
|
1652
|
+
for (const child of sym.children) {
|
|
1653
|
+
if (child.kind === "function" && child.name === sym.name) continue;
|
|
1654
|
+
convertSymbol(child, symId, nodes, rels, stats);
|
|
1655
|
+
}
|
|
1656
|
+
} else {
|
|
1657
|
+
stats.functionsExtracted++;
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
function analyzePythonImports(rootNode, filePath, knownFiles) {
|
|
1661
|
+
const internal = {};
|
|
1662
|
+
const external = {};
|
|
1663
|
+
const fileDir = parentDir(filePath);
|
|
1664
|
+
for (const child of rootNode.children) {
|
|
1665
|
+
if (child.type === "import_statement") {
|
|
1666
|
+
parsePythonImport(child, knownFiles, internal, external);
|
|
1667
|
+
} else if (child.type === "import_from_statement") {
|
|
1668
|
+
parsePythonFromImport(child, fileDir, knownFiles, internal, external);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
return { internal, external };
|
|
1672
|
+
}
|
|
1673
|
+
function parsePythonImport(node, knownFiles, internal, external) {
|
|
1674
|
+
for (const child of node.children) {
|
|
1675
|
+
if (child.type === "dotted_name") {
|
|
1676
|
+
const moduleName = child.text;
|
|
1677
|
+
const candidates = moduleToPaths(moduleName);
|
|
1678
|
+
let found = false;
|
|
1679
|
+
for (const candidate of candidates) {
|
|
1680
|
+
if (knownFiles.has(candidate)) {
|
|
1681
|
+
const parts = moduleName.split(".");
|
|
1682
|
+
internal[parts[parts.length - 1]] = candidate;
|
|
1683
|
+
found = true;
|
|
1684
|
+
break;
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
if (!found) {
|
|
1688
|
+
const topLevel = moduleName.split(".")[0];
|
|
1689
|
+
const name = normalizePyName(topLevel);
|
|
1690
|
+
external[name] = packageId("pypi", name);
|
|
1691
|
+
}
|
|
1692
|
+
} else if (child.type === "aliased_import") {
|
|
1693
|
+
const nameNode = child.childForFieldName("name");
|
|
1694
|
+
const aliasNode = child.childForFieldName("alias");
|
|
1695
|
+
if (nameNode && aliasNode) {
|
|
1696
|
+
const moduleName = nameNode.text;
|
|
1697
|
+
const alias = aliasNode.text;
|
|
1698
|
+
const candidates = moduleToPaths(moduleName);
|
|
1699
|
+
let found = false;
|
|
1700
|
+
for (const candidate of candidates) {
|
|
1701
|
+
if (knownFiles.has(candidate)) {
|
|
1702
|
+
internal[alias] = candidate;
|
|
1703
|
+
found = true;
|
|
1704
|
+
break;
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
if (!found) {
|
|
1708
|
+
const topLevel = moduleName.split(".")[0];
|
|
1709
|
+
const name = normalizePyName(topLevel);
|
|
1710
|
+
external[name] = packageId("pypi", name);
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
function parsePythonFromImport(node, fileDir, knownFiles, internal, external) {
|
|
1717
|
+
const moduleNameNode = node.childForFieldName("module_name");
|
|
1718
|
+
if (!moduleNameNode) return;
|
|
1719
|
+
let moduleText = moduleNameNode.text;
|
|
1720
|
+
let isRelative = false;
|
|
1721
|
+
let baseDir = fileDir;
|
|
1722
|
+
for (const child of node.children) {
|
|
1723
|
+
if (child.type === "relative_import") {
|
|
1724
|
+
isRelative = true;
|
|
1725
|
+
for (const sub of child.children) {
|
|
1726
|
+
if (sub.type === "dotted_name") {
|
|
1727
|
+
moduleText = sub.text;
|
|
1728
|
+
} else if (sub.type === "import_prefix") {
|
|
1729
|
+
const dots = sub.text;
|
|
1730
|
+
baseDir = fileDir;
|
|
1731
|
+
for (let i = 0; i < dots.length - 1; i++) {
|
|
1732
|
+
baseDir = parentDir(baseDir);
|
|
1733
|
+
}
|
|
1734
|
+
if (moduleText === moduleNameNode.text && moduleText.startsWith(".")) {
|
|
1735
|
+
moduleText = "";
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
break;
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
let candidates;
|
|
1743
|
+
if (isRelative) {
|
|
1744
|
+
const basePath = moduleText ? baseDir ? `${baseDir}/${moduleText.replace(/\./g, "/")}` : moduleText.replace(/\./g, "/") : baseDir;
|
|
1745
|
+
candidates = [`${basePath}.py`, `${basePath}/__init__.py`];
|
|
1746
|
+
} else {
|
|
1747
|
+
candidates = moduleToPaths(moduleText);
|
|
1748
|
+
}
|
|
1749
|
+
let found = false;
|
|
1750
|
+
let resolvedPath = null;
|
|
1751
|
+
for (const candidate of candidates) {
|
|
1752
|
+
if (knownFiles.has(candidate)) {
|
|
1753
|
+
const alias = moduleText ? moduleText.split(".").pop() : "";
|
|
1754
|
+
if (alias) {
|
|
1755
|
+
internal[alias] = candidate;
|
|
1756
|
+
}
|
|
1757
|
+
resolvedPath = candidate;
|
|
1758
|
+
found = true;
|
|
1759
|
+
break;
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
if (resolvedPath) {
|
|
1763
|
+
for (const child of node.children) {
|
|
1764
|
+
if (child.type === "dotted_name" && child !== node.childForFieldName("module_name")) {
|
|
1765
|
+
internal[child.text] = resolvedPath;
|
|
1766
|
+
} else if (child.type === "aliased_import") {
|
|
1767
|
+
const nameNode = child.childForFieldName("name");
|
|
1768
|
+
const aliasNode = child.childForFieldName("alias");
|
|
1769
|
+
if (aliasNode) {
|
|
1770
|
+
internal[aliasNode.text] = resolvedPath;
|
|
1771
|
+
} else if (nameNode) {
|
|
1772
|
+
internal[nameNode.text] = resolvedPath;
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
if (!found && !isRelative && moduleText) {
|
|
1778
|
+
const topLevel = moduleText.split(".")[0];
|
|
1779
|
+
const name = normalizePyName(topLevel);
|
|
1780
|
+
external[name] = packageId("pypi", name);
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
function buildDirIndex(knownFiles) {
|
|
1784
|
+
const index = /* @__PURE__ */ new Map();
|
|
1785
|
+
const baseDirs = /* @__PURE__ */ new Map();
|
|
1786
|
+
for (const filePath of knownFiles) {
|
|
1787
|
+
if (!filePath.endsWith(".go")) continue;
|
|
1788
|
+
const dir = parentDir(filePath);
|
|
1789
|
+
if (!dir) continue;
|
|
1790
|
+
if (!index.has(dir)) index.set(dir, filePath);
|
|
1791
|
+
const dirBase = dir.split("/").pop();
|
|
1792
|
+
let dirs = baseDirs.get(dirBase);
|
|
1793
|
+
if (!dirs) {
|
|
1794
|
+
dirs = /* @__PURE__ */ new Set();
|
|
1795
|
+
baseDirs.set(dirBase, dirs);
|
|
1796
|
+
}
|
|
1797
|
+
dirs.add(dir);
|
|
1798
|
+
}
|
|
1799
|
+
for (const [dirBase, dirs] of baseDirs) {
|
|
1800
|
+
if (dirs.size === 1) {
|
|
1801
|
+
const dir = dirs.values().next().value;
|
|
1802
|
+
index.set(dirBase, index.get(dir));
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
return index;
|
|
1806
|
+
}
|
|
1807
|
+
let cachedDirIndex = null;
|
|
1808
|
+
let cachedDirIndexSource = null;
|
|
1809
|
+
function getDirIndex(knownFiles) {
|
|
1810
|
+
if (cachedDirIndex && cachedDirIndexSource === knownFiles) {
|
|
1811
|
+
return cachedDirIndex;
|
|
1812
|
+
}
|
|
1813
|
+
cachedDirIndex = buildDirIndex(knownFiles);
|
|
1814
|
+
cachedDirIndexSource = knownFiles;
|
|
1815
|
+
return cachedDirIndex;
|
|
1816
|
+
}
|
|
1817
|
+
function resetDirIndexCache() {
|
|
1818
|
+
cachedDirIndex = null;
|
|
1819
|
+
cachedDirIndexSource = null;
|
|
1820
|
+
}
|
|
1821
|
+
function analyzeGoImports(rootNode, knownFiles, modulePath) {
|
|
1822
|
+
const internal = {};
|
|
1823
|
+
const external = {};
|
|
1824
|
+
const dirIndex = getDirIndex(knownFiles);
|
|
1825
|
+
for (const child of rootNode.children) {
|
|
1826
|
+
if (child.type === "import_declaration") {
|
|
1827
|
+
parseGoImportDecl(child, dirIndex, internal, external, modulePath);
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
return { internal, external };
|
|
1831
|
+
}
|
|
1832
|
+
function parseGoImportDecl(node, dirIndex, internal, external, modulePath) {
|
|
1833
|
+
for (const child of node.children) {
|
|
1834
|
+
if (child.type === "import_spec") {
|
|
1835
|
+
parseGoImportSpec(child, dirIndex, internal, external, modulePath);
|
|
1836
|
+
} else if (child.type === "import_spec_list") {
|
|
1837
|
+
for (const spec of child.children) {
|
|
1838
|
+
if (spec.type === "import_spec") {
|
|
1839
|
+
parseGoImportSpec(spec, dirIndex, internal, external, modulePath);
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
function parseGoImportSpec(node, dirIndex, internal, external, modulePath) {
|
|
1846
|
+
const pathNode = node.childForFieldName("path");
|
|
1847
|
+
const nameNode = node.childForFieldName("name");
|
|
1848
|
+
if (!pathNode) return;
|
|
1849
|
+
const importPath = pathNode.text.replace(/^["']|["']$/g, "");
|
|
1850
|
+
if (!importPath.includes(".")) return;
|
|
1851
|
+
let alias;
|
|
1852
|
+
if (nameNode) {
|
|
1853
|
+
alias = nameNode.text;
|
|
1854
|
+
if (alias === "_" || alias === ".") return;
|
|
1855
|
+
} else {
|
|
1856
|
+
alias = importPath.split("/").pop();
|
|
1857
|
+
}
|
|
1858
|
+
const fullMatch = dirIndex.get(importPath);
|
|
1859
|
+
if (fullMatch) {
|
|
1860
|
+
internal[alias] = fullMatch;
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1863
|
+
if (modulePath && importPath.startsWith(modulePath + "/")) {
|
|
1864
|
+
const repoRelDir = importPath.slice(modulePath.length + 1);
|
|
1865
|
+
const relMatch = dirIndex.get(repoRelDir);
|
|
1866
|
+
if (relMatch) {
|
|
1867
|
+
internal[alias] = relMatch;
|
|
1868
|
+
return;
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
const pkgName = importPath.split("/").pop();
|
|
1872
|
+
const baseMatch = dirIndex.get(pkgName);
|
|
1873
|
+
if (baseMatch) {
|
|
1874
|
+
internal[alias] = baseMatch;
|
|
1875
|
+
return;
|
|
1876
|
+
}
|
|
1877
|
+
const isOwnModule = modulePath && importPath.startsWith(modulePath);
|
|
1878
|
+
if (!isOwnModule) {
|
|
1879
|
+
const goModuleName = goModuleRoot(importPath);
|
|
1880
|
+
external[goModuleName] = packageId("go", goModuleName);
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
function goModuleRoot(importPath) {
|
|
1884
|
+
const parts = importPath.split("/");
|
|
1885
|
+
const host = parts[0];
|
|
1886
|
+
if (host === "github.com" || host === "gitlab.com" || host === "bitbucket.org" || host === "golang.org" || host === "google.golang.org" || host === "gopkg.in") {
|
|
1887
|
+
return parts.slice(0, 3).join("/");
|
|
1888
|
+
}
|
|
1889
|
+
return parts.length >= 3 ? parts.slice(0, 3).join("/") : importPath;
|
|
1890
|
+
}
|
|
1891
|
+
function analyzeTypeScriptImports(rootNode, filePath, knownFiles) {
|
|
1892
|
+
const internal = {};
|
|
1893
|
+
const external = {};
|
|
1894
|
+
const fileDir = parentDir(filePath);
|
|
1895
|
+
for (const child of rootNode.children) {
|
|
1896
|
+
if (child.type === "import_statement") {
|
|
1897
|
+
parseTsImport(child, fileDir, knownFiles, internal, external);
|
|
1898
|
+
} else if (child.type === "export_statement") {
|
|
1899
|
+
parseTsReexport(child, fileDir, knownFiles, internal, external);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
return { internal, external };
|
|
1903
|
+
}
|
|
1904
|
+
function parseTsImport(node, fileDir, knownFiles, internal, external) {
|
|
1905
|
+
const sourceNode = node.childForFieldName("source");
|
|
1906
|
+
if (!sourceNode) return;
|
|
1907
|
+
const sourceText = sourceNode.text.replace(/^["']|["']$/g, "");
|
|
1908
|
+
if (!sourceText.startsWith(".")) {
|
|
1909
|
+
const pkgName = npmPackageName(sourceText);
|
|
1910
|
+
external[pkgName] = packageId("npm", pkgName);
|
|
1911
|
+
return;
|
|
1912
|
+
}
|
|
1913
|
+
const resolved = resolveRelativePath(fileDir, sourceText);
|
|
1914
|
+
const extensions = [
|
|
1915
|
+
"",
|
|
1916
|
+
".ts",
|
|
1917
|
+
".tsx",
|
|
1918
|
+
".js",
|
|
1919
|
+
".jsx",
|
|
1920
|
+
"/index.ts",
|
|
1921
|
+
"/index.tsx",
|
|
1922
|
+
"/index.js"
|
|
1923
|
+
];
|
|
1924
|
+
for (const ext of extensions) {
|
|
1925
|
+
const candidate = resolved + ext;
|
|
1926
|
+
if (knownFiles.has(candidate)) {
|
|
1927
|
+
const alias = sourceText.split("/").pop();
|
|
1928
|
+
internal[alias] = candidate;
|
|
1929
|
+
break;
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
function parseTsReexport(node, fileDir, knownFiles, internal, external) {
|
|
1934
|
+
const sourceNode = node.childForFieldName("source");
|
|
1935
|
+
if (!sourceNode) return;
|
|
1936
|
+
const sourceText = sourceNode.text.replace(/^["']|["']$/g, "");
|
|
1937
|
+
if (!sourceText.startsWith(".")) {
|
|
1938
|
+
const pkgName = npmPackageName(sourceText);
|
|
1939
|
+
external[pkgName] = packageId("npm", pkgName);
|
|
1940
|
+
return;
|
|
1941
|
+
}
|
|
1942
|
+
const resolved = resolveRelativePath(fileDir, sourceText);
|
|
1943
|
+
const extensions = [
|
|
1944
|
+
"",
|
|
1945
|
+
".ts",
|
|
1946
|
+
".tsx",
|
|
1947
|
+
".js",
|
|
1948
|
+
".jsx",
|
|
1949
|
+
"/index.ts",
|
|
1950
|
+
"/index.tsx",
|
|
1951
|
+
"/index.js"
|
|
1952
|
+
];
|
|
1953
|
+
for (const ext of extensions) {
|
|
1954
|
+
const candidate = resolved + ext;
|
|
1955
|
+
if (knownFiles.has(candidate)) {
|
|
1956
|
+
const alias = sourceText.split("/").pop();
|
|
1957
|
+
internal[alias] = candidate;
|
|
1958
|
+
break;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
function analyzeRustImports(rootNode, filePath, knownFiles) {
|
|
1963
|
+
const internal = {};
|
|
1964
|
+
const external = {};
|
|
1965
|
+
const fileDir = parentDir(filePath);
|
|
1966
|
+
for (const child of rootNode.children) {
|
|
1967
|
+
if (child.type === "mod_item") {
|
|
1968
|
+
const nameNode = child.childForFieldName("name");
|
|
1969
|
+
if (!nameNode) continue;
|
|
1970
|
+
const hasBody = child.children.some((c) => c.type === "declaration_list");
|
|
1971
|
+
if (hasBody) continue;
|
|
1972
|
+
const modName = nameNode.text;
|
|
1973
|
+
const candidates = [
|
|
1974
|
+
fileDir ? `${fileDir}/${modName}.rs` : `${modName}.rs`,
|
|
1975
|
+
fileDir ? `${fileDir}/${modName}/mod.rs` : `${modName}/mod.rs`
|
|
1976
|
+
];
|
|
1977
|
+
let found = false;
|
|
1978
|
+
for (const candidate of candidates) {
|
|
1979
|
+
if (knownFiles.has(candidate)) {
|
|
1980
|
+
internal[modName] = candidate;
|
|
1981
|
+
found = true;
|
|
1982
|
+
break;
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
if (!found) {
|
|
1986
|
+
external[modName] = packageId("crates", modName);
|
|
1987
|
+
}
|
|
1988
|
+
} else if (child.type === "use_declaration") {
|
|
1989
|
+
parseRustUseDecl(child, fileDir, knownFiles, internal, external);
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
return { internal, external };
|
|
1993
|
+
}
|
|
1994
|
+
function parseRustUseDecl(node, fileDir, knownFiles, internal, external) {
|
|
1995
|
+
for (const child of node.children) {
|
|
1996
|
+
if (child.type === "scoped_identifier") {
|
|
1997
|
+
const parts = child.text.split("::");
|
|
1998
|
+
const root = parts[0];
|
|
1999
|
+
resolveRustRoot(root, fileDir, knownFiles, internal, external);
|
|
2000
|
+
} else if (child.type === "scoped_use_list") {
|
|
2001
|
+
for (const sub of child.children) {
|
|
2002
|
+
if (sub.type === "identifier" || sub.type === "scoped_identifier") {
|
|
2003
|
+
const root = sub.text.split("::")[0];
|
|
2004
|
+
resolveRustRoot(root, fileDir, knownFiles, internal, external);
|
|
2005
|
+
break;
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
} else if (child.type === "identifier") {
|
|
2009
|
+
resolveRustRoot(child.text, fileDir, knownFiles, internal, external);
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
function resolveRustRoot(root, fileDir, knownFiles, internal, external) {
|
|
2014
|
+
if (root === "std" || root === "core" || root === "alloc" || root === "self" || root === "super" || root === "crate") {
|
|
2015
|
+
return;
|
|
2016
|
+
}
|
|
2017
|
+
if (!internal[root]) {
|
|
2018
|
+
const candidates = [
|
|
2019
|
+
fileDir ? `${fileDir}/${root}.rs` : `${root}.rs`,
|
|
2020
|
+
fileDir ? `${fileDir}/${root}/mod.rs` : `${root}/mod.rs`,
|
|
2021
|
+
`src/${root}.rs`,
|
|
2022
|
+
`src/${root}/mod.rs`
|
|
2023
|
+
];
|
|
2024
|
+
for (const candidate of candidates) {
|
|
2025
|
+
if (knownFiles.has(candidate)) {
|
|
2026
|
+
internal[root] = candidate;
|
|
2027
|
+
return;
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
external[root] = packageId("crates", root);
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
function analyzeRubyImports(rootNode, filePath, knownFiles) {
|
|
2034
|
+
const internal = {};
|
|
2035
|
+
const external = {};
|
|
2036
|
+
const fileDir = parentDir(filePath);
|
|
2037
|
+
for (const child of rootNode.children) {
|
|
2038
|
+
if (child.type === "call") {
|
|
2039
|
+
const funcNode = child.children[0];
|
|
2040
|
+
if (!funcNode || funcNode.type !== "identifier") continue;
|
|
2041
|
+
const funcName = funcNode.text;
|
|
2042
|
+
if (funcName === "require_relative") {
|
|
2043
|
+
const argNode = child.childForFieldName("arguments") ?? child.children.find((c) => c.type === "argument_list");
|
|
2044
|
+
if (!argNode) continue;
|
|
2045
|
+
const strNode = argNode.children.find((c) => c.type === "string");
|
|
2046
|
+
if (!strNode) continue;
|
|
2047
|
+
const contentNode = strNode.children.find(
|
|
2048
|
+
(c) => c.type === "string_content"
|
|
2049
|
+
);
|
|
2050
|
+
if (!contentNode) continue;
|
|
2051
|
+
const requirePath = contentNode.text;
|
|
2052
|
+
const resolved = resolveRelativePath(fileDir, requirePath);
|
|
2053
|
+
const candidates = [`${resolved}.rb`, resolved, `${resolved}/index.rb`];
|
|
2054
|
+
for (const candidate of candidates) {
|
|
2055
|
+
if (knownFiles.has(candidate)) {
|
|
2056
|
+
const alias = requirePath.split("/").pop();
|
|
2057
|
+
internal[alias] = candidate;
|
|
2058
|
+
break;
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
} else if (funcName === "require") {
|
|
2062
|
+
const argNode = child.childForFieldName("arguments") ?? child.children.find((c) => c.type === "argument_list");
|
|
2063
|
+
if (!argNode) continue;
|
|
2064
|
+
const strNode = argNode.children.find((c) => c.type === "string");
|
|
2065
|
+
if (!strNode) continue;
|
|
2066
|
+
const contentNode = strNode.children.find(
|
|
2067
|
+
(c) => c.type === "string_content"
|
|
2068
|
+
);
|
|
2069
|
+
if (!contentNode) continue;
|
|
2070
|
+
const gemName = contentNode.text;
|
|
2071
|
+
const candidates = [
|
|
2072
|
+
`${gemName}.rb`,
|
|
2073
|
+
`lib/${gemName}.rb`,
|
|
2074
|
+
`${gemName}/init.rb`
|
|
2075
|
+
];
|
|
2076
|
+
let found = false;
|
|
2077
|
+
for (const candidate of candidates) {
|
|
2078
|
+
if (knownFiles.has(candidate)) {
|
|
2079
|
+
internal[gemName] = candidate;
|
|
2080
|
+
found = true;
|
|
2081
|
+
break;
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
if (!found) {
|
|
2085
|
+
const name = gemName.split("/")[0];
|
|
2086
|
+
external[name] = packageId("rubygems", name);
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
}
|
|
2091
|
+
return { internal, external };
|
|
2092
|
+
}
|
|
2093
|
+
function parentDir(path) {
|
|
2094
|
+
const parts = path.split("/");
|
|
2095
|
+
return parts.length > 1 ? parts.slice(0, -1).join("/") : "";
|
|
2096
|
+
}
|
|
2097
|
+
function moduleToPaths(moduleName) {
|
|
2098
|
+
const path = moduleName.replace(/\./g, "/");
|
|
2099
|
+
return [`${path}.py`, `${path}/__init__.py`];
|
|
2100
|
+
}
|
|
2101
|
+
function resolveRelativePath(baseDir, relative) {
|
|
2102
|
+
const parts = relative.split("/");
|
|
2103
|
+
const baseParts = baseDir ? baseDir.split("/") : [];
|
|
2104
|
+
for (const part of parts) {
|
|
2105
|
+
if (part === ".") continue;
|
|
2106
|
+
else if (part === "..") {
|
|
2107
|
+
if (baseParts.length > 0) baseParts.pop();
|
|
2108
|
+
} else {
|
|
2109
|
+
baseParts.push(part);
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
return baseParts.join("/");
|
|
2113
|
+
}
|
|
2114
|
+
function analyzeImports(rootNode, language, filePath, knownPaths, modulePath) {
|
|
2115
|
+
switch (language) {
|
|
2116
|
+
case "python":
|
|
2117
|
+
return analyzePythonImports(rootNode, filePath, knownPaths);
|
|
2118
|
+
case "go":
|
|
2119
|
+
return analyzeGoImports(rootNode, knownPaths, modulePath);
|
|
2120
|
+
case "typescript":
|
|
2121
|
+
case "javascript":
|
|
2122
|
+
return analyzeTypeScriptImports(rootNode, filePath, knownPaths);
|
|
2123
|
+
case "rust":
|
|
2124
|
+
return analyzeRustImports(rootNode, filePath, knownPaths);
|
|
2125
|
+
case "ruby":
|
|
2126
|
+
return analyzeRubyImports(rootNode, filePath, knownPaths);
|
|
2127
|
+
default:
|
|
2128
|
+
return { internal: {}, external: {} };
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
function* execute$2(input, ctx) {
|
|
2132
|
+
const { parseableFiles, repoId, knownPaths, pathToFileId, goModulePath } = input;
|
|
2133
|
+
const total = parseableFiles.length;
|
|
2134
|
+
yield {
|
|
2135
|
+
kind: "stage_start",
|
|
2136
|
+
phase: "processing",
|
|
2137
|
+
message: `Processing ${total} files`
|
|
2138
|
+
};
|
|
2139
|
+
const registries = {
|
|
2140
|
+
nameRegistry: /* @__PURE__ */ new Map(),
|
|
2141
|
+
fileRegistry: /* @__PURE__ */ new Map(),
|
|
2142
|
+
classRegistry: /* @__PURE__ */ new Map(),
|
|
2143
|
+
importRegistry: /* @__PURE__ */ new Map()
|
|
2144
|
+
};
|
|
2145
|
+
const allCallInfo = [];
|
|
2146
|
+
const emittedNodeIds = /* @__PURE__ */ new Set();
|
|
2147
|
+
const packageNodes = new Map(input.packageNodes);
|
|
2148
|
+
const structureNodes = [
|
|
2149
|
+
input.repoNode,
|
|
2150
|
+
...input.dirNodes.values(),
|
|
2151
|
+
...input.fileNodes,
|
|
2152
|
+
...input.packageNodes.values()
|
|
2153
|
+
];
|
|
2154
|
+
let totalNodes = structureNodes.length;
|
|
2155
|
+
let totalRels = input.structureRels.length + input.dependencyRels.length;
|
|
2156
|
+
let filesProcessed = 0;
|
|
2157
|
+
let classesExtracted = 0;
|
|
2158
|
+
let functionsExtracted = 0;
|
|
2159
|
+
const errors = [];
|
|
2160
|
+
for (let i = 0; i < parseableFiles.length; i++) {
|
|
2161
|
+
if (ctx.cancelled) break;
|
|
2162
|
+
const file = parseableFiles[i];
|
|
2163
|
+
const ext = getExtension(file.path);
|
|
2164
|
+
const language = detectLanguage(ext);
|
|
2165
|
+
const fileId = `${repoId}/${file.path}`;
|
|
2166
|
+
if (!language) {
|
|
2167
|
+
filesProcessed++;
|
|
2168
|
+
continue;
|
|
2169
|
+
}
|
|
2170
|
+
const parser = getParserForLanguage(language, ext);
|
|
2171
|
+
const extractor = getExtractor(language);
|
|
2172
|
+
if (!parser || !extractor) {
|
|
2173
|
+
filesProcessed++;
|
|
2174
|
+
yield {
|
|
2175
|
+
kind: "stage_progress",
|
|
2176
|
+
phase: "processing",
|
|
2177
|
+
message: `Processing ${file.path}`,
|
|
2178
|
+
detail: { current: i + 1, total, fileName: file.path }
|
|
2179
|
+
};
|
|
2180
|
+
continue;
|
|
2181
|
+
}
|
|
2182
|
+
let extraction = null;
|
|
2183
|
+
const nodes = [];
|
|
2184
|
+
const rels = [];
|
|
2185
|
+
try {
|
|
2186
|
+
const tree = parser.parse(file.content);
|
|
2187
|
+
if (!tree) {
|
|
2188
|
+
errors.push(`${file.path}: parse returned null`);
|
|
2189
|
+
} else {
|
|
2190
|
+
extraction = extractor(tree.rootNode);
|
|
2191
|
+
registries.fileRegistry.set(fileId, /* @__PURE__ */ new Map());
|
|
2192
|
+
for (const sym of extraction.symbols) {
|
|
2193
|
+
processSymbol(
|
|
2194
|
+
sym,
|
|
2195
|
+
fileId,
|
|
2196
|
+
language,
|
|
2197
|
+
registries,
|
|
2198
|
+
allCallInfo,
|
|
2199
|
+
nodes,
|
|
2200
|
+
rels,
|
|
2201
|
+
emittedNodeIds
|
|
2202
|
+
);
|
|
2203
|
+
}
|
|
2204
|
+
const symCounts = countSymbols(extraction.symbols);
|
|
2205
|
+
classesExtracted += symCounts.classes;
|
|
2206
|
+
functionsExtracted += symCounts.functions;
|
|
2207
|
+
const rootNode = extraction.rootNode;
|
|
2208
|
+
if (rootNode) {
|
|
2209
|
+
const importResult = analyzeImports(
|
|
2210
|
+
rootNode,
|
|
2211
|
+
language,
|
|
2212
|
+
file.path,
|
|
2213
|
+
knownPaths,
|
|
2214
|
+
goModulePath
|
|
2215
|
+
);
|
|
2216
|
+
const fileImports = {};
|
|
2217
|
+
const seenTargetFiles = /* @__PURE__ */ new Set();
|
|
2218
|
+
for (const [alias, targetPath] of Object.entries(
|
|
2219
|
+
importResult.internal
|
|
2220
|
+
)) {
|
|
2221
|
+
const targetFileId = pathToFileId.get(targetPath);
|
|
2222
|
+
if (targetFileId) {
|
|
2223
|
+
fileImports[alias] = targetFileId;
|
|
2224
|
+
if (!seenTargetFiles.has(targetFileId)) {
|
|
2225
|
+
seenTargetFiles.add(targetFileId);
|
|
2226
|
+
rels.push({
|
|
2227
|
+
id: `${fileId}->IMPORTS->${targetFileId}`,
|
|
2228
|
+
type: "IMPORTS",
|
|
2229
|
+
source_id: fileId,
|
|
2230
|
+
target_id: targetFileId
|
|
2231
|
+
});
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
registries.importRegistry.set(fileId, fileImports);
|
|
2236
|
+
for (const [pkgName, pkgId] of Object.entries(
|
|
2237
|
+
importResult.external
|
|
2238
|
+
)) {
|
|
2239
|
+
if (!packageNodes.has(pkgId)) {
|
|
2240
|
+
const pkgNode = {
|
|
2241
|
+
id: pkgId,
|
|
2242
|
+
type: "Package",
|
|
2243
|
+
name: pkgName,
|
|
2244
|
+
properties: { registry: pkgId.split(":")[1] }
|
|
2245
|
+
};
|
|
2246
|
+
packageNodes.set(pkgId, pkgNode);
|
|
2247
|
+
nodes.push(pkgNode);
|
|
2248
|
+
}
|
|
2249
|
+
rels.push({
|
|
2250
|
+
id: `${fileId}->IMPORTS->${pkgId}`,
|
|
2251
|
+
type: "IMPORTS",
|
|
2252
|
+
source_id: fileId,
|
|
2253
|
+
target_id: pkgId
|
|
2254
|
+
});
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
} catch (err) {
|
|
2259
|
+
errors.push(
|
|
2260
|
+
`${file.path}: ${err instanceof Error ? err.message : String(err)}`
|
|
2261
|
+
);
|
|
2262
|
+
}
|
|
2263
|
+
totalNodes += nodes.length;
|
|
2264
|
+
totalRels += rels.length;
|
|
2265
|
+
filesProcessed++;
|
|
2266
|
+
yield {
|
|
2267
|
+
kind: "stage_progress",
|
|
2268
|
+
phase: "processing",
|
|
2269
|
+
message: `Processing ${file.path}`,
|
|
2270
|
+
detail: { current: i + 1, total, fileName: file.path },
|
|
2271
|
+
nodes: nodes.length > 0 ? nodes : void 0,
|
|
2272
|
+
relationships: rels.length > 0 ? rels : void 0
|
|
2273
|
+
};
|
|
2274
|
+
}
|
|
2275
|
+
yield {
|
|
2276
|
+
kind: "stage_stop",
|
|
2277
|
+
phase: "processing",
|
|
2278
|
+
message: `Processed ${filesProcessed} files`,
|
|
2279
|
+
errors: errors.length > 0 ? errors : void 0
|
|
2280
|
+
};
|
|
2281
|
+
return {
|
|
2282
|
+
scanResult: input,
|
|
2283
|
+
registries,
|
|
2284
|
+
allCallInfo,
|
|
2285
|
+
packageNodes,
|
|
2286
|
+
stats: {
|
|
2287
|
+
nodesCreated: totalNodes,
|
|
2288
|
+
relationshipsCreated: totalRels,
|
|
2289
|
+
filesProcessed,
|
|
2290
|
+
classesExtracted,
|
|
2291
|
+
functionsExtracted
|
|
2292
|
+
}
|
|
2293
|
+
};
|
|
2294
|
+
}
|
|
2295
|
+
function resolveCalls(callInfos, registries) {
|
|
2296
|
+
const results = [];
|
|
2297
|
+
for (const { callerNode, calls, fileId } of callInfos) {
|
|
2298
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2299
|
+
for (const ref of calls) {
|
|
2300
|
+
const dedupKey = `${ref.receiver ?? ""}:${ref.name}`;
|
|
2301
|
+
if (seen.has(dedupKey)) continue;
|
|
2302
|
+
if (ref.name === callerNode.name && ref.receiver === null) continue;
|
|
2303
|
+
const resolved = resolveSingleCall(ref, callerNode, fileId, registries);
|
|
2304
|
+
if (!resolved) continue;
|
|
2305
|
+
seen.add(dedupKey);
|
|
2306
|
+
results.push({
|
|
2307
|
+
sourceId: callerNode.id,
|
|
2308
|
+
targetId: resolved.targetId,
|
|
2309
|
+
confidence: resolved.confidence
|
|
2310
|
+
});
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
return results;
|
|
2314
|
+
}
|
|
2315
|
+
function resolveSingleCall(ref, callerNode, fileId, registries) {
|
|
2316
|
+
const { nameRegistry, fileRegistry, classRegistry, importRegistry } = registries;
|
|
2317
|
+
if (ref.kind === "attribute" && (ref.receiver === "self" || ref.receiver === "this")) {
|
|
2318
|
+
const classNode = findEnclosingClass(callerNode, classRegistry);
|
|
2319
|
+
if (classNode) {
|
|
2320
|
+
for (const child of classNode.children) {
|
|
2321
|
+
if (child.name === ref.name) {
|
|
2322
|
+
return { targetId: child.id, confidence: 1 };
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
return null;
|
|
2327
|
+
}
|
|
2328
|
+
if (ref.kind === "attribute") {
|
|
2329
|
+
if (callerNode.receiverVar && ref.receiver === callerNode.receiverVar && callerNode.receiverType) {
|
|
2330
|
+
const candidates = nameRegistry.get(ref.name) ?? [];
|
|
2331
|
+
for (const candidate of candidates) {
|
|
2332
|
+
if (candidate.receiverType === callerNode.receiverType) {
|
|
2333
|
+
return { targetId: candidate.id, confidence: 1 };
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
if (ref.kind === "attribute" && ref.receiver && callerNode.paramTypes) {
|
|
2339
|
+
const typeName = callerNode.paramTypes[ref.receiver];
|
|
2340
|
+
if (typeName && classRegistry.has(typeName)) {
|
|
2341
|
+
const classCandidates = classRegistry.get(typeName);
|
|
2342
|
+
for (const cls of classCandidates) {
|
|
2343
|
+
for (const child of cls.children) {
|
|
2344
|
+
if (child.name === ref.name) {
|
|
2345
|
+
return { targetId: child.id, confidence: 0.7 };
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
if (ref.kind === "attribute" && ref.receiver && classRegistry.has(ref.receiver)) {
|
|
2352
|
+
const classCandidates = classRegistry.get(ref.receiver);
|
|
2353
|
+
const sorted = [...classCandidates].sort(
|
|
2354
|
+
(a, b) => (a.fileId === fileId ? 0 : 1) - (b.fileId === fileId ? 0 : 1)
|
|
2355
|
+
);
|
|
2356
|
+
for (const cls of sorted) {
|
|
2357
|
+
for (const child of cls.children) {
|
|
2358
|
+
if (child.name === ref.name) {
|
|
2359
|
+
const targetFileId = child.id.split("::")[0];
|
|
2360
|
+
const conf = targetFileId === fileId ? 1 : 0.9;
|
|
2361
|
+
return { targetId: child.id, confidence: conf };
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
const candidates = nameRegistry.get(ref.name) ?? [];
|
|
2366
|
+
for (const candidate of candidates) {
|
|
2367
|
+
if (candidate.receiverType === ref.receiver) {
|
|
2368
|
+
const targetFileId = candidate.id.split("::")[0];
|
|
2369
|
+
const conf = targetFileId === fileId ? 1 : 0.9;
|
|
2370
|
+
return { targetId: candidate.id, confidence: conf };
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
return null;
|
|
2374
|
+
}
|
|
2375
|
+
if (ref.kind === "attribute" && ref.receiver) {
|
|
2376
|
+
const fileImports = importRegistry.get(fileId);
|
|
2377
|
+
if (fileImports) {
|
|
2378
|
+
const targetFileId = fileImports[ref.receiver];
|
|
2379
|
+
if (targetFileId) {
|
|
2380
|
+
const targetNames = fileRegistry.get(targetFileId);
|
|
2381
|
+
if (targetNames) {
|
|
2382
|
+
const target = targetNames.get(ref.name);
|
|
2383
|
+
if (target) {
|
|
2384
|
+
return { targetId: target.id, confidence: 0.9 };
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
if (ref.kind === "bare") {
|
|
2391
|
+
const fileImports = importRegistry.get(fileId);
|
|
2392
|
+
if (fileImports) {
|
|
2393
|
+
const targetFileId = fileImports[ref.name];
|
|
2394
|
+
if (targetFileId) {
|
|
2395
|
+
const targetNames = fileRegistry.get(targetFileId);
|
|
2396
|
+
if (targetNames) {
|
|
2397
|
+
const target = targetNames.get(ref.name);
|
|
2398
|
+
if (target) {
|
|
2399
|
+
if (target.kind === "class") {
|
|
2400
|
+
for (const child of target.children) {
|
|
2401
|
+
if (child.name === "__init__" || child.name === "constructor") {
|
|
2402
|
+
return { targetId: child.id, confidence: 0.9 };
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
return { targetId: target.id, confidence: 0.9 };
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
if (ref.kind === "bare" && classRegistry.has(ref.name)) {
|
|
2413
|
+
const classCandidates = classRegistry.get(ref.name);
|
|
2414
|
+
const sorted = [...classCandidates].sort(
|
|
2415
|
+
(a, b) => (a.fileId === fileId ? 0 : 1) - (b.fileId === fileId ? 0 : 1)
|
|
2416
|
+
);
|
|
2417
|
+
for (const cls2 of sorted) {
|
|
2418
|
+
for (const child of cls2.children) {
|
|
2419
|
+
if (child.name === "__init__" || child.name === "constructor") {
|
|
2420
|
+
const targetFileId2 = child.id.split("::")[0];
|
|
2421
|
+
const conf2 = targetFileId2 === fileId ? 1 : 0.8;
|
|
2422
|
+
return { targetId: child.id, confidence: conf2 };
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
const cls = sorted[0];
|
|
2427
|
+
const targetFileId = cls.id.split("::")[0];
|
|
2428
|
+
const conf = targetFileId === fileId ? 1 : 0.8;
|
|
2429
|
+
return { targetId: cls.id, confidence: conf };
|
|
2430
|
+
}
|
|
2431
|
+
if (ref.kind === "bare") {
|
|
2432
|
+
const fileNames = fileRegistry.get(fileId);
|
|
2433
|
+
if (fileNames) {
|
|
2434
|
+
const target = fileNames.get(ref.name);
|
|
2435
|
+
if (target) {
|
|
2436
|
+
return { targetId: target.id, confidence: 1 };
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
if (ref.kind === "bare") {
|
|
2441
|
+
const candidates = nameRegistry.get(ref.name) ?? [];
|
|
2442
|
+
const crossFile = candidates.filter((c) => c.id.split("::")[0] !== fileId);
|
|
2443
|
+
if (crossFile.length === 1) {
|
|
2444
|
+
return { targetId: crossFile[0].id, confidence: 0.8 };
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2447
|
+
return null;
|
|
2448
|
+
}
|
|
2449
|
+
function findEnclosingClass(node, classRegistry) {
|
|
2450
|
+
const parts = node.id.split("::");
|
|
2451
|
+
for (let i = parts.length - 1; i >= 1; i--) {
|
|
2452
|
+
const potentialClassName = parts[i];
|
|
2453
|
+
const candidates = classRegistry.get(potentialClassName);
|
|
2454
|
+
if (candidates) {
|
|
2455
|
+
for (const cls of candidates) {
|
|
2456
|
+
if (node.id.startsWith(cls.id + "::")) {
|
|
2457
|
+
return cls;
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
return null;
|
|
2463
|
+
}
|
|
2464
|
+
function resolvedCallsToRelationships(resolvedCalls) {
|
|
2465
|
+
return resolvedCalls.map((call) => ({
|
|
2466
|
+
id: `${call.sourceId}->CALLS->${call.targetId}`,
|
|
2467
|
+
type: "CALLS",
|
|
2468
|
+
source_id: call.sourceId,
|
|
2469
|
+
target_id: call.targetId,
|
|
2470
|
+
properties: { confidence: call.confidence }
|
|
2471
|
+
}));
|
|
2472
|
+
}
|
|
2473
|
+
function* execute$1(input) {
|
|
2474
|
+
const { registries, allCallInfo, stats } = input;
|
|
2475
|
+
yield {
|
|
2476
|
+
kind: "stage_start",
|
|
2477
|
+
phase: "resolving",
|
|
2478
|
+
message: `Resolving ${allCallInfo.length} call sites`
|
|
2479
|
+
};
|
|
2480
|
+
const resolvedCalls = resolveCalls(allCallInfo, registries);
|
|
2481
|
+
const callRels = resolvedCallsToRelationships(resolvedCalls);
|
|
2482
|
+
if (callRels.length > 0) {
|
|
2483
|
+
yield {
|
|
2484
|
+
kind: "stage_progress",
|
|
2485
|
+
phase: "resolving",
|
|
2486
|
+
message: `Resolved ${callRels.length} calls`,
|
|
2487
|
+
relationships: callRels
|
|
2488
|
+
};
|
|
2489
|
+
}
|
|
2490
|
+
yield {
|
|
2491
|
+
kind: "stage_stop",
|
|
2492
|
+
phase: "resolving",
|
|
2493
|
+
message: `Resolved ${callRels.length} calls`
|
|
2494
|
+
};
|
|
2495
|
+
return {
|
|
2496
|
+
nodesCreated: stats.nodesCreated,
|
|
2497
|
+
relationshipsCreated: stats.relationshipsCreated + callRels.length,
|
|
2498
|
+
filesProcessed: stats.filesProcessed,
|
|
2499
|
+
classesExtracted: stats.classesExtracted,
|
|
2500
|
+
functionsExtracted: stats.functionsExtracted
|
|
2501
|
+
};
|
|
2502
|
+
}
|
|
2503
|
+
function* execute(inner, store) {
|
|
2504
|
+
let nodesSaved = 0;
|
|
2505
|
+
let relsSaved = 0;
|
|
2506
|
+
for (const event of inner) {
|
|
2507
|
+
if (event.nodes) {
|
|
2508
|
+
for (const node of event.nodes) {
|
|
2509
|
+
store.saveNode(node);
|
|
2510
|
+
}
|
|
2511
|
+
nodesSaved += event.nodes.length;
|
|
2512
|
+
}
|
|
2513
|
+
if (event.relationships) {
|
|
2514
|
+
for (const rel of event.relationships) {
|
|
2515
|
+
store.saveRelationship(rel);
|
|
2516
|
+
}
|
|
2517
|
+
relsSaved += event.relationships.length;
|
|
2518
|
+
}
|
|
2519
|
+
yield event;
|
|
2520
|
+
}
|
|
2521
|
+
yield {
|
|
2522
|
+
kind: "stage_stop",
|
|
2523
|
+
phase: "submitting",
|
|
2524
|
+
message: `Saved ${nodesSaved} nodes and ${relsSaved} relationships`
|
|
2525
|
+
};
|
|
2526
|
+
}
|
|
2527
|
+
const VERB_MAP = {
|
|
2528
|
+
get: "Retrieves",
|
|
2529
|
+
fetch: "Retrieves",
|
|
2530
|
+
load: "Retrieves",
|
|
2531
|
+
find: "Retrieves",
|
|
2532
|
+
query: "Retrieves",
|
|
2533
|
+
read: "Reads",
|
|
2534
|
+
lookup: "Looks up",
|
|
2535
|
+
set: "Updates",
|
|
2536
|
+
update: "Updates",
|
|
2537
|
+
modify: "Updates",
|
|
2538
|
+
patch: "Updates",
|
|
2539
|
+
put: "Updates",
|
|
2540
|
+
validate: "Validates",
|
|
2541
|
+
verify: "Validates",
|
|
2542
|
+
check: "Validates",
|
|
2543
|
+
ensure: "Ensures",
|
|
2544
|
+
create: "Creates",
|
|
2545
|
+
make: "Creates",
|
|
2546
|
+
build: "Creates",
|
|
2547
|
+
generate: "Creates",
|
|
2548
|
+
new: "Creates",
|
|
2549
|
+
add: "Adds",
|
|
2550
|
+
insert: "Adds",
|
|
2551
|
+
append: "Adds",
|
|
2552
|
+
register: "Registers",
|
|
2553
|
+
delete: "Removes",
|
|
2554
|
+
remove: "Removes",
|
|
2555
|
+
destroy: "Removes",
|
|
2556
|
+
drop: "Removes",
|
|
2557
|
+
unset: "Removes",
|
|
2558
|
+
clear: "Clears",
|
|
2559
|
+
parse: "Parses",
|
|
2560
|
+
extract: "Extracts",
|
|
2561
|
+
decode: "Decodes",
|
|
2562
|
+
handle: "Handles",
|
|
2563
|
+
process: "Processes",
|
|
2564
|
+
run: "Runs",
|
|
2565
|
+
execute: "Executes",
|
|
2566
|
+
do: "Performs",
|
|
2567
|
+
perform: "Performs",
|
|
2568
|
+
convert: "Converts",
|
|
2569
|
+
transform: "Converts",
|
|
2570
|
+
to: "Converts to",
|
|
2571
|
+
format: "Formats",
|
|
2572
|
+
encode: "Encodes",
|
|
2573
|
+
serialize: "Serializes",
|
|
2574
|
+
marshal: "Serializes",
|
|
2575
|
+
deserialize: "Deserializes",
|
|
2576
|
+
unmarshal: "Deserializes",
|
|
2577
|
+
is: "Checks whether",
|
|
2578
|
+
has: "Checks whether",
|
|
2579
|
+
can: "Checks whether",
|
|
2580
|
+
should: "Checks whether",
|
|
2581
|
+
init: "Initializes",
|
|
2582
|
+
setup: "Initializes",
|
|
2583
|
+
configure: "Configures",
|
|
2584
|
+
start: "Starts",
|
|
2585
|
+
stop: "Stops",
|
|
2586
|
+
open: "Opens",
|
|
2587
|
+
close: "Closes",
|
|
2588
|
+
connect: "Connects",
|
|
2589
|
+
disconnect: "Disconnects",
|
|
2590
|
+
render: "Renders",
|
|
2591
|
+
display: "Renders",
|
|
2592
|
+
show: "Renders",
|
|
2593
|
+
draw: "Renders",
|
|
2594
|
+
paint: "Renders",
|
|
2595
|
+
hide: "Hides",
|
|
2596
|
+
send: "Sends",
|
|
2597
|
+
emit: "Emits",
|
|
2598
|
+
dispatch: "Dispatches",
|
|
2599
|
+
publish: "Publishes",
|
|
2600
|
+
broadcast: "Broadcasts",
|
|
2601
|
+
notify: "Notifies",
|
|
2602
|
+
receive: "Receives",
|
|
2603
|
+
listen: "Listens for",
|
|
2604
|
+
subscribe: "Subscribes to",
|
|
2605
|
+
on: "Handles",
|
|
2606
|
+
sort: "Sorts",
|
|
2607
|
+
filter: "Filters",
|
|
2608
|
+
map: "Maps",
|
|
2609
|
+
reduce: "Reduces",
|
|
2610
|
+
merge: "Merges",
|
|
2611
|
+
join: "Joins",
|
|
2612
|
+
split: "Splits",
|
|
2613
|
+
group: "Groups",
|
|
2614
|
+
flatten: "Flattens",
|
|
2615
|
+
test: "Tests",
|
|
2616
|
+
assert: "Tests",
|
|
2617
|
+
expect: "Tests",
|
|
2618
|
+
log: "Logs",
|
|
2619
|
+
print: "Logs",
|
|
2620
|
+
warn: "Logs warning for",
|
|
2621
|
+
error: "Logs error for",
|
|
2622
|
+
debug: "Logs debug info for",
|
|
2623
|
+
write: "Writes",
|
|
2624
|
+
save: "Saves",
|
|
2625
|
+
store: "Stores",
|
|
2626
|
+
cache: "Caches",
|
|
2627
|
+
flush: "Flushes",
|
|
2628
|
+
sync: "Synchronizes",
|
|
2629
|
+
reset: "Resets",
|
|
2630
|
+
refresh: "Refreshes",
|
|
2631
|
+
reload: "Reloads",
|
|
2632
|
+
retry: "Retries",
|
|
2633
|
+
wrap: "Wraps",
|
|
2634
|
+
unwrap: "Unwraps",
|
|
2635
|
+
apply: "Applies",
|
|
2636
|
+
resolve: "Resolves",
|
|
2637
|
+
reject: "Rejects",
|
|
2638
|
+
throw: "Throws",
|
|
2639
|
+
raise: "Raises",
|
|
2640
|
+
try: "Attempts",
|
|
2641
|
+
await: "Awaits",
|
|
2642
|
+
wait: "Waits for",
|
|
2643
|
+
schedule: "Schedules",
|
|
2644
|
+
defer: "Defers",
|
|
2645
|
+
cancel: "Cancels",
|
|
2646
|
+
abort: "Aborts",
|
|
2647
|
+
clone: "Clones",
|
|
2648
|
+
copy: "Copies",
|
|
2649
|
+
compare: "Compares",
|
|
2650
|
+
equals: "Checks equality of",
|
|
2651
|
+
match: "Matches",
|
|
2652
|
+
contains: "Checks whether contains",
|
|
2653
|
+
include: "Includes",
|
|
2654
|
+
exclude: "Excludes",
|
|
2655
|
+
enable: "Enables",
|
|
2656
|
+
disable: "Disables",
|
|
2657
|
+
toggle: "Toggles",
|
|
2658
|
+
mount: "Mounts",
|
|
2659
|
+
unmount: "Unmounts",
|
|
2660
|
+
use: "Uses",
|
|
2661
|
+
with: "Configures with",
|
|
2662
|
+
from: "Creates from",
|
|
2663
|
+
of: "Creates"
|
|
2664
|
+
};
|
|
2665
|
+
const CLASS_SUFFIX_MAP = {
|
|
2666
|
+
service: "Service",
|
|
2667
|
+
handler: "Handler",
|
|
2668
|
+
controller: "Controller",
|
|
2669
|
+
factory: "Factory",
|
|
2670
|
+
repository: "Repository",
|
|
2671
|
+
repo: "Repository",
|
|
2672
|
+
manager: "Manager",
|
|
2673
|
+
provider: "Provider",
|
|
2674
|
+
adapter: "Adapter",
|
|
2675
|
+
middleware: "Middleware",
|
|
2676
|
+
guard: "Guard",
|
|
2677
|
+
interceptor: "Interceptor",
|
|
2678
|
+
resolver: "Resolver",
|
|
2679
|
+
validator: "Validator",
|
|
2680
|
+
builder: "Builder",
|
|
2681
|
+
parser: "Parser",
|
|
2682
|
+
formatter: "Formatter",
|
|
2683
|
+
converter: "Converter",
|
|
2684
|
+
serializer: "Serializer",
|
|
2685
|
+
client: "Client",
|
|
2686
|
+
server: "Server",
|
|
2687
|
+
router: "Router",
|
|
2688
|
+
store: "Store",
|
|
2689
|
+
cache: "Cache",
|
|
2690
|
+
queue: "Queue",
|
|
2691
|
+
pool: "Pool",
|
|
2692
|
+
registry: "Registry",
|
|
2693
|
+
observer: "Observer",
|
|
2694
|
+
emitter: "Emitter",
|
|
2695
|
+
listener: "Listener",
|
|
2696
|
+
subscriber: "Subscriber",
|
|
2697
|
+
publisher: "Publisher",
|
|
2698
|
+
component: "Component",
|
|
2699
|
+
module: "Module",
|
|
2700
|
+
plugin: "Plugin",
|
|
2701
|
+
helper: "Helper",
|
|
2702
|
+
util: "Utility",
|
|
2703
|
+
utils: "Utility",
|
|
2704
|
+
error: "Error",
|
|
2705
|
+
exception: "Exception",
|
|
2706
|
+
model: "Model",
|
|
2707
|
+
entity: "Entity",
|
|
2708
|
+
dto: "DTO",
|
|
2709
|
+
config: "Configuration",
|
|
2710
|
+
options: "Options",
|
|
2711
|
+
context: "Context",
|
|
2712
|
+
state: "State",
|
|
2713
|
+
hook: "Hook"
|
|
2714
|
+
};
|
|
2715
|
+
const FILE_PATTERNS = [
|
|
2716
|
+
[/_test\.go$/, "Tests for"],
|
|
2717
|
+
[/_test\.py$/, "Tests for"],
|
|
2718
|
+
[/\.test\.[jt]sx?$/, "Tests for"],
|
|
2719
|
+
[/\.spec\.[jt]sx?$/, "Tests for"],
|
|
2720
|
+
[/^test_/, "Tests for"],
|
|
2721
|
+
[/^conftest\.py$/, "Pytest fixtures and configuration"],
|
|
2722
|
+
[/^setup\.[jt]s$/, "Setup configuration"],
|
|
2723
|
+
[/^index\.[jt]sx?$/, "Barrel exports for"],
|
|
2724
|
+
[/^main\.[a-z]+$/, "Application entry point"],
|
|
2725
|
+
[/^mod\.rs$/, "Module declarations"],
|
|
2726
|
+
[/^__init__\.py$/, "Package initialization for"],
|
|
2727
|
+
[/^constants?\.[a-z]+$/, "Constants and configuration values"],
|
|
2728
|
+
[/^types?\.[a-z]+$/, "Type definitions"],
|
|
2729
|
+
[/^utils?\.[a-z]+$/, "Utility functions"],
|
|
2730
|
+
[/^helpers?\.[a-z]+$/, "Helper functions"],
|
|
2731
|
+
[/^middleware\.[a-z]+$/, "Middleware definitions"],
|
|
2732
|
+
[/^routes?\.[a-z]+$/, "Route definitions"],
|
|
2733
|
+
[/^models?\.[a-z]+$/, "Data model definitions"],
|
|
2734
|
+
[/^schema\.[a-z]+$/, "Schema definitions"],
|
|
2735
|
+
[/^migrations?/, "Database migration"],
|
|
2736
|
+
[/^dockerfile/i, "Docker container configuration"],
|
|
2737
|
+
[/^makefile$/i, "Build automation rules"],
|
|
2738
|
+
[/^readme/i, "Project documentation"],
|
|
2739
|
+
[/^changelog/i, "Version change history"],
|
|
2740
|
+
[/^license/i, "License information"],
|
|
2741
|
+
[/\.config\.[a-z]+$/, "Configuration for"],
|
|
2742
|
+
[/rc\.[a-z]+$/, "Configuration for"]
|
|
2743
|
+
];
|
|
2744
|
+
const DIR_PATTERNS = {
|
|
2745
|
+
api: "API layer",
|
|
2746
|
+
apis: "API layer",
|
|
2747
|
+
handlers: "Request handlers",
|
|
2748
|
+
handler: "Request handlers",
|
|
2749
|
+
controllers: "Request controllers",
|
|
2750
|
+
controller: "Request controllers",
|
|
2751
|
+
routes: "Route definitions",
|
|
2752
|
+
routing: "Route definitions",
|
|
2753
|
+
models: "Data models",
|
|
2754
|
+
model: "Data models",
|
|
2755
|
+
entities: "Data entities",
|
|
2756
|
+
schema: "Schema definitions",
|
|
2757
|
+
schemas: "Schema definitions",
|
|
2758
|
+
services: "Service layer",
|
|
2759
|
+
service: "Service layer",
|
|
2760
|
+
middleware: "Middleware",
|
|
2761
|
+
middlewares: "Middleware",
|
|
2762
|
+
utils: "Utility functions",
|
|
2763
|
+
util: "Utility functions",
|
|
2764
|
+
helpers: "Helper functions",
|
|
2765
|
+
helper: "Helper functions",
|
|
2766
|
+
lib: "Library modules",
|
|
2767
|
+
libs: "Library modules",
|
|
2768
|
+
pkg: "Package modules",
|
|
2769
|
+
internal: "Internal packages",
|
|
2770
|
+
cmd: "Command entry points",
|
|
2771
|
+
config: "Configuration",
|
|
2772
|
+
configs: "Configuration",
|
|
2773
|
+
tests: "Test suite",
|
|
2774
|
+
test: "Test suite",
|
|
2775
|
+
__tests__: "Test suite",
|
|
2776
|
+
spec: "Test specifications",
|
|
2777
|
+
specs: "Test specifications",
|
|
2778
|
+
fixtures: "Test fixtures",
|
|
2779
|
+
mocks: "Test mocks",
|
|
2780
|
+
components: "UI components",
|
|
2781
|
+
component: "UI components",
|
|
2782
|
+
pages: "Page components",
|
|
2783
|
+
views: "View components",
|
|
2784
|
+
layouts: "Layout components",
|
|
2785
|
+
hooks: "React hooks",
|
|
2786
|
+
store: "State management",
|
|
2787
|
+
stores: "State management",
|
|
2788
|
+
state: "State management",
|
|
2789
|
+
reducers: "State reducers",
|
|
2790
|
+
actions: "State actions",
|
|
2791
|
+
selectors: "State selectors",
|
|
2792
|
+
types: "Type definitions",
|
|
2793
|
+
interfaces: "Interface definitions",
|
|
2794
|
+
constants: "Constants",
|
|
2795
|
+
static: "Static assets",
|
|
2796
|
+
assets: "Static assets",
|
|
2797
|
+
public: "Public assets",
|
|
2798
|
+
styles: "Stylesheets",
|
|
2799
|
+
css: "Stylesheets",
|
|
2800
|
+
docs: "Documentation",
|
|
2801
|
+
doc: "Documentation",
|
|
2802
|
+
scripts: "Build and utility scripts",
|
|
2803
|
+
migrations: "Database migrations",
|
|
2804
|
+
seeds: "Database seed data",
|
|
2805
|
+
templates: "Templates",
|
|
2806
|
+
i18n: "Internationalization",
|
|
2807
|
+
locales: "Locale translations",
|
|
2808
|
+
proto: "Protocol buffer definitions",
|
|
2809
|
+
generated: "Generated code",
|
|
2810
|
+
gen: "Generated code",
|
|
2811
|
+
dist: "Build output",
|
|
2812
|
+
build: "Build output",
|
|
2813
|
+
vendor: "Vendored dependencies",
|
|
2814
|
+
node_modules: "NPM dependencies",
|
|
2815
|
+
bin: "Executable binaries",
|
|
2816
|
+
examples: "Usage examples",
|
|
2817
|
+
example: "Usage examples",
|
|
2818
|
+
plugins: "Plugin modules",
|
|
2819
|
+
extensions: "Extension modules",
|
|
2820
|
+
auth: "Authentication and authorization",
|
|
2821
|
+
security: "Security modules",
|
|
2822
|
+
crypto: "Cryptographic utilities",
|
|
2823
|
+
db: "Database layer",
|
|
2824
|
+
database: "Database layer",
|
|
2825
|
+
cache: "Caching layer",
|
|
2826
|
+
queue: "Message queue handlers",
|
|
2827
|
+
workers: "Background workers",
|
|
2828
|
+
jobs: "Background jobs",
|
|
2829
|
+
tasks: "Task definitions",
|
|
2830
|
+
events: "Event handlers",
|
|
2831
|
+
subscribers: "Event subscribers",
|
|
2832
|
+
publishers: "Event publishers",
|
|
2833
|
+
adapters: "Adapter implementations",
|
|
2834
|
+
providers: "Service providers",
|
|
2835
|
+
repositories: "Data repositories",
|
|
2836
|
+
repository: "Data repositories",
|
|
2837
|
+
clients: "External API clients",
|
|
2838
|
+
sdk: "SDK modules",
|
|
2839
|
+
common: "Shared common modules",
|
|
2840
|
+
shared: "Shared modules",
|
|
2841
|
+
core: "Core modules",
|
|
2842
|
+
base: "Base classes and interfaces",
|
|
2843
|
+
errors: "Error definitions",
|
|
2844
|
+
exceptions: "Exception definitions",
|
|
2845
|
+
validators: "Input validators",
|
|
2846
|
+
validation: "Validation logic",
|
|
2847
|
+
serializers: "Data serializers",
|
|
2848
|
+
parsers: "Data parsers",
|
|
2849
|
+
formatters: "Data formatters",
|
|
2850
|
+
converters: "Data converters",
|
|
2851
|
+
transformers: "Data transformers",
|
|
2852
|
+
mappers: "Data mappers",
|
|
2853
|
+
resolvers: "GraphQL resolvers",
|
|
2854
|
+
guards: "Route guards",
|
|
2855
|
+
interceptors: "Request interceptors",
|
|
2856
|
+
decorators: "Decorators",
|
|
2857
|
+
annotations: "Annotations",
|
|
2858
|
+
factories: "Factory functions",
|
|
2859
|
+
builders: "Builder patterns",
|
|
2860
|
+
observers: "Observer implementations",
|
|
2861
|
+
strategies: "Strategy pattern implementations",
|
|
2862
|
+
commands: "Command implementations",
|
|
2863
|
+
queries: "Query implementations",
|
|
2864
|
+
notifications: "Notification handlers",
|
|
2865
|
+
mailers: "Email sending",
|
|
2866
|
+
logging: "Logging configuration",
|
|
2867
|
+
monitoring: "Monitoring and metrics",
|
|
2868
|
+
metrics: "Application metrics",
|
|
2869
|
+
tracing: "Distributed tracing",
|
|
2870
|
+
indexer: "Indexing pipeline",
|
|
2871
|
+
graph: "Graph data structures",
|
|
2872
|
+
summarizer: "Summarization modules",
|
|
2873
|
+
embedder: "Embedding modules",
|
|
2874
|
+
extractors: "Data extractors"
|
|
2875
|
+
};
|
|
2876
|
+
const KEYWORD_PATTERNS = [
|
|
2877
|
+
{
|
|
2878
|
+
domain: "database",
|
|
2879
|
+
patterns: [
|
|
2880
|
+
/\bsql\b/i,
|
|
2881
|
+
/\bquery\b/i,
|
|
2882
|
+
/\bSELECT\b/,
|
|
2883
|
+
/\bINSERT\b/,
|
|
2884
|
+
/\bUPDATE\b.*\bSET\b/,
|
|
2885
|
+
/\bdb\./i,
|
|
2886
|
+
/\bcursor\b/i,
|
|
2887
|
+
/\bconnection\b/i,
|
|
2888
|
+
/\bprisma\b/i,
|
|
2889
|
+
/\bknex\b/i,
|
|
2890
|
+
/\bsequelize\b/i,
|
|
2891
|
+
/\bmongoose\b/i,
|
|
2892
|
+
/\bsqlx\b/i,
|
|
2893
|
+
/\borm\b/i,
|
|
2894
|
+
/\bmigration\b/i,
|
|
2895
|
+
/\bschema\b/i,
|
|
2896
|
+
/\btable\b/i,
|
|
2897
|
+
/\btransaction\b/i,
|
|
2898
|
+
/\bcolumn\b/i
|
|
2899
|
+
]
|
|
2900
|
+
},
|
|
2901
|
+
{
|
|
2902
|
+
domain: "auth",
|
|
2903
|
+
patterns: [
|
|
2904
|
+
/\bauth\b/i,
|
|
2905
|
+
/\bjwt\b/i,
|
|
2906
|
+
/\btoken\b/i,
|
|
2907
|
+
/\bpassword\b/i,
|
|
2908
|
+
/\blogin\b/i,
|
|
2909
|
+
/\bsession\b/i,
|
|
2910
|
+
/\boauth\b/i,
|
|
2911
|
+
/\bbcrypt\b/i,
|
|
2912
|
+
/\bcredential\b/i,
|
|
2913
|
+
/\bpermission\b/i,
|
|
2914
|
+
/\brole\b/i,
|
|
2915
|
+
/\bacl\b/i,
|
|
2916
|
+
/\bsignin\b/i,
|
|
2917
|
+
/\bsignup\b/i
|
|
2918
|
+
]
|
|
2919
|
+
},
|
|
2920
|
+
{
|
|
2921
|
+
domain: "http",
|
|
2922
|
+
patterns: [
|
|
2923
|
+
/\bhttp\b/i,
|
|
2924
|
+
/\brequest\b/i,
|
|
2925
|
+
/\bresponse\b/i,
|
|
2926
|
+
/\bfetch\s*\(/i,
|
|
2927
|
+
/\baxios\b/i,
|
|
2928
|
+
/\bhandler\b/i,
|
|
2929
|
+
/\bmiddleware\b/i,
|
|
2930
|
+
/\bcors\b/i,
|
|
2931
|
+
/\bendpoint\b/i,
|
|
2932
|
+
/\broute\b/i,
|
|
2933
|
+
/\bREST\b/i,
|
|
2934
|
+
/\bgraphql\b/i,
|
|
2935
|
+
/\bheader\b/i
|
|
2936
|
+
]
|
|
2937
|
+
},
|
|
2938
|
+
{
|
|
2939
|
+
domain: "filesystem",
|
|
2940
|
+
patterns: [
|
|
2941
|
+
/\bfs\./i,
|
|
2942
|
+
/\breadFile\b/i,
|
|
2943
|
+
/\bwriteFile\b/i,
|
|
2944
|
+
/\bpath\./i,
|
|
2945
|
+
/\bstream\b/i,
|
|
2946
|
+
/\bbuffer\b/i,
|
|
2947
|
+
/\bmkdir\b/i,
|
|
2948
|
+
/\bunlink\b/i,
|
|
2949
|
+
/\bglob\b/i
|
|
2950
|
+
]
|
|
2951
|
+
},
|
|
2952
|
+
{
|
|
2953
|
+
domain: "crypto",
|
|
2954
|
+
patterns: [
|
|
2955
|
+
/\bcrypto\b/i,
|
|
2956
|
+
/\bencrypt\b/i,
|
|
2957
|
+
/\bdecrypt\b/i,
|
|
2958
|
+
/\bhash\b/i,
|
|
2959
|
+
/\bsign\b/i,
|
|
2960
|
+
/\bverify\b/i,
|
|
2961
|
+
/\bcipher\b/i,
|
|
2962
|
+
/\bhmac\b/i
|
|
2963
|
+
]
|
|
2964
|
+
},
|
|
2965
|
+
{
|
|
2966
|
+
domain: "cache",
|
|
2967
|
+
patterns: [
|
|
2968
|
+
/\bcache\b/i,
|
|
2969
|
+
/\bredis\b/i,
|
|
2970
|
+
/\bmemcached\b/i,
|
|
2971
|
+
/\bttl\b/i,
|
|
2972
|
+
/\binvalidate\b/i,
|
|
2973
|
+
/\bLRU\b/i
|
|
2974
|
+
]
|
|
2975
|
+
},
|
|
2976
|
+
{
|
|
2977
|
+
domain: "queue",
|
|
2978
|
+
patterns: [
|
|
2979
|
+
/\bqueue\b/i,
|
|
2980
|
+
/\bworker\b/i,
|
|
2981
|
+
/\bjob\b/i,
|
|
2982
|
+
/\bpublish\b/i,
|
|
2983
|
+
/\bsubscribe\b/i,
|
|
2984
|
+
/\bkafka\b/i,
|
|
2985
|
+
/\brabbitmq\b/i,
|
|
2986
|
+
/\bamqp\b/i
|
|
2987
|
+
]
|
|
2988
|
+
},
|
|
2989
|
+
{
|
|
2990
|
+
domain: "config",
|
|
2991
|
+
patterns: [
|
|
2992
|
+
/\bconfig\b/i,
|
|
2993
|
+
/\benv\b/i,
|
|
2994
|
+
/\bsettings\b/i,
|
|
2995
|
+
/\byaml\b/i,
|
|
2996
|
+
/\bdotenv\b/i,
|
|
2997
|
+
/\.env\b/i
|
|
2998
|
+
]
|
|
2999
|
+
},
|
|
3000
|
+
{
|
|
3001
|
+
domain: "logging",
|
|
3002
|
+
patterns: [
|
|
3003
|
+
/\blogger\b/i,
|
|
3004
|
+
/\bwinston\b/i,
|
|
3005
|
+
/\bpino\b/i,
|
|
3006
|
+
/\blogrus\b/i,
|
|
3007
|
+
/\bslog\b/i,
|
|
3008
|
+
/\blog\./i
|
|
3009
|
+
]
|
|
3010
|
+
},
|
|
3011
|
+
{
|
|
3012
|
+
domain: "testing",
|
|
3013
|
+
patterns: [
|
|
3014
|
+
/\btest\b/i,
|
|
3015
|
+
/\bassert\b/i,
|
|
3016
|
+
/\bexpect\b/i,
|
|
3017
|
+
/\bmock\b/i,
|
|
3018
|
+
/\bstub\b/i,
|
|
3019
|
+
/\bspy\b/i,
|
|
3020
|
+
/\bfixture\b/i,
|
|
3021
|
+
/\bjest\b/i,
|
|
3022
|
+
/\bpytest\b/i
|
|
3023
|
+
]
|
|
3024
|
+
},
|
|
3025
|
+
{
|
|
3026
|
+
domain: "async",
|
|
3027
|
+
patterns: [
|
|
3028
|
+
/\basync\b/i,
|
|
3029
|
+
/\bawait\b/i,
|
|
3030
|
+
/\bPromise\b/,
|
|
3031
|
+
/\bgoroutine\b/i,
|
|
3032
|
+
/\bchannel\b/i,
|
|
3033
|
+
/\bconcurrent\b/i,
|
|
3034
|
+
/\bparallel\b/i,
|
|
3035
|
+
/\bmutex\b/i
|
|
3036
|
+
]
|
|
3037
|
+
},
|
|
3038
|
+
{
|
|
3039
|
+
domain: "error",
|
|
3040
|
+
patterns: [
|
|
3041
|
+
/\btry\b/,
|
|
3042
|
+
/\bcatch\b/,
|
|
3043
|
+
/\bthrow\b/,
|
|
3044
|
+
/\bError\b/,
|
|
3045
|
+
/\bpanic\b/i,
|
|
3046
|
+
/\brecover\b/i,
|
|
3047
|
+
/\berrno\b/i
|
|
3048
|
+
]
|
|
3049
|
+
},
|
|
3050
|
+
{
|
|
3051
|
+
domain: "validation",
|
|
3052
|
+
patterns: [
|
|
3053
|
+
/\bvalidate\b/i,
|
|
3054
|
+
/\bsanitize\b/i,
|
|
3055
|
+
/\bzod\b/i,
|
|
3056
|
+
/\byup\b/i,
|
|
3057
|
+
/\bjoi\b/i,
|
|
3058
|
+
/\bregexp\b/i,
|
|
3059
|
+
/\bregex\b/i
|
|
3060
|
+
]
|
|
3061
|
+
},
|
|
3062
|
+
{
|
|
3063
|
+
domain: "websocket",
|
|
3064
|
+
patterns: [
|
|
3065
|
+
/\bwebsocket\b/i,
|
|
3066
|
+
/\bws\./i,
|
|
3067
|
+
/\bsocket\b/i,
|
|
3068
|
+
/\bWebSocket\b/,
|
|
3069
|
+
/\bemit\s*\(/i
|
|
3070
|
+
]
|
|
3071
|
+
},
|
|
3072
|
+
{
|
|
3073
|
+
domain: "email",
|
|
3074
|
+
patterns: [/\bemail\b/i, /\bsmtp\b/i, /\bsendmail\b/i, /\bmailer\b/i]
|
|
3075
|
+
}
|
|
3076
|
+
];
|
|
3077
|
+
function splitIdentifier(name) {
|
|
3078
|
+
const cleaned = name.replace(/^[_$]+/, "").replace(/[_$]+$/, "");
|
|
3079
|
+
if (!cleaned) return [name];
|
|
3080
|
+
const parts = cleaned.split(/[_-]+/).filter(Boolean);
|
|
3081
|
+
const words = [];
|
|
3082
|
+
for (const part of parts) {
|
|
3083
|
+
const subWords = part.replace(/([a-z])([A-Z])/g, "$1\0$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1\0$2").replace(/([a-zA-Z])(\d)/g, "$1\0$2").replace(/(\d)([a-zA-Z])/g, "$1\0$2").split("\0").filter(Boolean);
|
|
3084
|
+
words.push(...subWords);
|
|
3085
|
+
}
|
|
3086
|
+
return words.length > 0 ? words : [name];
|
|
3087
|
+
}
|
|
3088
|
+
function wordsToPhrase(words) {
|
|
3089
|
+
return words.map((w) => {
|
|
3090
|
+
if (w.length <= 4 && w === w.toUpperCase() && /^[A-Z]+$/.test(w))
|
|
3091
|
+
return w;
|
|
3092
|
+
return w.toLowerCase();
|
|
3093
|
+
}).join(" ");
|
|
3094
|
+
}
|
|
3095
|
+
function extractKeywords(source) {
|
|
3096
|
+
if (!source) return [];
|
|
3097
|
+
const found = [];
|
|
3098
|
+
for (const kp of KEYWORD_PATTERNS) {
|
|
3099
|
+
if (found.length >= 4) break;
|
|
3100
|
+
for (const pat of kp.patterns) {
|
|
3101
|
+
if (pat.test(source)) {
|
|
3102
|
+
found.push(kp.domain);
|
|
3103
|
+
break;
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
return found;
|
|
3108
|
+
}
|
|
3109
|
+
function formatKeywords(keywords) {
|
|
3110
|
+
return keywords.length > 0 ? ` [${keywords.join(", ")}]` : "";
|
|
3111
|
+
}
|
|
3112
|
+
const CONSTRUCTOR_NAMES = /* @__PURE__ */ new Set([
|
|
3113
|
+
"__init__",
|
|
3114
|
+
"constructor",
|
|
3115
|
+
"init",
|
|
3116
|
+
"initialize",
|
|
3117
|
+
"New"
|
|
3118
|
+
]);
|
|
3119
|
+
function isNonDescriptive(name) {
|
|
3120
|
+
if (name.length <= 2) return true;
|
|
3121
|
+
const lower = name.toLowerCase();
|
|
3122
|
+
return [
|
|
3123
|
+
"foo",
|
|
3124
|
+
"bar",
|
|
3125
|
+
"baz",
|
|
3126
|
+
"tmp",
|
|
3127
|
+
"temp",
|
|
3128
|
+
"x",
|
|
3129
|
+
"y",
|
|
3130
|
+
"z",
|
|
3131
|
+
"fn",
|
|
3132
|
+
"cb",
|
|
3133
|
+
"f",
|
|
3134
|
+
"g"
|
|
3135
|
+
].includes(lower);
|
|
3136
|
+
}
|
|
3137
|
+
function summarizeFunction(name, _signature, _language, _lineCount, receiverType, source) {
|
|
3138
|
+
if (CONSTRUCTOR_NAMES.has(name)) {
|
|
3139
|
+
const subject = receiverType || "instance";
|
|
3140
|
+
return `Initializes ${subject}`;
|
|
3141
|
+
}
|
|
3142
|
+
const lowerName = name.toLowerCase();
|
|
3143
|
+
if (lowerName.startsWith("test_") || lowerName.startsWith("test")) {
|
|
3144
|
+
const words2 = splitIdentifier(name);
|
|
3145
|
+
const testWords = words2[0].toLowerCase() === "test" ? words2.slice(1) : words2;
|
|
3146
|
+
if (testWords.length > 0) {
|
|
3147
|
+
return `Tests ${wordsToPhrase(testWords)}`;
|
|
3148
|
+
}
|
|
3149
|
+
return `Tests ${name}`;
|
|
3150
|
+
}
|
|
3151
|
+
if (isNonDescriptive(name)) {
|
|
3152
|
+
return `Function ${name}`;
|
|
3153
|
+
}
|
|
3154
|
+
const words = splitIdentifier(name);
|
|
3155
|
+
if (words.length === 0) return `Function ${name}`;
|
|
3156
|
+
const firstWord = words[0].toLowerCase();
|
|
3157
|
+
const restWords = words.slice(1);
|
|
3158
|
+
const verb = VERB_MAP[firstWord];
|
|
3159
|
+
if (verb) {
|
|
3160
|
+
const object = restWords.length > 0 ? ` ${wordsToPhrase(restWords)}` : "";
|
|
3161
|
+
let prefix = "";
|
|
3162
|
+
if (receiverType) {
|
|
3163
|
+
prefix = `${receiverType} method that `;
|
|
3164
|
+
const lowerVerb = verb.toLowerCase();
|
|
3165
|
+
const result3 = `${prefix}${lowerVerb}${object}`;
|
|
3166
|
+
const keywords3 = source ? extractKeywords(source) : [];
|
|
3167
|
+
return result3 + formatKeywords(keywords3);
|
|
3168
|
+
}
|
|
3169
|
+
const result2 = `${verb}${object}`;
|
|
3170
|
+
const keywords2 = source ? extractKeywords(source) : [];
|
|
3171
|
+
return result2 + formatKeywords(keywords2);
|
|
3172
|
+
}
|
|
3173
|
+
const phrase = wordsToPhrase(words);
|
|
3174
|
+
let result;
|
|
3175
|
+
if (receiverType) {
|
|
3176
|
+
result = `${receiverType} method for ${phrase}`;
|
|
3177
|
+
} else {
|
|
3178
|
+
result = phrase.charAt(0).toUpperCase() + phrase.slice(1);
|
|
3179
|
+
}
|
|
3180
|
+
const keywords = source ? extractKeywords(source) : [];
|
|
3181
|
+
return result + formatKeywords(keywords);
|
|
3182
|
+
}
|
|
3183
|
+
const CRUD_METHODS = /* @__PURE__ */ new Set([
|
|
3184
|
+
"create",
|
|
3185
|
+
"read",
|
|
3186
|
+
"get",
|
|
3187
|
+
"find",
|
|
3188
|
+
"update",
|
|
3189
|
+
"delete",
|
|
3190
|
+
"remove",
|
|
3191
|
+
"save",
|
|
3192
|
+
"list"
|
|
3193
|
+
]);
|
|
3194
|
+
function summarizeClass(name, childNames, source) {
|
|
3195
|
+
const words = splitIdentifier(name);
|
|
3196
|
+
const readableName = wordsToPhrase(words);
|
|
3197
|
+
const capitalizedName = readableName.charAt(0).toUpperCase() + readableName.slice(1);
|
|
3198
|
+
const lastWord = words[words.length - 1].toLowerCase();
|
|
3199
|
+
const suffixLabel = CLASS_SUFFIX_MAP[lastWord];
|
|
3200
|
+
let hasCrud = false;
|
|
3201
|
+
if (childNames && childNames.length > 0) {
|
|
3202
|
+
const lowerChildren = childNames.map(
|
|
3203
|
+
(c) => splitIdentifier(c)[0].toLowerCase()
|
|
3204
|
+
);
|
|
3205
|
+
const crudCount = lowerChildren.filter((c) => CRUD_METHODS.has(c)).length;
|
|
3206
|
+
hasCrud = crudCount >= 3;
|
|
3207
|
+
}
|
|
3208
|
+
let summary = capitalizedName;
|
|
3209
|
+
if (hasCrud) {
|
|
3210
|
+
summary += " for CRUD operations";
|
|
3211
|
+
} else if (suffixLabel) {
|
|
3212
|
+
if (words.length > 1) ;
|
|
3213
|
+
}
|
|
3214
|
+
if (childNames && childNames.length > 0) {
|
|
3215
|
+
const methodList = childNames.slice(0, 5).join(", ");
|
|
3216
|
+
const extra = childNames.length > 5 ? ` and ${childNames.length - 5} more` : "";
|
|
3217
|
+
summary += ` with methods: ${methodList}${extra}`;
|
|
3218
|
+
}
|
|
3219
|
+
const keywords = source ? extractKeywords(source) : [];
|
|
3220
|
+
return summary + formatKeywords(keywords);
|
|
3221
|
+
}
|
|
3222
|
+
function summarizeFile(fileName, symbolNames, language, _source) {
|
|
3223
|
+
const lowerFile = fileName.toLowerCase();
|
|
3224
|
+
for (const [pattern, prefix] of FILE_PATTERNS) {
|
|
3225
|
+
if (pattern.test(lowerFile)) {
|
|
3226
|
+
if (prefix.endsWith("for")) {
|
|
3227
|
+
const baseName = fileName.replace(/_test\.\w+$/, "").replace(/\.test\.\w+$/, "").replace(/\.spec\.\w+$/, "").replace(/^test_/, "").replace(/\.\w+$/, "");
|
|
3228
|
+
const subject = wordsToPhrase(splitIdentifier(baseName));
|
|
3229
|
+
return `${prefix} ${subject}`;
|
|
3230
|
+
}
|
|
3231
|
+
if (prefix.endsWith("for") || prefix === "Barrel exports for") {
|
|
3232
|
+
if (symbolNames && symbolNames.length > 0) {
|
|
3233
|
+
const listing = symbolNames.slice(0, 3).join(", ");
|
|
3234
|
+
const extra = symbolNames.length > 3 ? ` and ${symbolNames.length - 3} more` : "";
|
|
3235
|
+
return `${prefix} ${listing}${extra}`;
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
return prefix;
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
if (symbolNames && symbolNames.length > 0) {
|
|
3242
|
+
const listing = symbolNames.slice(0, 3).join(", ");
|
|
3243
|
+
const extra = symbolNames.length > 3 ? ` and ${symbolNames.length - 3} more` : "";
|
|
3244
|
+
const langNote = language ? ` ${language}` : "";
|
|
3245
|
+
return `Defines${langNote} ${listing}${extra}`;
|
|
3246
|
+
}
|
|
3247
|
+
return `Source file ${fileName}`;
|
|
3248
|
+
}
|
|
3249
|
+
function summarizeDirectory(dirName, childNames) {
|
|
3250
|
+
const lowerDir = dirName.toLowerCase();
|
|
3251
|
+
const knownPurpose = DIR_PATTERNS[lowerDir];
|
|
3252
|
+
if (knownPurpose) {
|
|
3253
|
+
if (childNames.length > 0) {
|
|
3254
|
+
const listing = childNames.slice(0, 5).join(", ");
|
|
3255
|
+
const extra = childNames.length > 5 ? ` and ${childNames.length - 5} more` : "";
|
|
3256
|
+
return `${knownPurpose} containing ${listing}${extra}`;
|
|
3257
|
+
}
|
|
3258
|
+
return knownPurpose;
|
|
3259
|
+
}
|
|
3260
|
+
if (childNames.length > 0) {
|
|
3261
|
+
const listing = childNames.slice(0, 5).join(", ");
|
|
3262
|
+
const extra = childNames.length > 5 ? ` and ${childNames.length - 5} more` : "";
|
|
3263
|
+
return `Directory containing ${listing}${extra}`;
|
|
3264
|
+
}
|
|
3265
|
+
return `Directory ${dirName}`;
|
|
3266
|
+
}
|
|
3267
|
+
function summarizeFromMetadata(meta) {
|
|
3268
|
+
if (meta.docs) {
|
|
3269
|
+
const firstSentence = meta.docs.split(/\.\s|\n/)[0].trim();
|
|
3270
|
+
if (firstSentence) {
|
|
3271
|
+
return firstSentence.endsWith(".") ? firstSentence.slice(0, -1) : firstSentence;
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
switch (meta.kind) {
|
|
3275
|
+
case "function":
|
|
3276
|
+
return summarizeFunction(
|
|
3277
|
+
meta.name,
|
|
3278
|
+
meta.signature,
|
|
3279
|
+
meta.language,
|
|
3280
|
+
meta.lineCount,
|
|
3281
|
+
meta.receiverType,
|
|
3282
|
+
meta.source
|
|
3283
|
+
);
|
|
3284
|
+
case "class":
|
|
3285
|
+
return summarizeClass(meta.name, meta.childNames, meta.source);
|
|
3286
|
+
case "file":
|
|
3287
|
+
return summarizeFile(
|
|
3288
|
+
meta.fileName || meta.name,
|
|
3289
|
+
meta.childNames,
|
|
3290
|
+
meta.language,
|
|
3291
|
+
meta.source
|
|
3292
|
+
);
|
|
3293
|
+
case "directory":
|
|
3294
|
+
return summarizeDirectory(meta.name, meta.childNames || []);
|
|
3295
|
+
default:
|
|
3296
|
+
return `${meta.kind} ${meta.name}`;
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
class TemplateSummarizer {
|
|
3300
|
+
async init() {
|
|
3301
|
+
}
|
|
3302
|
+
async summarize(source, kind) {
|
|
3303
|
+
const firstLine = source.split("\n")[0] || "";
|
|
3304
|
+
const nameMatch = firstLine.match(
|
|
3305
|
+
/(?:function|def|func|class|const|let|var|type|interface)\s+(\w+)/
|
|
3306
|
+
);
|
|
3307
|
+
const name = nameMatch?.[1] || "unknown";
|
|
3308
|
+
return summarizeFromMetadata({ name, kind, source });
|
|
3309
|
+
}
|
|
3310
|
+
async summarizeBatch(items) {
|
|
3311
|
+
return Promise.all(
|
|
3312
|
+
items.map((item) => this.summarize(item.source, item.kind))
|
|
3313
|
+
);
|
|
3314
|
+
}
|
|
3315
|
+
async dispose() {
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
const TYPE_TO_KIND = {
|
|
3319
|
+
Function: "function",
|
|
3320
|
+
Class: "class",
|
|
3321
|
+
File: "file",
|
|
3322
|
+
Directory: "directory"
|
|
3323
|
+
};
|
|
3324
|
+
function summarizeNode(node) {
|
|
3325
|
+
const kind = TYPE_TO_KIND[node.type];
|
|
3326
|
+
if (!kind) {
|
|
3327
|
+
return `${node.type} ${node.name}`;
|
|
3328
|
+
}
|
|
3329
|
+
const props = node.properties ?? {};
|
|
3330
|
+
return summarizeFromMetadata({
|
|
3331
|
+
name: node.name,
|
|
3332
|
+
kind,
|
|
3333
|
+
signature: props.signature,
|
|
3334
|
+
language: props.language,
|
|
3335
|
+
lineCount: typeof props.start_line === "number" && typeof props.end_line === "number" ? props.end_line - props.start_line + 1 : void 0,
|
|
3336
|
+
receiverType: props.receiver_type,
|
|
3337
|
+
fileName: kind === "file" ? props.path ?? node.name : void 0,
|
|
3338
|
+
childNames: props.childNames,
|
|
3339
|
+
docs: props.docs
|
|
3340
|
+
});
|
|
3341
|
+
}
|
|
3342
|
+
function* wrapWithSummaries(inner) {
|
|
3343
|
+
let summarized = 0;
|
|
3344
|
+
let total = 0;
|
|
3345
|
+
yield {
|
|
3346
|
+
kind: "stage_start",
|
|
3347
|
+
phase: "summarizing",
|
|
3348
|
+
message: "Summarizing nodes..."
|
|
3349
|
+
};
|
|
3350
|
+
for (const event of inner) {
|
|
3351
|
+
if (event.nodes) {
|
|
3352
|
+
for (const node of event.nodes) {
|
|
3353
|
+
total++;
|
|
3354
|
+
if (!node.properties?.summary) {
|
|
3355
|
+
const summary = summarizeNode(node);
|
|
3356
|
+
if (summary) {
|
|
3357
|
+
node.properties = { ...node.properties, summary };
|
|
3358
|
+
summarized++;
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
yield {
|
|
3363
|
+
kind: "stage_progress",
|
|
3364
|
+
phase: "summarizing",
|
|
3365
|
+
message: `Summarized ${summarized} of ${total} nodes`,
|
|
3366
|
+
detail: { current: summarized, total }
|
|
3367
|
+
};
|
|
3368
|
+
}
|
|
3369
|
+
yield event;
|
|
3370
|
+
}
|
|
3371
|
+
yield {
|
|
3372
|
+
kind: "stage_stop",
|
|
3373
|
+
phase: "summarizing",
|
|
3374
|
+
message: `Summarized ${summarized} nodes`
|
|
3375
|
+
};
|
|
3376
|
+
}
|
|
3377
|
+
function* corePipeline(input, ctx) {
|
|
3378
|
+
const scanResult = yield* execute$3(input, ctx);
|
|
3379
|
+
if (ctx.cancelled) return;
|
|
3380
|
+
const processingOutput = yield* execute$2(scanResult, ctx);
|
|
3381
|
+
if (ctx.cancelled) return;
|
|
3382
|
+
const finalResult = yield* execute$1(processingOutput);
|
|
3383
|
+
yield {
|
|
3384
|
+
kind: "done",
|
|
3385
|
+
phase: "resolving",
|
|
3386
|
+
message: "Pipeline complete",
|
|
3387
|
+
result: finalResult
|
|
3388
|
+
};
|
|
3389
|
+
}
|
|
3390
|
+
function* runPipeline(input, ctx, store) {
|
|
3391
|
+
const inner = wrapWithSummaries(corePipeline(input, ctx));
|
|
3392
|
+
if (store) {
|
|
3393
|
+
yield* execute(inner, store);
|
|
3394
|
+
} else {
|
|
3395
|
+
yield* inner;
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
function collectPipeline(input, ctx, store) {
|
|
3399
|
+
const events = [];
|
|
3400
|
+
const nodes = [];
|
|
3401
|
+
const relationships = [];
|
|
3402
|
+
for (const event of runPipeline(input, ctx, store)) {
|
|
3403
|
+
events.push(event);
|
|
3404
|
+
if (event.nodes) nodes.push(...event.nodes);
|
|
3405
|
+
if (event.relationships) relationships.push(...event.relationships);
|
|
3406
|
+
}
|
|
3407
|
+
return { events, nodes, relationships };
|
|
3408
|
+
}
|
|
3409
|
+
class MemoryStore {
|
|
3410
|
+
nodes = /* @__PURE__ */ new Map();
|
|
3411
|
+
relationships = /* @__PURE__ */ new Map();
|
|
3412
|
+
saveNode(node) {
|
|
3413
|
+
const existing = this.nodes.get(node.id);
|
|
3414
|
+
if (existing) {
|
|
3415
|
+
existing.properties = {
|
|
3416
|
+
...existing.properties,
|
|
3417
|
+
...node.properties ?? {}
|
|
3418
|
+
};
|
|
3419
|
+
} else {
|
|
3420
|
+
this.nodes.set(node.id, node);
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
saveRelationship(rel) {
|
|
3424
|
+
this.relationships.set(rel.id, rel);
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
const DEFAULT_SUMMARIZER_CONFIG = {
|
|
3428
|
+
enabled: true,
|
|
3429
|
+
strategy: "template",
|
|
3430
|
+
model: "Xenova/flan-t5-small",
|
|
3431
|
+
maxInputLength: 480,
|
|
3432
|
+
minLines: 5
|
|
3433
|
+
};
|
|
3434
|
+
exports.DEFAULT_SUMMARIZER_CONFIG = DEFAULT_SUMMARIZER_CONFIG;
|
|
3435
|
+
exports.MemoryStore = MemoryStore;
|
|
3436
|
+
exports.TemplateSummarizer = TemplateSummarizer;
|
|
3437
|
+
exports.addToRegistry = addToRegistry;
|
|
3438
|
+
exports.analyzeGoImports = analyzeGoImports;
|
|
3439
|
+
exports.analyzeImports = analyzeImports;
|
|
3440
|
+
exports.analyzePythonImports = analyzePythonImports;
|
|
3441
|
+
exports.analyzeRubyImports = analyzeRubyImports;
|
|
3442
|
+
exports.analyzeRustImports = analyzeRustImports;
|
|
3443
|
+
exports.analyzeTypeScriptImports = analyzeTypeScriptImports;
|
|
3444
|
+
exports.collectPipeline = collectPipeline;
|
|
3445
|
+
exports.countSymbols = countSymbols;
|
|
3446
|
+
exports.detectLanguage = detectLanguage;
|
|
3447
|
+
exports.ensureDirChain = ensureDirChain;
|
|
3448
|
+
exports.extractGeneric = extractGeneric;
|
|
3449
|
+
exports.extractGo = extractGo;
|
|
3450
|
+
exports.extractKeywords = extractKeywords;
|
|
3451
|
+
exports.extractPython = extractPython;
|
|
3452
|
+
exports.extractTypeScript = extractTypeScript;
|
|
3453
|
+
exports.getExtension = getExtension;
|
|
3454
|
+
exports.getExtractor = getExtractor;
|
|
3455
|
+
exports.getParserForLanguage = getParserForLanguage;
|
|
3456
|
+
exports.initParsers = initParsers;
|
|
3457
|
+
exports.isManifestFile = isManifestFile;
|
|
3458
|
+
exports.normalizePyName = normalizePyName;
|
|
3459
|
+
exports.npmPackageName = npmPackageName;
|
|
3460
|
+
exports.packageId = packageId;
|
|
3461
|
+
exports.packageSourceUrl = packageSourceUrl;
|
|
3462
|
+
exports.parentDir = parentDir$1;
|
|
3463
|
+
exports.parseCargoToml = parseCargoToml;
|
|
3464
|
+
exports.parseFile = parseFile;
|
|
3465
|
+
exports.parseGoMod = parseGoMod;
|
|
3466
|
+
exports.parseManifest = parseManifest;
|
|
3467
|
+
exports.parsePackageJson = parsePackageJson;
|
|
3468
|
+
exports.parsePyprojectToml = parsePyprojectToml;
|
|
3469
|
+
exports.parseRequirementsTxt = parseRequirementsTxt;
|
|
3470
|
+
exports.processSymbol = processSymbol;
|
|
3471
|
+
exports.resetDirIndexCache = resetDirIndexCache;
|
|
3472
|
+
exports.resolveCalls = resolveCalls;
|
|
3473
|
+
exports.resolvedCallsToRelationships = resolvedCallsToRelationships;
|
|
3474
|
+
exports.runPipeline = runPipeline;
|
|
3475
|
+
exports.splitIdentifier = splitIdentifier;
|
|
3476
|
+
exports.summarizeClass = summarizeClass;
|
|
3477
|
+
exports.summarizeDirectory = summarizeDirectory;
|
|
3478
|
+
exports.summarizeFile = summarizeFile;
|
|
3479
|
+
exports.summarizeFromMetadata = summarizeFromMetadata;
|
|
3480
|
+
exports.summarizeFunction = summarizeFunction;
|
|
3481
|
+
//# sourceMappingURL=pipeline.cjs.map
|