depgraph-core 1.0.3 → 1.5.1
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/.vscode/depgraph-output.json +4634 -0
- package/README.md +64 -37
- package/depgraph-output.json +404 -47
- package/depgraph.js +1295 -74
- package/docs/stage-impact.md +4 -4
- package/docs/stage-output.md +2 -2
- package/package.json +4 -1
package/depgraph.js
CHANGED
|
@@ -87,12 +87,37 @@ var require_javascript = __commonJS({
|
|
|
87
87
|
var registry_1 = require_registry();
|
|
88
88
|
var constants_1 = require_constants();
|
|
89
89
|
function estimateComplexity(code, name) {
|
|
90
|
-
const
|
|
91
|
-
}`, "m")
|
|
92
|
-
|
|
90
|
+
const lines = code.split("\n");
|
|
91
|
+
const nameRegex = new RegExp(`(?:(?:async\\s+)?function(?:\\s*\\*|\\s+)|(?:const|let|var)\\s+)${name}\\b|\\b${name}\\s*(?:<[^>]*>)?\\s*\\(`, "m");
|
|
92
|
+
const defLineIdx = lines.findIndex((l) => nameRegex.test(l));
|
|
93
|
+
if (defLineIdx === -1)
|
|
94
|
+
return "low";
|
|
95
|
+
let startLine = defLineIdx;
|
|
96
|
+
while (startLine < lines.length && !lines[startLine].includes("{")) {
|
|
97
|
+
startLine++;
|
|
98
|
+
}
|
|
99
|
+
if (startLine >= lines.length)
|
|
93
100
|
return "low";
|
|
94
|
-
|
|
95
|
-
|
|
101
|
+
let braceCount = 0;
|
|
102
|
+
let started = false;
|
|
103
|
+
const bodyLines = [];
|
|
104
|
+
for (let i = startLine; i < lines.length; i++) {
|
|
105
|
+
const line = lines[i];
|
|
106
|
+
for (const char of line) {
|
|
107
|
+
if (char === "{") {
|
|
108
|
+
braceCount++;
|
|
109
|
+
started = true;
|
|
110
|
+
} else if (char === "}") {
|
|
111
|
+
braceCount--;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
bodyLines.push(line);
|
|
115
|
+
if (started && braceCount <= 0) {
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const body = bodyLines.join("\n");
|
|
120
|
+
const branches = (body.match(/\b(if|else\s+if|for|while|switch|case|catch|&&|\|\||\?\?)\b|\?[^:]*:/g) || []).length;
|
|
96
121
|
if (branches <= constants_1.COMPLEXITY_THRESHOLDS.low)
|
|
97
122
|
return "low";
|
|
98
123
|
if (branches <= constants_1.COMPLEXITY_THRESHOLDS.medium)
|
|
@@ -100,44 +125,69 @@ var require_javascript = __commonJS({
|
|
|
100
125
|
return "high";
|
|
101
126
|
}
|
|
102
127
|
exports2.jsEntityPatterns = [
|
|
103
|
-
// React components
|
|
128
|
+
// React components: wrapped in memo/forwardRef
|
|
129
|
+
{
|
|
130
|
+
regex: /^(?:export\s+)?(?:default\s+)?(?:const|let|var)\s+([A-Z]\w*)\s*=\s*(?:React\.)?(?:memo|forwardRef)\(/gm,
|
|
131
|
+
type: "component"
|
|
132
|
+
},
|
|
133
|
+
// React components: PascalCase arrow functions
|
|
134
|
+
{
|
|
135
|
+
regex: /^(?:export\s+)?(?:default\s+)?(?:const|let|var)\s+([A-Z]\w*)\s*=\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_]\w*)\s*=>/gm,
|
|
136
|
+
type: "component"
|
|
137
|
+
},
|
|
138
|
+
// React components: PascalCase function declarations
|
|
104
139
|
{
|
|
105
|
-
regex: /^(?:export\s+)?
|
|
140
|
+
regex: /^(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s+([A-Z]\w*)\s*(?:<[^>]*>)?\s*\(/gm,
|
|
106
141
|
type: "component"
|
|
107
142
|
},
|
|
108
|
-
// React hooks
|
|
143
|
+
// React hooks: camelCase starting with "use" (arrow functions or const assignments)
|
|
144
|
+
{
|
|
145
|
+
regex: /^(?:export\s+)?(?:default\s+)?(?:const|let|var)\s+(use[A-Z]\w*)\s*=/gm,
|
|
146
|
+
type: "hook"
|
|
147
|
+
},
|
|
148
|
+
// React hooks: camelCase starting with "use" (function declarations)
|
|
109
149
|
{
|
|
110
|
-
regex: /^(?:export\s+)?(?:
|
|
150
|
+
regex: /^(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s+(use[A-Z]\w*)\s*(?:<[^>]*>)?\s*\(/gm,
|
|
111
151
|
type: "hook"
|
|
112
152
|
},
|
|
113
|
-
// regular functions
|
|
153
|
+
// regular and async function declarations (including generator functions)
|
|
154
|
+
{
|
|
155
|
+
regex: /^(?:export\s+)?(?:default\s+)?(?:async\s+)?function(?:\s*\*\s*|\s+)([A-Za-z_]\w*)\s*(?:<[^>]*>)?\s*\(/gm,
|
|
156
|
+
type: "function"
|
|
157
|
+
},
|
|
158
|
+
// arrow functions assigned to const / let / var
|
|
114
159
|
{
|
|
115
|
-
regex: /^(?:export\s+)?(?:
|
|
160
|
+
regex: /^(?:export\s+)?(?:const|let|var)\s+([A-Za-z_]\w*)\s*=\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_]\w*)\s*=>/gm,
|
|
116
161
|
type: "function"
|
|
117
162
|
},
|
|
118
|
-
//
|
|
163
|
+
// function expressions assigned to const / let / var
|
|
119
164
|
{
|
|
120
|
-
regex: /^(?:export\s+)?const\s+(\w
|
|
165
|
+
regex: /^(?:export\s+)?(?:const|let|var)\s+([A-Za-z_]\w*)\s*=\s*(?:async\s*)?function/gm,
|
|
121
166
|
type: "function"
|
|
122
167
|
},
|
|
123
|
-
// classes
|
|
168
|
+
// classes (regular, exported, abstract)
|
|
124
169
|
{
|
|
125
|
-
regex: /^(?:export\s+)?(?:default\s+)?class\s+(\w
|
|
170
|
+
regex: /^(?:export\s+)?(?:default\s+)?(?:abstract\s+)?class\s+([A-Za-z_]\w*)/gm,
|
|
126
171
|
type: "class"
|
|
127
172
|
},
|
|
128
173
|
// TypeScript interfaces
|
|
129
174
|
{
|
|
130
|
-
regex: /^(?:export\s+)?interface\s+(\w
|
|
175
|
+
regex: /^(?:export\s+)?(?:default\s+)?interface\s+([A-Za-z_]\w*)/gm,
|
|
131
176
|
type: "interface"
|
|
132
177
|
},
|
|
133
178
|
// TypeScript types
|
|
134
179
|
{
|
|
135
|
-
regex: /^(?:export\s+)?type\s+(\w
|
|
180
|
+
regex: /^(?:export\s+)?(?:default\s+)?type\s+([A-Za-z_]\w*)\s*(?:<[^>]*>)?\s*=/gm,
|
|
136
181
|
type: "type"
|
|
137
182
|
},
|
|
138
|
-
//
|
|
183
|
+
// TypeScript enums (regular or const enum)
|
|
139
184
|
{
|
|
140
|
-
regex:
|
|
185
|
+
regex: /^(?:export\s+)?(?:const\s+)?enum\s+([A-Za-z_]\w*)/gm,
|
|
186
|
+
type: "class"
|
|
187
|
+
},
|
|
188
|
+
// Express / router routes (capture group 1 = method, group 2 = path — skipped in gitdiff context matching)
|
|
189
|
+
{
|
|
190
|
+
regex: /(?:app|router|server)\.(get|post|put|delete|patch|options|head)\s*\(\s*['"]([^'"]+)['"]/gm,
|
|
141
191
|
type: "api"
|
|
142
192
|
}
|
|
143
193
|
];
|
|
@@ -173,46 +223,114 @@ var require_javascript = __commonJS({
|
|
|
173
223
|
}
|
|
174
224
|
function extractImports(code) {
|
|
175
225
|
const imports = [];
|
|
176
|
-
const
|
|
226
|
+
const combinedPattern = /^import\s+(?:type\s+)?([A-Za-z_$]\w*)\s*,\s*(?:\{([^}]+)\}|\*\s+as\s+([A-Za-z_$]\w*))\s+from\s+['"]([^'"]+)['"]/gm;
|
|
177
227
|
let match;
|
|
228
|
+
while ((match = combinedPattern.exec(code)) !== null) {
|
|
229
|
+
const defaultName = match[1];
|
|
230
|
+
const namedClause = match[2];
|
|
231
|
+
const nsName = match[3];
|
|
232
|
+
const source = match[4];
|
|
233
|
+
const names = [defaultName];
|
|
234
|
+
if (namedClause) {
|
|
235
|
+
const parsedNamed = namedClause.split(",").map((n) => n.trim().replace(/^type\s+/, "").replace(/\s+as\s+\w+$/, "").trim()).filter((n) => n.length > 0);
|
|
236
|
+
names.push(...parsedNamed);
|
|
237
|
+
}
|
|
238
|
+
if (nsName) {
|
|
239
|
+
names.push(nsName);
|
|
240
|
+
}
|
|
241
|
+
imports.push({
|
|
242
|
+
source,
|
|
243
|
+
names: [...new Set(names)],
|
|
244
|
+
isLocal: source.startsWith(".") || source.startsWith("/")
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
const namedPattern = /^import\s+(?:type\s+)?\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/gm;
|
|
178
248
|
while ((match = namedPattern.exec(code)) !== null) {
|
|
179
|
-
const names = match[1].split(",").map((n) => n.trim().replace(/\s+as\s+\w+/, ""));
|
|
180
249
|
const source = match[2];
|
|
250
|
+
if (imports.some((i) => i.source === source))
|
|
251
|
+
continue;
|
|
252
|
+
const names = match[1].split(",").map((n) => n.trim().replace(/^type\s+/, "").replace(/\s+as\s+\w+$/, "").trim()).filter((n) => n.length > 0);
|
|
181
253
|
imports.push({
|
|
182
254
|
source,
|
|
183
|
-
names,
|
|
184
|
-
isLocal: source.startsWith(".")
|
|
255
|
+
names: [...new Set(names)],
|
|
256
|
+
isLocal: source.startsWith(".") || source.startsWith("/")
|
|
185
257
|
});
|
|
186
258
|
}
|
|
187
|
-
const defaultPattern = /^import\s+(\
|
|
259
|
+
const defaultPattern = /^import\s+(?:type\s+)?([A-Za-z_$]\w*)\s+from\s+['"]([^'"]+)['"]/gm;
|
|
188
260
|
while ((match = defaultPattern.exec(code)) !== null) {
|
|
261
|
+
const source = match[2];
|
|
262
|
+
if (imports.some((i) => i.source === source))
|
|
263
|
+
continue;
|
|
264
|
+
imports.push({
|
|
265
|
+
source,
|
|
266
|
+
names: [match[1]],
|
|
267
|
+
isLocal: source.startsWith(".") || source.startsWith("/")
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
const nsPattern = /^import\s+\*\s+as\s+([A-Za-z_$]\w*)\s+from\s+['"]([^'"]+)['"]/gm;
|
|
271
|
+
while ((match = nsPattern.exec(code)) !== null) {
|
|
272
|
+
const source = match[2];
|
|
273
|
+
if (imports.some((i) => i.source === source))
|
|
274
|
+
continue;
|
|
189
275
|
imports.push({
|
|
190
|
-
source
|
|
276
|
+
source,
|
|
191
277
|
names: [match[1]],
|
|
192
|
-
isLocal:
|
|
278
|
+
isLocal: source.startsWith(".") || source.startsWith("/")
|
|
193
279
|
});
|
|
194
280
|
}
|
|
195
281
|
const requirePattern = /(?:const|let|var)\s+\{?([^}=]+)\}?\s*=\s*require\s*\(\s*['"]([^'"]+)['"]\s*\)/gm;
|
|
196
282
|
while ((match = requirePattern.exec(code)) !== null) {
|
|
197
|
-
const
|
|
283
|
+
const rawNames = match[1];
|
|
284
|
+
const source = match[2];
|
|
285
|
+
const names = rawNames.split(",").map((n) => n.trim().replace(/^\w+:\s*/, "").trim()).filter((n) => n.length > 0);
|
|
286
|
+
imports.push({
|
|
287
|
+
source,
|
|
288
|
+
names: [...new Set(names)],
|
|
289
|
+
isLocal: source.startsWith(".") || source.startsWith("/")
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
const reexportPattern = /^export\s+(?:\{([^}]+)\}|\*\s+as\s+([A-Za-z_$]\w*)|\*)\s+from\s+['"]([^'"]+)['"]/gm;
|
|
293
|
+
while ((match = reexportPattern.exec(code)) !== null) {
|
|
294
|
+
const namedClause = match[1];
|
|
295
|
+
const nsAlias = match[2];
|
|
296
|
+
const source = match[3];
|
|
297
|
+
let names = [];
|
|
298
|
+
if (namedClause) {
|
|
299
|
+
names = namedClause.split(",").map((n) => n.trim().replace(/^type\s+/, "").replace(/\s+as\s+\w+$/, "").trim()).filter((n) => n.length > 0);
|
|
300
|
+
} else if (nsAlias) {
|
|
301
|
+
names = [nsAlias];
|
|
302
|
+
} else {
|
|
303
|
+
names = ["*"];
|
|
304
|
+
}
|
|
198
305
|
imports.push({
|
|
199
|
-
source
|
|
200
|
-
names,
|
|
201
|
-
isLocal:
|
|
306
|
+
source,
|
|
307
|
+
names: [...new Set(names)],
|
|
308
|
+
isLocal: source.startsWith(".") || source.startsWith("/")
|
|
202
309
|
});
|
|
203
310
|
}
|
|
204
311
|
return imports;
|
|
205
312
|
}
|
|
206
313
|
function extractExports(code) {
|
|
207
314
|
const exports3 = [];
|
|
208
|
-
const namedPattern = /^export\s+(?:default\s+)?(?:async\s+)?(?:function|class|const|let|var|type|interface)\s+(\w
|
|
315
|
+
const namedPattern = /^export\s+(?:default\s+)?(?:async\s+|abstract\s+)?(?:function(?:\s*\*|\s+)|class|const|let|var|type|interface|enum)\s+([A-Za-z_$]\w*)/gm;
|
|
209
316
|
let match;
|
|
210
317
|
while ((match = namedPattern.exec(code)) !== null) {
|
|
211
318
|
exports3.push(match[1]);
|
|
212
319
|
}
|
|
213
|
-
const
|
|
320
|
+
const defaultIdentPattern = /^export\s+default\s+([A-Za-z_$]\w*)\s*(?:;|$)/gm;
|
|
321
|
+
while ((match = defaultIdentPattern.exec(code)) !== null) {
|
|
322
|
+
if (!["function", "class", "interface", "abstract"].includes(match[1])) {
|
|
323
|
+
exports3.push(match[1]);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
const listPattern = /^export\s+\{([^}]+)\}(?!\s*from)/gm;
|
|
214
327
|
while ((match = listPattern.exec(code)) !== null) {
|
|
215
|
-
const names = match[1].split(",").map((n) => n.trim());
|
|
328
|
+
const names = match[1].split(",").map((n) => n.trim().replace(/^type\s+/, "").replace(/^\w+\s+as\s+/, "").trim()).filter((n) => n.length > 0);
|
|
329
|
+
exports3.push(...names);
|
|
330
|
+
}
|
|
331
|
+
const reexportPattern = /^export\s+\{([^}]+)\}\s+from/gm;
|
|
332
|
+
while ((match = reexportPattern.exec(code)) !== null) {
|
|
333
|
+
const names = match[1].split(",").map((n) => n.trim().replace(/^type\s+/, "").replace(/^\w+\s+as\s+/, "").trim()).filter((n) => n.length > 0);
|
|
216
334
|
exports3.push(...names);
|
|
217
335
|
}
|
|
218
336
|
return [...new Set(exports3)];
|
|
@@ -237,13 +355,22 @@ var require_python = __commonJS({
|
|
|
237
355
|
exports2.pyEntityPatterns = void 0;
|
|
238
356
|
var registry_1 = require_registry();
|
|
239
357
|
var constants_1 = require_constants();
|
|
358
|
+
function escapeRegex(s) {
|
|
359
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
360
|
+
}
|
|
240
361
|
function estimateComplexity(code, name) {
|
|
241
362
|
const lines = code.split("\n");
|
|
242
|
-
const
|
|
363
|
+
const defRegex = new RegExp(`^[ \\t]*(?:async\\s+)?def\\s+${escapeRegex(name)}\\s*\\(`, "m");
|
|
364
|
+
const defLine = lines.findIndex((l) => defRegex.test(l));
|
|
243
365
|
if (defLine === -1)
|
|
244
366
|
return "low";
|
|
367
|
+
let bodyStart = defLine;
|
|
368
|
+
while (bodyStart < lines.length && !lines[bodyStart].includes(":")) {
|
|
369
|
+
bodyStart++;
|
|
370
|
+
}
|
|
371
|
+
bodyStart++;
|
|
245
372
|
const bodyLines = [];
|
|
246
|
-
for (let i =
|
|
373
|
+
for (let i = bodyStart; i < lines.length; i++) {
|
|
247
374
|
const line = lines[i];
|
|
248
375
|
if (line.trim() === "")
|
|
249
376
|
continue;
|
|
@@ -251,8 +378,7 @@ var require_python = __commonJS({
|
|
|
251
378
|
break;
|
|
252
379
|
bodyLines.push(line);
|
|
253
380
|
}
|
|
254
|
-
const
|
|
255
|
-
const branches = (body.match(/\b(if|elif|else|for|while|except|and|or)\b/g) || []).length;
|
|
381
|
+
const branches = (bodyLines.join("\n").match(/\b(if|elif|else|for|while|except|and|or|match|case)\b/g) || []).length;
|
|
256
382
|
if (branches <= constants_1.COMPLEXITY_THRESHOLDS.low)
|
|
257
383
|
return "low";
|
|
258
384
|
if (branches <= constants_1.COMPLEXITY_THRESHOLDS.medium)
|
|
@@ -260,14 +386,14 @@ var require_python = __commonJS({
|
|
|
260
386
|
return "high";
|
|
261
387
|
}
|
|
262
388
|
exports2.pyEntityPatterns = [
|
|
263
|
-
//
|
|
389
|
+
// functions and methods (including async def)
|
|
264
390
|
{
|
|
265
|
-
regex: /^(?:async\s+)?def\s+(\w
|
|
391
|
+
regex: /^[ \t]*(?:async\s+)?def\s+([A-Za-z_]\w*)\s*\(/gm,
|
|
266
392
|
type: "function"
|
|
267
393
|
},
|
|
268
|
-
// classes
|
|
394
|
+
// classes (with optional generic parameters [T] and base classes (Base))
|
|
269
395
|
{
|
|
270
|
-
regex: /^class\s+(\w
|
|
396
|
+
regex: /^[ \t]*class\s+([A-Za-z_]\w*)(?:\s*\[[^\]]*\])?(?:\s*\([^)]*\))?\s*:/gm,
|
|
271
397
|
type: "class"
|
|
272
398
|
}
|
|
273
399
|
];
|
|
@@ -280,10 +406,10 @@ var require_python = __commonJS({
|
|
|
280
406
|
const name = match[1];
|
|
281
407
|
if (type === "function" && name.startsWith("__") && name.endsWith("__"))
|
|
282
408
|
continue;
|
|
283
|
-
if (entities.some((e) => e.name === name))
|
|
284
|
-
continue;
|
|
285
409
|
const upToMatch = code.slice(0, match.index);
|
|
286
410
|
const line = upToMatch.split("\n").length;
|
|
411
|
+
if (entities.some((e) => e.name === name && e.line === line))
|
|
412
|
+
continue;
|
|
287
413
|
entities.push({
|
|
288
414
|
name,
|
|
289
415
|
type,
|
|
@@ -294,33 +420,43 @@ var require_python = __commonJS({
|
|
|
294
420
|
}
|
|
295
421
|
return entities;
|
|
296
422
|
}
|
|
423
|
+
function stripAlias(name) {
|
|
424
|
+
return name.replace(/\s+as\s+[A-Za-z_]\w*$/, "").trim();
|
|
425
|
+
}
|
|
297
426
|
function extractImports(code) {
|
|
298
427
|
const imports = [];
|
|
428
|
+
const normalised = code.replace(/^(from\s+[\w.]+\s+import\s*)\(\s*([\s\S]*?)\)/gm, (_, prefix, body) => prefix + body.replace(/\s*\n\s*/g, ", "));
|
|
299
429
|
const fromPattern = /^from\s+([\w.]+)\s+import\s+(.+)$/gm;
|
|
300
430
|
let match;
|
|
301
|
-
while ((match = fromPattern.exec(
|
|
431
|
+
while ((match = fromPattern.exec(normalised)) !== null) {
|
|
302
432
|
const source = match[1];
|
|
303
|
-
const
|
|
304
|
-
const
|
|
305
|
-
imports.push({ source, names, isLocal });
|
|
433
|
+
const rawNames = match[2].replace(/#.*$/, "");
|
|
434
|
+
const names = rawNames.split(",").map((n) => stripAlias(n.trim())).filter((n) => n.length > 0 && n !== "*");
|
|
435
|
+
imports.push({ source, names, isLocal: source.startsWith(".") });
|
|
306
436
|
}
|
|
307
|
-
const importPattern = /^import\s+([
|
|
308
|
-
while ((match = importPattern.exec(
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
437
|
+
const importPattern = /^import\s+([^#\n]+)/gm;
|
|
438
|
+
while ((match = importPattern.exec(normalised)) !== null) {
|
|
439
|
+
const modules = match[1].split(",").map((m) => m.trim());
|
|
440
|
+
for (const mod of modules) {
|
|
441
|
+
if (!mod)
|
|
442
|
+
continue;
|
|
443
|
+
const cleanMod = stripAlias(mod);
|
|
444
|
+
if (!cleanMod)
|
|
445
|
+
continue;
|
|
446
|
+
imports.push({
|
|
447
|
+
source: cleanMod,
|
|
448
|
+
names: [cleanMod],
|
|
449
|
+
isLocal: cleanMod.startsWith(".")
|
|
450
|
+
});
|
|
451
|
+
}
|
|
316
452
|
}
|
|
317
453
|
return imports;
|
|
318
454
|
}
|
|
319
455
|
function extractExports(code) {
|
|
320
|
-
const allMatch = code.match(/__all__\s*=\s
|
|
456
|
+
const allMatch = code.match(/__all__\s*=\s*[\[\(]([\s\S]*?)[\]\)]/);
|
|
321
457
|
if (!allMatch)
|
|
322
458
|
return [];
|
|
323
|
-
return allMatch[1].split(",").map((n) => n.trim().replace(/['"]/g, "")).filter((n) => n.length > 0);
|
|
459
|
+
return allMatch[1].split(",").map((n) => n.trim().replace(/['"]/g, "").replace(/#.*$/, "").trim()).filter((n) => n.length > 0);
|
|
324
460
|
}
|
|
325
461
|
var PythonParser = {
|
|
326
462
|
lang: "py",
|
|
@@ -341,16 +477,60 @@ var require_go = __commonJS({
|
|
|
341
477
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
342
478
|
exports2.goEntityPatterns = void 0;
|
|
343
479
|
var registry_1 = require_registry();
|
|
480
|
+
var constants_1 = require_constants();
|
|
481
|
+
function estimateComplexity(code, name) {
|
|
482
|
+
const lines = code.split("\n");
|
|
483
|
+
const funcRegex = new RegExp(`func\\s+(?:\\([^)]*\\)\\s+)?${name}\\s*(?:\\[[^\\]]*\\])?\\s*\\(`, "m");
|
|
484
|
+
const defLineIdx = lines.findIndex((l) => funcRegex.test(l));
|
|
485
|
+
if (defLineIdx === -1)
|
|
486
|
+
return "low";
|
|
487
|
+
let startLine = defLineIdx;
|
|
488
|
+
while (startLine < lines.length && !lines[startLine].includes("{")) {
|
|
489
|
+
startLine++;
|
|
490
|
+
}
|
|
491
|
+
if (startLine >= lines.length)
|
|
492
|
+
return "low";
|
|
493
|
+
let braceCount = 0;
|
|
494
|
+
let started = false;
|
|
495
|
+
const bodyLines = [];
|
|
496
|
+
for (let i = startLine; i < lines.length; i++) {
|
|
497
|
+
const line = lines[i];
|
|
498
|
+
for (const char of line) {
|
|
499
|
+
if (char === "{") {
|
|
500
|
+
braceCount++;
|
|
501
|
+
started = true;
|
|
502
|
+
} else if (char === "}") {
|
|
503
|
+
braceCount--;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
bodyLines.push(line);
|
|
507
|
+
if (started && braceCount <= 0) {
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
const body = bodyLines.join("\n");
|
|
512
|
+
const branches = (body.match(/\b(if|else\s+if|for|switch|case|select|&&|\|\|)\b/g) || []).length;
|
|
513
|
+
if (branches <= constants_1.COMPLEXITY_THRESHOLDS.low)
|
|
514
|
+
return "low";
|
|
515
|
+
if (branches <= constants_1.COMPLEXITY_THRESHOLDS.medium)
|
|
516
|
+
return "medium";
|
|
517
|
+
return "high";
|
|
518
|
+
}
|
|
344
519
|
exports2.goEntityPatterns = [
|
|
345
|
-
// functions
|
|
520
|
+
// functions and methods with optional receiver and type parameters (generics)
|
|
346
521
|
{
|
|
347
|
-
regex: /^func\s+(?:\(
|
|
522
|
+
regex: /^func\s+(?:\([^)]*\)\s+)?([A-Za-z_]\w*)\s*(?:\[[^\]]*\])?\s*\(/gm,
|
|
348
523
|
type: "function"
|
|
349
524
|
},
|
|
350
|
-
// type declarations (structs, interfaces
|
|
525
|
+
// type declarations (structs, interfaces) with optional type parameters
|
|
351
526
|
{
|
|
352
|
-
regex: /^type\s+(\w
|
|
527
|
+
regex: /^type\s+([A-Za-z_]\w*)\s*(?:\[[^\]]*\])?\s+(?:struct|interface)/gm,
|
|
353
528
|
type: "class"
|
|
529
|
+
},
|
|
530
|
+
// type aliases and custom types (e.g. type HandlerFunc func(...), type MyInt int)
|
|
531
|
+
{
|
|
532
|
+
regex: /^type\s+([A-Za-z_]\w*)\s*(?:\[[^\]]*\])?\s+(?!(?:struct|interface)\b)[A-Za-z_\[\]\*]/gm,
|
|
533
|
+
type: "type"
|
|
354
534
|
}
|
|
355
535
|
];
|
|
356
536
|
function extractEntities(code, filePath) {
|
|
@@ -364,25 +544,89 @@ var require_go = __commonJS({
|
|
|
364
544
|
continue;
|
|
365
545
|
const upToMatch = code.slice(0, match.index);
|
|
366
546
|
const line = upToMatch.split("\n").length;
|
|
367
|
-
entities.push({
|
|
547
|
+
entities.push({
|
|
548
|
+
name,
|
|
549
|
+
type,
|
|
550
|
+
line,
|
|
551
|
+
complexity: type === "function" ? estimateComplexity(code, name) : "low"
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
const blockTypePattern = /^type\s*\(\s*\n?([\s\S]*?)\n\s*\)/gm;
|
|
556
|
+
let blockMatch;
|
|
557
|
+
while ((blockMatch = blockTypePattern.exec(code)) !== null) {
|
|
558
|
+
const blockContent = blockMatch[1];
|
|
559
|
+
const blockStartLine = code.slice(0, blockMatch.index).split("\n").length;
|
|
560
|
+
const lines = blockContent.split("\n");
|
|
561
|
+
let braceDepth = 0;
|
|
562
|
+
for (let i = 0; i < lines.length; i++) {
|
|
563
|
+
const line = lines[i].trim();
|
|
564
|
+
if (braceDepth === 0 && line.length > 0 && !line.startsWith("//")) {
|
|
565
|
+
const structInterfaceMatch = line.match(/^([A-Za-z_]\w*)\s*(?:\[[^\]]*\])?\s+(struct|interface)/);
|
|
566
|
+
if (structInterfaceMatch) {
|
|
567
|
+
const name = structInterfaceMatch[1];
|
|
568
|
+
if (!entities.some((e) => e.name === name)) {
|
|
569
|
+
entities.push({
|
|
570
|
+
name,
|
|
571
|
+
type: "class",
|
|
572
|
+
line: blockStartLine + i + 1,
|
|
573
|
+
complexity: "low"
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
} else {
|
|
577
|
+
const aliasMatch = line.match(/^([A-Za-z_]\w*)\s*(?:\[[^\]]*\])?\s+(?:=\s*)?(?!(?:struct|interface)\b)[A-Za-z_\[\]\*]/);
|
|
578
|
+
if (aliasMatch) {
|
|
579
|
+
const name = aliasMatch[1];
|
|
580
|
+
if (!entities.some((e) => e.name === name)) {
|
|
581
|
+
entities.push({
|
|
582
|
+
name,
|
|
583
|
+
type: "type",
|
|
584
|
+
line: blockStartLine + i + 1,
|
|
585
|
+
complexity: "low"
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
for (const ch of line) {
|
|
592
|
+
if (ch === "{")
|
|
593
|
+
braceDepth++;
|
|
594
|
+
else if (ch === "}")
|
|
595
|
+
braceDepth--;
|
|
596
|
+
}
|
|
368
597
|
}
|
|
369
598
|
}
|
|
370
599
|
return entities;
|
|
371
600
|
}
|
|
372
601
|
function extractImports(code) {
|
|
373
602
|
const imports = [];
|
|
374
|
-
const singlePattern = /^import\s+(
|
|
603
|
+
const singlePattern = /^import\s+(?:([A-Za-z_.\w]+)\s+)?["']([^"']+)["']/gm;
|
|
375
604
|
let match;
|
|
376
605
|
while ((match = singlePattern.exec(code)) !== null) {
|
|
377
|
-
|
|
606
|
+
const alias = match[1];
|
|
607
|
+
const source = match[2];
|
|
608
|
+
const pkgName = alias && alias !== "_" && alias !== "." ? alias : source.split("/").pop() || source;
|
|
609
|
+
imports.push({
|
|
610
|
+
source,
|
|
611
|
+
names: [pkgName],
|
|
612
|
+
isLocal: source.startsWith(".") || source.startsWith("/")
|
|
613
|
+
});
|
|
378
614
|
}
|
|
379
|
-
const blockPattern = /import\s
|
|
615
|
+
const blockPattern = /import\s*\(\s*([\s\S]*?)\s*\)/gm;
|
|
380
616
|
while ((match = blockPattern.exec(code)) !== null) {
|
|
381
617
|
const lines = match[1].split("\n");
|
|
382
618
|
for (const line of lines) {
|
|
383
|
-
const
|
|
619
|
+
const cleanLine = line.replace(/\/\/.*$/, "").trim();
|
|
620
|
+
const pkgMatch = cleanLine.match(/^(?:([A-Za-z_.\w]+)\s+)?["']([^"']+)["']/);
|
|
384
621
|
if (pkgMatch) {
|
|
385
|
-
|
|
622
|
+
const alias = pkgMatch[1];
|
|
623
|
+
const source = pkgMatch[2];
|
|
624
|
+
const pkgName = alias && alias !== "_" && alias !== "." ? alias : source.split("/").pop() || source;
|
|
625
|
+
imports.push({
|
|
626
|
+
source,
|
|
627
|
+
names: [pkgName],
|
|
628
|
+
isLocal: source.startsWith(".") || source.startsWith("/")
|
|
629
|
+
});
|
|
386
630
|
}
|
|
387
631
|
}
|
|
388
632
|
}
|
|
@@ -390,11 +634,51 @@ var require_go = __commonJS({
|
|
|
390
634
|
}
|
|
391
635
|
function extractExports(code) {
|
|
392
636
|
const exports3 = [];
|
|
393
|
-
const
|
|
637
|
+
const funcPattern = /^func\s+(?:\([^)]*\)\s+)?([A-Z]\w*)\s*(?:\[[^\]]*\])?\s*\(/gm;
|
|
394
638
|
let match;
|
|
395
|
-
while ((match =
|
|
639
|
+
while ((match = funcPattern.exec(code)) !== null) {
|
|
640
|
+
exports3.push(match[1]);
|
|
641
|
+
}
|
|
642
|
+
const typePattern = /^type\s+([A-Z]\w*)/gm;
|
|
643
|
+
while ((match = typePattern.exec(code)) !== null) {
|
|
644
|
+
exports3.push(match[1]);
|
|
645
|
+
}
|
|
646
|
+
const blockTypePattern = /^type\s*\(\s*\n?([\s\S]*?)\n\s*\)/gm;
|
|
647
|
+
let blockTypeMatch;
|
|
648
|
+
while ((blockTypeMatch = blockTypePattern.exec(code)) !== null) {
|
|
649
|
+
const lines = blockTypeMatch[1].split("\n");
|
|
650
|
+
let braceDepth = 0;
|
|
651
|
+
for (const line of lines) {
|
|
652
|
+
const trimmed = line.trim();
|
|
653
|
+
if (braceDepth === 0 && trimmed.length > 0 && !trimmed.startsWith("//")) {
|
|
654
|
+
const m = trimmed.match(/^([A-Z]\w*)/);
|
|
655
|
+
if (m)
|
|
656
|
+
exports3.push(m[1]);
|
|
657
|
+
}
|
|
658
|
+
for (const ch of trimmed) {
|
|
659
|
+
if (ch === "{")
|
|
660
|
+
braceDepth++;
|
|
661
|
+
else if (ch === "}")
|
|
662
|
+
braceDepth--;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
const constVarPattern = /^(?:const|var)\s+([A-Z]\w*)/gm;
|
|
667
|
+
while ((match = constVarPattern.exec(code)) !== null) {
|
|
396
668
|
exports3.push(match[1]);
|
|
397
669
|
}
|
|
670
|
+
const blockConstVarPattern = /^(?:const|var)\s*\(\s*\n?([\s\S]*?)\n\s*\)/gm;
|
|
671
|
+
while ((match = blockConstVarPattern.exec(code)) !== null) {
|
|
672
|
+
const lines = match[1].split("\n");
|
|
673
|
+
for (const line of lines) {
|
|
674
|
+
const trimmed = line.trim();
|
|
675
|
+
if (!trimmed.startsWith("//")) {
|
|
676
|
+
const m = trimmed.match(/^([A-Z]\w*)/);
|
|
677
|
+
if (m)
|
|
678
|
+
exports3.push(m[1]);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
398
682
|
return [...new Set(exports3)];
|
|
399
683
|
}
|
|
400
684
|
var GoParser = {
|
|
@@ -409,6 +693,933 @@ var require_go = __commonJS({
|
|
|
409
693
|
}
|
|
410
694
|
});
|
|
411
695
|
|
|
696
|
+
// dist/languages/csharp.js
|
|
697
|
+
var require_csharp = __commonJS({
|
|
698
|
+
"dist/languages/csharp.js"(exports2) {
|
|
699
|
+
"use strict";
|
|
700
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
701
|
+
exports2.csharpEntityPatterns = void 0;
|
|
702
|
+
var registry_1 = require_registry();
|
|
703
|
+
var constants_1 = require_constants();
|
|
704
|
+
function estimateComplexity(code, name) {
|
|
705
|
+
const lines = code.split("\n");
|
|
706
|
+
const methodRegex = new RegExp(`(?:(?:public|private|protected|internal|static|async|virtual|override|abstract|sealed|partial)\\s+)+[\\w<>\\[\\],?]+\\s+${name}\\s*(?:<[^>]*>)?\\s*\\(`, "m");
|
|
707
|
+
const defLineIdx = lines.findIndex((l) => methodRegex.test(l) || new RegExp(`\\b${name}\\s*\\(`, "m").test(l));
|
|
708
|
+
if (defLineIdx === -1)
|
|
709
|
+
return "low";
|
|
710
|
+
let startLine = defLineIdx;
|
|
711
|
+
while (startLine < lines.length && !lines[startLine].includes("{")) {
|
|
712
|
+
startLine++;
|
|
713
|
+
}
|
|
714
|
+
if (startLine >= lines.length)
|
|
715
|
+
return "low";
|
|
716
|
+
let braceCount = 0;
|
|
717
|
+
let started = false;
|
|
718
|
+
const bodyLines = [];
|
|
719
|
+
for (let i = startLine; i < lines.length; i++) {
|
|
720
|
+
const line = lines[i];
|
|
721
|
+
for (const char of line) {
|
|
722
|
+
if (char === "{") {
|
|
723
|
+
braceCount++;
|
|
724
|
+
started = true;
|
|
725
|
+
} else if (char === "}") {
|
|
726
|
+
braceCount--;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
bodyLines.push(line);
|
|
730
|
+
if (started && braceCount <= 0) {
|
|
731
|
+
break;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
const body = bodyLines.join("\n");
|
|
735
|
+
const branches = (body.match(/\b(if|else\s+if|for|foreach|while|do|switch|case|catch|&&|\|\||\?\?)\b|\?[^:]*:/g) || []).length;
|
|
736
|
+
if (branches <= constants_1.COMPLEXITY_THRESHOLDS.low)
|
|
737
|
+
return "low";
|
|
738
|
+
if (branches <= constants_1.COMPLEXITY_THRESHOLDS.medium)
|
|
739
|
+
return "medium";
|
|
740
|
+
return "high";
|
|
741
|
+
}
|
|
742
|
+
exports2.csharpEntityPatterns = [
|
|
743
|
+
// Classes, structs, records, interfaces, enums
|
|
744
|
+
{
|
|
745
|
+
regex: /^[ \t]*(?:(?:public|private|protected|internal|static|abstract|sealed|partial)\s+)*(?:class|interface|enum|struct|record(?:\s+(?:class|struct))?)\s+([A-Za-z_]\w*)/gm,
|
|
746
|
+
type: "class"
|
|
747
|
+
},
|
|
748
|
+
// Methods (constructors, instance methods, async/static methods)
|
|
749
|
+
{
|
|
750
|
+
regex: /^[ \t]*(?:(?:public|private|protected|internal|static|async|virtual|override|abstract|sealed|partial)\s+)+(?:(?:async\s+)?[\w<>[\]?,]+\s+)?([A-Za-z_]\w*)\s*(?:<[^>]*>)?\s*\(/gm,
|
|
751
|
+
type: "function"
|
|
752
|
+
}
|
|
753
|
+
];
|
|
754
|
+
function extractEntities(code, filePath) {
|
|
755
|
+
const entities = [];
|
|
756
|
+
for (const { regex, type } of exports2.csharpEntityPatterns) {
|
|
757
|
+
regex.lastIndex = 0;
|
|
758
|
+
let match;
|
|
759
|
+
while ((match = regex.exec(code)) !== null) {
|
|
760
|
+
const name = match[1];
|
|
761
|
+
if (["if", "for", "foreach", "while", "switch", "catch", "lock", "using", "get", "set"].includes(name)) {
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
const upToMatch = code.slice(0, match.index);
|
|
765
|
+
const line = upToMatch.split("\n").length;
|
|
766
|
+
if (entities.some((e) => e.name === name && e.line === line))
|
|
767
|
+
continue;
|
|
768
|
+
entities.push({
|
|
769
|
+
name,
|
|
770
|
+
type,
|
|
771
|
+
line,
|
|
772
|
+
complexity: type === "function" ? estimateComplexity(code, name) : "low"
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
return entities;
|
|
777
|
+
}
|
|
778
|
+
function extractImports(code) {
|
|
779
|
+
const imports = [];
|
|
780
|
+
const usingPattern = /^[ \t]*(?:global\s+)?using\s+(?:static\s+)?(?:([A-Za-z_]\w*)\s*=\s*)?([A-Za-z_][\w.]*(?:<[^>]*>)?)\s*;/gm;
|
|
781
|
+
let match;
|
|
782
|
+
while ((match = usingPattern.exec(code)) !== null) {
|
|
783
|
+
const alias = match[1];
|
|
784
|
+
const targetFqn = match[2].trim();
|
|
785
|
+
const simpleName = alias || targetFqn.split(".").pop() || targetFqn;
|
|
786
|
+
const isLocal = !targetFqn.startsWith("System") && !targetFqn.startsWith("Microsoft");
|
|
787
|
+
imports.push({
|
|
788
|
+
source: targetFqn,
|
|
789
|
+
names: [simpleName],
|
|
790
|
+
isLocal
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
return imports;
|
|
794
|
+
}
|
|
795
|
+
function extractExports(code) {
|
|
796
|
+
const exports3 = [];
|
|
797
|
+
const typePattern = /^[ \t]*(?:public|internal)\s+(?:(?:static|abstract|sealed|partial)\s+)*(?:class|interface|enum|struct|record(?:\s+(?:class|struct))?)\s+([A-Za-z_]\w*)/gm;
|
|
798
|
+
let match;
|
|
799
|
+
while ((match = typePattern.exec(code)) !== null) {
|
|
800
|
+
exports3.push(match[1]);
|
|
801
|
+
}
|
|
802
|
+
const methodPattern = /^[ \t]*(?:public|internal)\s+(?:(?:static|async|virtual|override|abstract|sealed|partial)\s+)*(?:[\w<>[\]?,]+\s+)?([A-Za-z_]\w*)\s*(?:<[^>]*>)?\s*\(/gm;
|
|
803
|
+
while ((match = methodPattern.exec(code)) !== null) {
|
|
804
|
+
const name = match[1];
|
|
805
|
+
if (!["if", "for", "foreach", "while", "switch", "catch", "lock", "using", "get", "set"].includes(name)) {
|
|
806
|
+
exports3.push(name);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
return [...new Set(exports3)];
|
|
810
|
+
}
|
|
811
|
+
var CSharpParser = {
|
|
812
|
+
lang: "cs",
|
|
813
|
+
extensions: [".cs"],
|
|
814
|
+
extractEntities,
|
|
815
|
+
extractImports,
|
|
816
|
+
extractExports,
|
|
817
|
+
entityPatterns: exports2.csharpEntityPatterns
|
|
818
|
+
};
|
|
819
|
+
(0, registry_1.registerParser)(CSharpParser);
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
// dist/languages/java.js
|
|
824
|
+
var require_java = __commonJS({
|
|
825
|
+
"dist/languages/java.js"(exports2) {
|
|
826
|
+
"use strict";
|
|
827
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
828
|
+
exports2.javaEntityPatterns = void 0;
|
|
829
|
+
var registry_1 = require_registry();
|
|
830
|
+
var constants_1 = require_constants();
|
|
831
|
+
function escapeRegex(s) {
|
|
832
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
833
|
+
}
|
|
834
|
+
function estimateComplexity(code, name) {
|
|
835
|
+
const lines = code.split("\n");
|
|
836
|
+
const defRegex = new RegExp(`(?:^|\\s)${escapeRegex(name)}\\s*\\(`, "m");
|
|
837
|
+
const defLineIdx = lines.findIndex((l) => defRegex.test(l));
|
|
838
|
+
if (defLineIdx === -1)
|
|
839
|
+
return "low";
|
|
840
|
+
let startLine = defLineIdx;
|
|
841
|
+
while (startLine < lines.length && !lines[startLine].includes("{")) {
|
|
842
|
+
startLine++;
|
|
843
|
+
}
|
|
844
|
+
if (startLine >= lines.length)
|
|
845
|
+
return "low";
|
|
846
|
+
let braceCount = 0;
|
|
847
|
+
let started = false;
|
|
848
|
+
const bodyLines = [];
|
|
849
|
+
for (let i = startLine; i < lines.length; i++) {
|
|
850
|
+
const line = lines[i];
|
|
851
|
+
for (const ch of line) {
|
|
852
|
+
if (ch === "{") {
|
|
853
|
+
braceCount++;
|
|
854
|
+
started = true;
|
|
855
|
+
} else if (ch === "}") {
|
|
856
|
+
braceCount--;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
bodyLines.push(line);
|
|
860
|
+
if (started && braceCount <= 0)
|
|
861
|
+
break;
|
|
862
|
+
}
|
|
863
|
+
const body = bodyLines.join("\n");
|
|
864
|
+
const branches = (body.match(/\b(if|else\s+if|else|for|while|do|switch|case|catch|&&|\|\|)\b/g) || []).length;
|
|
865
|
+
if (branches <= constants_1.COMPLEXITY_THRESHOLDS.low)
|
|
866
|
+
return "low";
|
|
867
|
+
if (branches <= constants_1.COMPLEXITY_THRESHOLDS.medium)
|
|
868
|
+
return "medium";
|
|
869
|
+
return "high";
|
|
870
|
+
}
|
|
871
|
+
exports2.javaEntityPatterns = [
|
|
872
|
+
// class / abstract class / final class
|
|
873
|
+
{
|
|
874
|
+
regex: /^[ \t]*(?:(?:public|protected|private|abstract|final|static)\s+)*class\s+([A-Za-z_]\w*)(?:\s*<[^{]*?)?\s*(?:extends\s+\S+\s*)?(?:implements\s+[^{]+)?\s*\{/gm,
|
|
875
|
+
type: "class"
|
|
876
|
+
},
|
|
877
|
+
// interface
|
|
878
|
+
{
|
|
879
|
+
regex: /^[ \t]*(?:(?:public|protected|private|abstract|static)\s+)*interface\s+([A-Za-z_]\w*)(?:\s*<[^{]*?)?\s*(?:extends\s+[^{]+)?\s*\{/gm,
|
|
880
|
+
type: "interface"
|
|
881
|
+
},
|
|
882
|
+
// record (Java 14+)
|
|
883
|
+
{
|
|
884
|
+
regex: /^[ \t]*(?:(?:public|protected|private|final|static)\s+)*record\s+([A-Za-z_]\w*)\s*\(/gm,
|
|
885
|
+
type: "class"
|
|
886
|
+
},
|
|
887
|
+
// enum
|
|
888
|
+
{
|
|
889
|
+
regex: /^[ \t]*(?:(?:public|protected|private|static)\s+)*enum\s+([A-Za-z_]\w*)\s*(?:implements\s+[^{]+)?\s*\{/gm,
|
|
890
|
+
type: "class"
|
|
891
|
+
},
|
|
892
|
+
// annotation type
|
|
893
|
+
{
|
|
894
|
+
regex: /^[ \t]*(?:(?:public|protected|private|abstract|static)\s+)*@interface\s+([A-Za-z_]\w*)\s*\{/gm,
|
|
895
|
+
type: "interface"
|
|
896
|
+
},
|
|
897
|
+
// method declarations (with return type before the name)
|
|
898
|
+
{
|
|
899
|
+
regex: /^[ \t]*(?:(?:public|protected|private|static|final|abstract|synchronized|native|default|override)\s+)*(?:<[^>]*>\s+)?(?:[\w.<>\[\]]+\s+)+([A-Za-z_]\w*)\s*\([^)]*\)\s*(?:throws\s+[\w,\s]+)?\s*\{/gm,
|
|
900
|
+
type: "function"
|
|
901
|
+
}
|
|
902
|
+
];
|
|
903
|
+
function extractEntities(code, _filePath) {
|
|
904
|
+
const entities = [];
|
|
905
|
+
for (const { regex, type } of exports2.javaEntityPatterns) {
|
|
906
|
+
regex.lastIndex = 0;
|
|
907
|
+
let match;
|
|
908
|
+
while ((match = regex.exec(code)) !== null) {
|
|
909
|
+
const name = match[1];
|
|
910
|
+
if (["if", "else", "for", "while", "do", "switch", "try", "catch", "return", "new", "void", "this", "super"].includes(name))
|
|
911
|
+
continue;
|
|
912
|
+
const upToMatch = code.slice(0, match.index);
|
|
913
|
+
const line = upToMatch.split("\n").length;
|
|
914
|
+
if (entities.some((e) => e.name === name && e.line === line))
|
|
915
|
+
continue;
|
|
916
|
+
entities.push({
|
|
917
|
+
name,
|
|
918
|
+
type,
|
|
919
|
+
line,
|
|
920
|
+
complexity: type === "function" ? estimateComplexity(code, name) : "low"
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
return entities;
|
|
925
|
+
}
|
|
926
|
+
function extractImports(code) {
|
|
927
|
+
const imports = [];
|
|
928
|
+
const importPattern = /^import\s+((?:static)\s+)?([\w.]+(?:\.\*)?)?\s*;/gm;
|
|
929
|
+
let match;
|
|
930
|
+
while ((match = importPattern.exec(code)) !== null) {
|
|
931
|
+
const isStatic = Boolean(match[1]);
|
|
932
|
+
const fullPath = (match[2] || "").trim();
|
|
933
|
+
if (!fullPath)
|
|
934
|
+
continue;
|
|
935
|
+
const isWildcard = fullPath.endsWith(".*");
|
|
936
|
+
const cleanPath = isWildcard ? fullPath.slice(0, -2) : fullPath;
|
|
937
|
+
const segments = cleanPath.split(".");
|
|
938
|
+
let source;
|
|
939
|
+
let name;
|
|
940
|
+
if (isStatic) {
|
|
941
|
+
name = segments.pop() || cleanPath;
|
|
942
|
+
source = segments.join(".") || cleanPath;
|
|
943
|
+
} else {
|
|
944
|
+
name = segments[segments.length - 1] || cleanPath;
|
|
945
|
+
source = cleanPath;
|
|
946
|
+
}
|
|
947
|
+
imports.push({
|
|
948
|
+
source,
|
|
949
|
+
names: [name],
|
|
950
|
+
isLocal: false
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
return imports;
|
|
954
|
+
}
|
|
955
|
+
function extractExports(code) {
|
|
956
|
+
const exports3 = [];
|
|
957
|
+
const typePattern = /^public\s+(?:(?:abstract|final|static)\s+)*(?:class|interface|enum|record|@interface)\s+([A-Za-z_]\w*)/gm;
|
|
958
|
+
let match;
|
|
959
|
+
while ((match = typePattern.exec(code)) !== null) {
|
|
960
|
+
exports3.push(match[1]);
|
|
961
|
+
}
|
|
962
|
+
const methodPattern = /^[ \t]*public\s+(?:(?:static|final|abstract|synchronized|native|default)\s+)*(?:<[^>]*>\s+)?(?:[\w.<>\[\]]+\s+)+([A-Za-z_]\w*)\s*\(/gm;
|
|
963
|
+
while ((match = methodPattern.exec(code)) !== null) {
|
|
964
|
+
const name = match[1];
|
|
965
|
+
if (!["if", "for", "while", "switch", "class", "interface", "enum"].includes(name)) {
|
|
966
|
+
exports3.push(name);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
return [...new Set(exports3)];
|
|
970
|
+
}
|
|
971
|
+
var JavaParser = {
|
|
972
|
+
lang: "java",
|
|
973
|
+
extensions: [".java"],
|
|
974
|
+
extractEntities,
|
|
975
|
+
extractImports,
|
|
976
|
+
extractExports,
|
|
977
|
+
entityPatterns: exports2.javaEntityPatterns
|
|
978
|
+
};
|
|
979
|
+
(0, registry_1.registerParser)(JavaParser);
|
|
980
|
+
}
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
// dist/languages/kotlin.js
|
|
984
|
+
var require_kotlin = __commonJS({
|
|
985
|
+
"dist/languages/kotlin.js"(exports2) {
|
|
986
|
+
"use strict";
|
|
987
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
988
|
+
exports2.kotlinEntityPatterns = void 0;
|
|
989
|
+
var registry_1 = require_registry();
|
|
990
|
+
var constants_1 = require_constants();
|
|
991
|
+
function escapeRegex(s) {
|
|
992
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
993
|
+
}
|
|
994
|
+
function estimateComplexity(code, name) {
|
|
995
|
+
const lines = code.split("\n");
|
|
996
|
+
const defRegex = new RegExp(`(?:^|\\s)fun\\s+(?:<[^>]*>\\s+)?(?:\\w[\\w.]*\\.)?${escapeRegex(name)}\\s*(?:\\(|<)`, "m");
|
|
997
|
+
const defLineIdx = lines.findIndex((l) => defRegex.test(l));
|
|
998
|
+
if (defLineIdx === -1)
|
|
999
|
+
return "low";
|
|
1000
|
+
let startLine = defLineIdx;
|
|
1001
|
+
while (startLine < lines.length && !lines[startLine].includes("{")) {
|
|
1002
|
+
startLine++;
|
|
1003
|
+
}
|
|
1004
|
+
if (startLine >= lines.length)
|
|
1005
|
+
return "low";
|
|
1006
|
+
let braceCount = 0;
|
|
1007
|
+
let started = false;
|
|
1008
|
+
const bodyLines = [];
|
|
1009
|
+
for (let i = startLine; i < lines.length; i++) {
|
|
1010
|
+
const line = lines[i];
|
|
1011
|
+
for (const ch of line) {
|
|
1012
|
+
if (ch === "{") {
|
|
1013
|
+
braceCount++;
|
|
1014
|
+
started = true;
|
|
1015
|
+
} else if (ch === "}") {
|
|
1016
|
+
braceCount--;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
bodyLines.push(line);
|
|
1020
|
+
if (started && braceCount <= 0)
|
|
1021
|
+
break;
|
|
1022
|
+
}
|
|
1023
|
+
const body = bodyLines.join("\n");
|
|
1024
|
+
const branches = (body.match(/\b(if|else\s+if|else|for|while|when|catch|&&|\|\|)\b/g) || []).length;
|
|
1025
|
+
if (branches <= constants_1.COMPLEXITY_THRESHOLDS.low)
|
|
1026
|
+
return "low";
|
|
1027
|
+
if (branches <= constants_1.COMPLEXITY_THRESHOLDS.medium)
|
|
1028
|
+
return "medium";
|
|
1029
|
+
return "high";
|
|
1030
|
+
}
|
|
1031
|
+
exports2.kotlinEntityPatterns = [
|
|
1032
|
+
// class declarations (including data class, sealed class, abstract class, inner class)
|
|
1033
|
+
// The trailing brace is optional: abstract classes may have no body on the same line.
|
|
1034
|
+
// We anchor by requiring the class name to be followed by whitespace, <, (, :, { or EOL.
|
|
1035
|
+
{
|
|
1036
|
+
regex: /^[ \t]*(?:(?:public|private|protected|internal|abstract|sealed|data|open|inner|inline|value|annotation)\s+)*class\s+([A-Za-z_]\w*)(?=[\s<(:,{\n]|$)/gm,
|
|
1037
|
+
type: "class"
|
|
1038
|
+
},
|
|
1039
|
+
// object declarations (singleton objects and companion objects)
|
|
1040
|
+
{
|
|
1041
|
+
regex: /^[ \t]*(?:(?:public|private|protected|internal)\s+)*(?:companion\s+)?object\s+([A-Za-z_]\w*)\s*(?::\s*[^{]+)?\s*\{/gm,
|
|
1042
|
+
type: "class"
|
|
1043
|
+
},
|
|
1044
|
+
// interface declarations
|
|
1045
|
+
{
|
|
1046
|
+
regex: /^[ \t]*(?:(?:public|private|protected|internal|sealed|fun)\s+)*interface\s+([A-Za-z_]\w*)(?:\s*<[^{]*)?(?:\s*:\s*[^{]+)?\s*\{/gm,
|
|
1047
|
+
type: "interface"
|
|
1048
|
+
},
|
|
1049
|
+
// enum class
|
|
1050
|
+
{
|
|
1051
|
+
regex: /^[ \t]*(?:(?:public|private|protected|internal)\s+)*enum\s+class\s+([A-Za-z_]\w*)\s*(?:\([^)]*\))?\s*\{/gm,
|
|
1052
|
+
type: "class"
|
|
1053
|
+
},
|
|
1054
|
+
// function declarations (including suspend, inline, operator, extension functions)
|
|
1055
|
+
{
|
|
1056
|
+
regex: /^[ \t]*(?:(?:public|private|protected|internal|override|open|final|abstract|suspend|inline|operator|infix|tailrec|external|actual|expect)\s+)*fun\s+(?:<[^>]*>\s+)?(?:[\w.]+\.)?([A-Za-z_]\w*)\s*(?:<[^>]*>)?\s*\(/gm,
|
|
1057
|
+
type: "function"
|
|
1058
|
+
}
|
|
1059
|
+
];
|
|
1060
|
+
function extractEntities(code, _filePath) {
|
|
1061
|
+
const entities = [];
|
|
1062
|
+
for (const { regex, type } of exports2.kotlinEntityPatterns) {
|
|
1063
|
+
regex.lastIndex = 0;
|
|
1064
|
+
let match;
|
|
1065
|
+
while ((match = regex.exec(code)) !== null) {
|
|
1066
|
+
const name = match[1];
|
|
1067
|
+
if (!name)
|
|
1068
|
+
continue;
|
|
1069
|
+
const upToMatch = code.slice(0, match.index);
|
|
1070
|
+
const line = upToMatch.split("\n").length;
|
|
1071
|
+
if (entities.some((e) => e.name === name && e.line === line))
|
|
1072
|
+
continue;
|
|
1073
|
+
entities.push({
|
|
1074
|
+
name,
|
|
1075
|
+
type,
|
|
1076
|
+
line,
|
|
1077
|
+
complexity: type === "function" ? estimateComplexity(code, name) : "low"
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
return entities;
|
|
1082
|
+
}
|
|
1083
|
+
function extractImports(code) {
|
|
1084
|
+
const imports = [];
|
|
1085
|
+
const importPattern = /^import\s+([\w.]+?)(\.\*)?\s*(?:as\s+(\w+))?\s*$/gm;
|
|
1086
|
+
let match;
|
|
1087
|
+
while ((match = importPattern.exec(code)) !== null) {
|
|
1088
|
+
const fullPath = match[1];
|
|
1089
|
+
const isWild = Boolean(match[2]);
|
|
1090
|
+
const alias = match[3];
|
|
1091
|
+
if (isWild)
|
|
1092
|
+
continue;
|
|
1093
|
+
const lastName = fullPath.split(".").pop() || fullPath;
|
|
1094
|
+
const localName = alias || lastName;
|
|
1095
|
+
imports.push({
|
|
1096
|
+
source: fullPath,
|
|
1097
|
+
names: [localName],
|
|
1098
|
+
isLocal: false
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
return imports;
|
|
1102
|
+
}
|
|
1103
|
+
function extractExports(code) {
|
|
1104
|
+
const exports3 = [];
|
|
1105
|
+
const patterns = [
|
|
1106
|
+
/^(?:(?:public|open|abstract|sealed|data|inline|value)\s+)*class\s+([A-Za-z_]\w*)/gm,
|
|
1107
|
+
/^(?:(?:public)\s+)?object\s+([A-Za-z_]\w*)/gm,
|
|
1108
|
+
/^(?:(?:public|sealed|fun)\s+)*interface\s+([A-Za-z_]\w*)/gm,
|
|
1109
|
+
/^(?:(?:public|open|inline|suspend|operator|infix|tailrec)\s+)*fun\s+(?:<[^>]*>\s+)?([A-Za-z_]\w*)\s*(?:<[^>]*>)?\s*\(/gm
|
|
1110
|
+
];
|
|
1111
|
+
for (const pattern of patterns) {
|
|
1112
|
+
let match;
|
|
1113
|
+
while ((match = pattern.exec(code)) !== null) {
|
|
1114
|
+
if (match[1])
|
|
1115
|
+
exports3.push(match[1]);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
return [...new Set(exports3)];
|
|
1119
|
+
}
|
|
1120
|
+
var KotlinParser = {
|
|
1121
|
+
lang: "kotlin",
|
|
1122
|
+
extensions: [".kt", ".kts"],
|
|
1123
|
+
extractEntities,
|
|
1124
|
+
extractImports,
|
|
1125
|
+
extractExports,
|
|
1126
|
+
entityPatterns: exports2.kotlinEntityPatterns
|
|
1127
|
+
};
|
|
1128
|
+
(0, registry_1.registerParser)(KotlinParser);
|
|
1129
|
+
}
|
|
1130
|
+
});
|
|
1131
|
+
|
|
1132
|
+
// dist/languages/php.js
|
|
1133
|
+
var require_php = __commonJS({
|
|
1134
|
+
"dist/languages/php.js"(exports2) {
|
|
1135
|
+
"use strict";
|
|
1136
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
1137
|
+
exports2.phpEntityPatterns = void 0;
|
|
1138
|
+
var registry_1 = require_registry();
|
|
1139
|
+
var constants_1 = require_constants();
|
|
1140
|
+
function escapeRegex(s) {
|
|
1141
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1142
|
+
}
|
|
1143
|
+
function estimateComplexity(code, name) {
|
|
1144
|
+
const lines = code.split("\n");
|
|
1145
|
+
const defRegex = new RegExp(`(?:^|\\s)function\\s+${escapeRegex(name)}\\s*\\(`, "m");
|
|
1146
|
+
const defLineIdx = lines.findIndex((l) => defRegex.test(l));
|
|
1147
|
+
if (defLineIdx === -1)
|
|
1148
|
+
return "low";
|
|
1149
|
+
let startLine = defLineIdx;
|
|
1150
|
+
while (startLine < lines.length && !lines[startLine].includes("{")) {
|
|
1151
|
+
startLine++;
|
|
1152
|
+
}
|
|
1153
|
+
if (startLine >= lines.length)
|
|
1154
|
+
return "low";
|
|
1155
|
+
let braceCount = 0;
|
|
1156
|
+
let started = false;
|
|
1157
|
+
const bodyLines = [];
|
|
1158
|
+
for (let i = startLine; i < lines.length; i++) {
|
|
1159
|
+
const line = lines[i];
|
|
1160
|
+
for (const ch of line) {
|
|
1161
|
+
if (ch === "{") {
|
|
1162
|
+
braceCount++;
|
|
1163
|
+
started = true;
|
|
1164
|
+
} else if (ch === "}") {
|
|
1165
|
+
braceCount--;
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
bodyLines.push(line);
|
|
1169
|
+
if (started && braceCount <= 0)
|
|
1170
|
+
break;
|
|
1171
|
+
}
|
|
1172
|
+
const body = bodyLines.join("\n");
|
|
1173
|
+
const branches = (body.match(/\b(if|elseif|else|for|foreach|while|do|switch|case|catch|&&|\|\|)\b/g) || []).length;
|
|
1174
|
+
if (branches <= constants_1.COMPLEXITY_THRESHOLDS.low)
|
|
1175
|
+
return "low";
|
|
1176
|
+
if (branches <= constants_1.COMPLEXITY_THRESHOLDS.medium)
|
|
1177
|
+
return "medium";
|
|
1178
|
+
return "high";
|
|
1179
|
+
}
|
|
1180
|
+
exports2.phpEntityPatterns = [
|
|
1181
|
+
// class (abstract class, final class, readonly class, etc.)
|
|
1182
|
+
{
|
|
1183
|
+
regex: /^[ \t]*(?:(?:abstract|final|readonly)\s+)*class\s+([A-Za-z_]\w*)(?:\s+extends\s+\S+)?(?:\s+implements\s+[^{]+)?\s*\{/gm,
|
|
1184
|
+
type: "class"
|
|
1185
|
+
},
|
|
1186
|
+
// interface
|
|
1187
|
+
{
|
|
1188
|
+
regex: /^[ \t]*interface\s+([A-Za-z_]\w*)(?:\s+extends\s+[^{]+)?\s*\{/gm,
|
|
1189
|
+
type: "interface"
|
|
1190
|
+
},
|
|
1191
|
+
// trait
|
|
1192
|
+
{
|
|
1193
|
+
regex: /^[ \t]*trait\s+([A-Za-z_]\w*)\s*\{/gm,
|
|
1194
|
+
type: "class"
|
|
1195
|
+
},
|
|
1196
|
+
// enum (PHP 8.1+)
|
|
1197
|
+
{
|
|
1198
|
+
regex: /^[ \t]*enum\s+([A-Za-z_]\w*)(?:\s*:\s*\w+)?(?:\s+implements\s+[^{]+)?\s*\{/gm,
|
|
1199
|
+
type: "class"
|
|
1200
|
+
},
|
|
1201
|
+
// function and method declarations
|
|
1202
|
+
{
|
|
1203
|
+
regex: /^[ \t]*(?:(?:public|protected|private|static|abstract|final|readonly)\s+)*function\s+([A-Za-z_]\w*)\s*\(/gm,
|
|
1204
|
+
type: "function"
|
|
1205
|
+
}
|
|
1206
|
+
];
|
|
1207
|
+
function extractEntities(code, _filePath) {
|
|
1208
|
+
const entities = [];
|
|
1209
|
+
for (const { regex, type } of exports2.phpEntityPatterns) {
|
|
1210
|
+
regex.lastIndex = 0;
|
|
1211
|
+
let match;
|
|
1212
|
+
while ((match = regex.exec(code)) !== null) {
|
|
1213
|
+
const name = match[1];
|
|
1214
|
+
if (!name)
|
|
1215
|
+
continue;
|
|
1216
|
+
const upToMatch = code.slice(0, match.index);
|
|
1217
|
+
const line = upToMatch.split("\n").length;
|
|
1218
|
+
if (entities.some((e) => e.name === name && e.line === line))
|
|
1219
|
+
continue;
|
|
1220
|
+
entities.push({
|
|
1221
|
+
name,
|
|
1222
|
+
type,
|
|
1223
|
+
line,
|
|
1224
|
+
complexity: type === "function" ? estimateComplexity(code, name) : "low"
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
return entities;
|
|
1229
|
+
}
|
|
1230
|
+
function extractImports(code) {
|
|
1231
|
+
const imports = [];
|
|
1232
|
+
const usePattern = /^use\s+([\w\\]+(?:\s*\{[^}]*\})?)\s*(?:as\s+(\w+)\s*)?;/gm;
|
|
1233
|
+
let match;
|
|
1234
|
+
while ((match = usePattern.exec(code)) !== null) {
|
|
1235
|
+
const raw = match[1].trim();
|
|
1236
|
+
const alias = match[2]?.trim();
|
|
1237
|
+
if (raw.includes("{")) {
|
|
1238
|
+
const prefixMatch = raw.match(/^([\w\\]+)\\?\s*\{([^}]*)\}/);
|
|
1239
|
+
if (prefixMatch) {
|
|
1240
|
+
const prefix = prefixMatch[1];
|
|
1241
|
+
const items = prefixMatch[2].split(",");
|
|
1242
|
+
for (const item of items) {
|
|
1243
|
+
const parts = item.trim().split(/\s+as\s+/i);
|
|
1244
|
+
const fullName = (prefix + "\\" + parts[0].trim()).replace(/\\+/g, "\\");
|
|
1245
|
+
const lastName = parts[1] || parts[0].trim().split("\\").pop() || fullName;
|
|
1246
|
+
imports.push({ source: fullName, names: [lastName.trim()], isLocal: false });
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
} else {
|
|
1250
|
+
const fullPath = raw;
|
|
1251
|
+
const lastName = alias || fullPath.split("\\").pop() || fullPath;
|
|
1252
|
+
imports.push({ source: fullPath, names: [lastName.trim()], isLocal: false });
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
const includePattern = /(?:require|include)(?:_once)?\s*[^;'"]*?['"]([^'"]+)['"]/gm;
|
|
1256
|
+
while ((match = includePattern.exec(code)) !== null) {
|
|
1257
|
+
const source = match[1];
|
|
1258
|
+
const name = source.split("/").pop()?.replace(/\.php$/i, "") || source;
|
|
1259
|
+
imports.push({ source, names: [name], isLocal: true });
|
|
1260
|
+
}
|
|
1261
|
+
return imports;
|
|
1262
|
+
}
|
|
1263
|
+
function extractExports(code) {
|
|
1264
|
+
const exports3 = [];
|
|
1265
|
+
const patterns = [
|
|
1266
|
+
/^(?:(?:abstract|final|readonly)\s+)*class\s+([A-Za-z_]\w*)/gm,
|
|
1267
|
+
/^interface\s+([A-Za-z_]\w*)/gm,
|
|
1268
|
+
/^trait\s+([A-Za-z_]\w*)/gm,
|
|
1269
|
+
/^enum\s+([A-Za-z_]\w*)/gm,
|
|
1270
|
+
/^function\s+([A-Za-z_]\w*)\s*\(/gm
|
|
1271
|
+
];
|
|
1272
|
+
for (const pattern of patterns) {
|
|
1273
|
+
let match;
|
|
1274
|
+
while ((match = pattern.exec(code)) !== null) {
|
|
1275
|
+
if (match[1])
|
|
1276
|
+
exports3.push(match[1]);
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
return [...new Set(exports3)];
|
|
1280
|
+
}
|
|
1281
|
+
var PhpParser = {
|
|
1282
|
+
lang: "php",
|
|
1283
|
+
extensions: [".php", ".phtml", ".php3", ".php4", ".php5", ".php7"],
|
|
1284
|
+
extractEntities,
|
|
1285
|
+
extractImports,
|
|
1286
|
+
extractExports,
|
|
1287
|
+
entityPatterns: exports2.phpEntityPatterns
|
|
1288
|
+
};
|
|
1289
|
+
(0, registry_1.registerParser)(PhpParser);
|
|
1290
|
+
}
|
|
1291
|
+
});
|
|
1292
|
+
|
|
1293
|
+
// dist/languages/ruby.js
|
|
1294
|
+
var require_ruby = __commonJS({
|
|
1295
|
+
"dist/languages/ruby.js"(exports2) {
|
|
1296
|
+
"use strict";
|
|
1297
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
1298
|
+
exports2.rubyEntityPatterns = void 0;
|
|
1299
|
+
var registry_1 = require_registry();
|
|
1300
|
+
var constants_1 = require_constants();
|
|
1301
|
+
function escapeRegex(s) {
|
|
1302
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1303
|
+
}
|
|
1304
|
+
function sanitizeMethodName(name) {
|
|
1305
|
+
if (name.endsWith("!"))
|
|
1306
|
+
return `${name.slice(0, -1)}_bang`;
|
|
1307
|
+
if (name.endsWith("?"))
|
|
1308
|
+
return `${name.slice(0, -1)}_pred`;
|
|
1309
|
+
if (name.endsWith("="))
|
|
1310
|
+
return `${name.slice(0, -1)}_eq`;
|
|
1311
|
+
return name;
|
|
1312
|
+
}
|
|
1313
|
+
function estimateComplexity(code, name) {
|
|
1314
|
+
const lines = code.split("\n");
|
|
1315
|
+
const defRegex = new RegExp(`^[ \\t]*def\\s+(?:self\\.)?${escapeRegex(name)}(?:[!?=])?\\s*(\\(|$)`, "m");
|
|
1316
|
+
const defLineIdx = lines.findIndex((l) => defRegex.test(l));
|
|
1317
|
+
if (defLineIdx === -1)
|
|
1318
|
+
return "low";
|
|
1319
|
+
const bodyLines = [];
|
|
1320
|
+
let depth = 0;
|
|
1321
|
+
for (let i = defLineIdx; i < lines.length; i++) {
|
|
1322
|
+
const line = lines[i];
|
|
1323
|
+
const trimmed = line.trim();
|
|
1324
|
+
if (/\b(def|class|module|do\b|begin|if(?!.*\bend\b)|unless(?!.*\bend\b)|while(?!.*\bend\b)|until(?!.*\bend\b)|for\s|case\b)\b/.test(trimmed)) {
|
|
1325
|
+
depth++;
|
|
1326
|
+
}
|
|
1327
|
+
if (/\bend\b/.test(trimmed)) {
|
|
1328
|
+
depth--;
|
|
1329
|
+
if (depth <= 0) {
|
|
1330
|
+
bodyLines.push(line);
|
|
1331
|
+
break;
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
bodyLines.push(line);
|
|
1335
|
+
}
|
|
1336
|
+
const body = bodyLines.join("\n");
|
|
1337
|
+
const branches = (body.match(/\b(if|elsif|else|unless|while|until|for|rescue|when|and|or|&&|\|\|)\b/g) || []).length;
|
|
1338
|
+
if (branches <= constants_1.COMPLEXITY_THRESHOLDS.low)
|
|
1339
|
+
return "low";
|
|
1340
|
+
if (branches <= constants_1.COMPLEXITY_THRESHOLDS.medium)
|
|
1341
|
+
return "medium";
|
|
1342
|
+
return "high";
|
|
1343
|
+
}
|
|
1344
|
+
exports2.rubyEntityPatterns = [
|
|
1345
|
+
// class declarations (class Foo, class Foo < Bar)
|
|
1346
|
+
{
|
|
1347
|
+
regex: /^[ \t]*class\s+([A-Z]\w*(?:::[A-Z]\w*)*)\s*(?:<\s*\S+)?\s*$/gm,
|
|
1348
|
+
type: "class"
|
|
1349
|
+
},
|
|
1350
|
+
// module declarations
|
|
1351
|
+
{
|
|
1352
|
+
regex: /^[ \t]*module\s+([A-Z]\w*(?:::[A-Z]\w*)*)\s*$/gm,
|
|
1353
|
+
type: "class"
|
|
1354
|
+
},
|
|
1355
|
+
// singleton methods: def self.method_name[!?=]
|
|
1356
|
+
{
|
|
1357
|
+
regex: /^[ \t]*def\s+self\.([A-Za-z_]\w*[!?=]?)\s*(?:\(|$)/gm,
|
|
1358
|
+
type: "function"
|
|
1359
|
+
},
|
|
1360
|
+
// instance methods: def method_name[!?=]
|
|
1361
|
+
{
|
|
1362
|
+
regex: /^[ \t]*def\s+([A-Za-z_]\w*[!?=]?)\s*(?:\(|$)/gm,
|
|
1363
|
+
type: "function"
|
|
1364
|
+
}
|
|
1365
|
+
];
|
|
1366
|
+
function extractEntities(code, _filePath) {
|
|
1367
|
+
const entities = [];
|
|
1368
|
+
for (const { regex, type } of exports2.rubyEntityPatterns) {
|
|
1369
|
+
regex.lastIndex = 0;
|
|
1370
|
+
let match;
|
|
1371
|
+
while ((match = regex.exec(code)) !== null) {
|
|
1372
|
+
const rawName = match[1];
|
|
1373
|
+
if (!rawName)
|
|
1374
|
+
continue;
|
|
1375
|
+
const name = type === "function" ? sanitizeMethodName(rawName) : rawName;
|
|
1376
|
+
const upToMatch = code.slice(0, match.index);
|
|
1377
|
+
const line = upToMatch.split("\n").length;
|
|
1378
|
+
if (entities.some((e) => e.name === name && e.line === line))
|
|
1379
|
+
continue;
|
|
1380
|
+
entities.push({
|
|
1381
|
+
name,
|
|
1382
|
+
type,
|
|
1383
|
+
line,
|
|
1384
|
+
complexity: type === "function" ? estimateComplexity(code, rawName) : "low"
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
return entities;
|
|
1389
|
+
}
|
|
1390
|
+
function extractImports(code) {
|
|
1391
|
+
const imports = [];
|
|
1392
|
+
const requirePattern = /^[ \t]*require\s+['"]([^'"]+)['"]/gm;
|
|
1393
|
+
let match;
|
|
1394
|
+
while ((match = requirePattern.exec(code)) !== null) {
|
|
1395
|
+
const source = match[1];
|
|
1396
|
+
const name = source.split("/").pop() || source;
|
|
1397
|
+
imports.push({ source, names: [name], isLocal: false });
|
|
1398
|
+
}
|
|
1399
|
+
const relPattern = /^[ \t]*require_relative\s+['"]([^'"]+)['"]/gm;
|
|
1400
|
+
while ((match = relPattern.exec(code)) !== null) {
|
|
1401
|
+
const source = match[1];
|
|
1402
|
+
const name = source.split("/").pop() || source;
|
|
1403
|
+
imports.push({ source, names: [name], isLocal: true });
|
|
1404
|
+
}
|
|
1405
|
+
const loadPattern = /^[ \t]*load\s+['"]([^'"]+)['"]/gm;
|
|
1406
|
+
while ((match = loadPattern.exec(code)) !== null) {
|
|
1407
|
+
const source = match[1];
|
|
1408
|
+
const name = source.split("/").pop()?.replace(/\.rb$/, "") || source;
|
|
1409
|
+
imports.push({ source, names: [name], isLocal: true });
|
|
1410
|
+
}
|
|
1411
|
+
const mixinPattern = /^[ \t]*(?:include|extend|prepend)\s+([A-Z]\w*(?:::[A-Z]\w*)*)/gm;
|
|
1412
|
+
while ((match = mixinPattern.exec(code)) !== null) {
|
|
1413
|
+
const source = match[1];
|
|
1414
|
+
const name = source.split("::").pop() || source;
|
|
1415
|
+
imports.push({ source, names: [name], isLocal: false });
|
|
1416
|
+
}
|
|
1417
|
+
return imports;
|
|
1418
|
+
}
|
|
1419
|
+
function extractExports(code) {
|
|
1420
|
+
const exports3 = [];
|
|
1421
|
+
const typePattern = /^(?:class|module)\s+([A-Z]\w*(?:::[A-Z]\w*)*)/gm;
|
|
1422
|
+
let match;
|
|
1423
|
+
while ((match = typePattern.exec(code)) !== null) {
|
|
1424
|
+
exports3.push(match[1]);
|
|
1425
|
+
}
|
|
1426
|
+
const attrPattern = /^[ \t]*attr_(?:reader|writer|accessor)\s+(.+)$/gm;
|
|
1427
|
+
while ((match = attrPattern.exec(code)) !== null) {
|
|
1428
|
+
const syms = match[1].split(",").map((s) => s.trim().replace(/^:/, ""));
|
|
1429
|
+
exports3.push(...syms.filter(Boolean));
|
|
1430
|
+
}
|
|
1431
|
+
const pubFuncPattern = /^[ \t]*(?:module_function|public)\s+def\s+([A-Za-z_]\w*[!?=]?)/gm;
|
|
1432
|
+
while ((match = pubFuncPattern.exec(code)) !== null) {
|
|
1433
|
+
exports3.push(sanitizeMethodName(match[1]));
|
|
1434
|
+
}
|
|
1435
|
+
return [...new Set(exports3)];
|
|
1436
|
+
}
|
|
1437
|
+
var RubyParser = {
|
|
1438
|
+
lang: "ruby",
|
|
1439
|
+
extensions: [".rb", ".rake", ".gemspec"],
|
|
1440
|
+
extractEntities,
|
|
1441
|
+
extractImports,
|
|
1442
|
+
extractExports,
|
|
1443
|
+
entityPatterns: exports2.rubyEntityPatterns
|
|
1444
|
+
};
|
|
1445
|
+
(0, registry_1.registerParser)(RubyParser);
|
|
1446
|
+
}
|
|
1447
|
+
});
|
|
1448
|
+
|
|
1449
|
+
// dist/languages/swift.js
|
|
1450
|
+
var require_swift = __commonJS({
|
|
1451
|
+
"dist/languages/swift.js"(exports2) {
|
|
1452
|
+
"use strict";
|
|
1453
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
1454
|
+
exports2.swiftEntityPatterns = void 0;
|
|
1455
|
+
var registry_1 = require_registry();
|
|
1456
|
+
var constants_1 = require_constants();
|
|
1457
|
+
function escapeRegex(s) {
|
|
1458
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1459
|
+
}
|
|
1460
|
+
function estimateComplexity(code, name) {
|
|
1461
|
+
const lines = code.split("\n");
|
|
1462
|
+
const defRegex = new RegExp(`(?:^|\\s)(?:func\\s+${escapeRegex(name)}|init|deinit|subscript)\\s*(?:<[^>]*>)?\\s*\\(`, "m");
|
|
1463
|
+
const defLineIdx = lines.findIndex((l) => defRegex.test(l));
|
|
1464
|
+
if (defLineIdx === -1)
|
|
1465
|
+
return "low";
|
|
1466
|
+
let startLine = defLineIdx;
|
|
1467
|
+
while (startLine < lines.length && !lines[startLine].includes("{")) {
|
|
1468
|
+
startLine++;
|
|
1469
|
+
}
|
|
1470
|
+
if (startLine >= lines.length)
|
|
1471
|
+
return "low";
|
|
1472
|
+
let braceCount = 0;
|
|
1473
|
+
let started = false;
|
|
1474
|
+
const bodyLines = [];
|
|
1475
|
+
for (let i = startLine; i < lines.length; i++) {
|
|
1476
|
+
const line = lines[i];
|
|
1477
|
+
for (const ch of line) {
|
|
1478
|
+
if (ch === "{") {
|
|
1479
|
+
braceCount++;
|
|
1480
|
+
started = true;
|
|
1481
|
+
} else if (ch === "}") {
|
|
1482
|
+
braceCount--;
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
bodyLines.push(line);
|
|
1486
|
+
if (started && braceCount <= 0)
|
|
1487
|
+
break;
|
|
1488
|
+
}
|
|
1489
|
+
const body = bodyLines.join("\n");
|
|
1490
|
+
const branches = (body.match(/\b(if|else\s+if|else|for\s|while|repeat|switch|case|catch|guard|&&|\|\|)\b/g) || []).length;
|
|
1491
|
+
if (branches <= constants_1.COMPLEXITY_THRESHOLDS.low)
|
|
1492
|
+
return "low";
|
|
1493
|
+
if (branches <= constants_1.COMPLEXITY_THRESHOLDS.medium)
|
|
1494
|
+
return "medium";
|
|
1495
|
+
return "high";
|
|
1496
|
+
}
|
|
1497
|
+
exports2.swiftEntityPatterns = [
|
|
1498
|
+
// class (including final class, open class, public class)
|
|
1499
|
+
{
|
|
1500
|
+
regex: /^[ \t]*(?:(?:public|internal|private|fileprivate|open|final|@MainActor)\s+)*class\s+([A-Za-z_]\w*)(?:\s*<[^{]*?)?\s*(?::\s*[^{]+)?\s*\{/gm,
|
|
1501
|
+
type: "class"
|
|
1502
|
+
},
|
|
1503
|
+
// struct
|
|
1504
|
+
{
|
|
1505
|
+
regex: /^[ \t]*(?:(?:public|internal|private|fileprivate)\s+)*struct\s+([A-Za-z_]\w*)(?:\s*<[^{]*?)?\s*(?::\s*[^{]+)?\s*\{/gm,
|
|
1506
|
+
type: "class"
|
|
1507
|
+
},
|
|
1508
|
+
// enum
|
|
1509
|
+
{
|
|
1510
|
+
regex: /^[ \t]*(?:(?:public|internal|private|fileprivate|indirect)\s+)*enum\s+([A-Za-z_]\w*)(?:\s*<[^{]*?)?\s*(?::\s*[^{]+)?\s*\{/gm,
|
|
1511
|
+
type: "class"
|
|
1512
|
+
},
|
|
1513
|
+
// protocol
|
|
1514
|
+
{
|
|
1515
|
+
regex: /^[ \t]*(?:(?:public|internal|private|fileprivate)\s+)*protocol\s+([A-Za-z_]\w*)(?:\s*<[^{]*?)?\s*(?::\s*[^{]+)?\s*\{/gm,
|
|
1516
|
+
type: "interface"
|
|
1517
|
+
},
|
|
1518
|
+
// actor (Swift 5.5+)
|
|
1519
|
+
{
|
|
1520
|
+
regex: /^[ \t]*(?:(?:public|internal|private|fileprivate|distributed)\s+)*actor\s+([A-Za-z_]\w*)(?:\s*:\s*[^{]+)?\s*\{/gm,
|
|
1521
|
+
type: "class"
|
|
1522
|
+
},
|
|
1523
|
+
// extension (cross-file method container, same type as class in Python extractor)
|
|
1524
|
+
// Supports: extension Foo, extension Array where Element: Comparable
|
|
1525
|
+
{
|
|
1526
|
+
regex: /^[ \t]*extension\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)(?:\s*<[^>{]*>)?(?:\s*:\s*[^{]+)?(?:\s+where\s+[^{]+)?\s*\{/gm,
|
|
1527
|
+
type: "class"
|
|
1528
|
+
},
|
|
1529
|
+
// function declarations (including mutating, static, class func, override, async, throws)
|
|
1530
|
+
{
|
|
1531
|
+
regex: /^[ \t]*(?:(?:public|internal|private|fileprivate|open|override|static|class|mutating|nonmutating|dynamic|final|required|convenience|async|throws|rethrows|nonisolated|@discardableResult|@objc|@MainActor)\s+)*func\s+([A-Za-z_]\w*)\s*(?:<[^>]*>)?\s*\(/gm,
|
|
1532
|
+
type: "function"
|
|
1533
|
+
},
|
|
1534
|
+
// init declarations
|
|
1535
|
+
{
|
|
1536
|
+
regex: /^[ \t]*(?:(?:public|internal|private|fileprivate|override|required|convenience)\s+)*init\??\s*(?:<[^>]*>)?\s*\(/gm,
|
|
1537
|
+
type: "function"
|
|
1538
|
+
},
|
|
1539
|
+
// deinit
|
|
1540
|
+
{
|
|
1541
|
+
regex: /^[ \t]*deinit\s*\{/gm,
|
|
1542
|
+
type: "function"
|
|
1543
|
+
},
|
|
1544
|
+
// subscript
|
|
1545
|
+
{
|
|
1546
|
+
regex: /^[ \t]*(?:(?:public|internal|private|fileprivate|static|override)\s+)*subscript\s*(?:<[^>]*>)?\s*\(/gm,
|
|
1547
|
+
type: "function"
|
|
1548
|
+
}
|
|
1549
|
+
];
|
|
1550
|
+
function extractEntities(code, _filePath) {
|
|
1551
|
+
const entities = [];
|
|
1552
|
+
for (const { regex, type } of exports2.swiftEntityPatterns) {
|
|
1553
|
+
regex.lastIndex = 0;
|
|
1554
|
+
let match;
|
|
1555
|
+
while ((match = regex.exec(code)) !== null) {
|
|
1556
|
+
const name = match[1] ?? (/\binit\b/.test(match[0]) ? "init" : /\bdeinit\b/.test(match[0]) ? "deinit" : "subscript");
|
|
1557
|
+
const upToMatch = code.slice(0, match.index);
|
|
1558
|
+
const line = upToMatch.split("\n").length;
|
|
1559
|
+
if (entities.some((e) => e.name === name && e.line === line))
|
|
1560
|
+
continue;
|
|
1561
|
+
entities.push({
|
|
1562
|
+
name,
|
|
1563
|
+
type,
|
|
1564
|
+
line,
|
|
1565
|
+
complexity: type === "function" ? estimateComplexity(code, name) : "low"
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
return entities;
|
|
1570
|
+
}
|
|
1571
|
+
function extractImports(code) {
|
|
1572
|
+
const imports = [];
|
|
1573
|
+
const importPattern = /^[ \t]*import\s+(?:(?:class|struct|enum|func|var|let|typealias)\s+)?([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)/gm;
|
|
1574
|
+
let match;
|
|
1575
|
+
while ((match = importPattern.exec(code)) !== null) {
|
|
1576
|
+
const fullPath = match[1];
|
|
1577
|
+
const moduleName = fullPath.split(".")[0];
|
|
1578
|
+
if (["class", "struct", "enum", "func", "var", "let", "typealias"].includes(moduleName))
|
|
1579
|
+
continue;
|
|
1580
|
+
imports.push({
|
|
1581
|
+
source: moduleName,
|
|
1582
|
+
names: [moduleName],
|
|
1583
|
+
isLocal: false
|
|
1584
|
+
});
|
|
1585
|
+
}
|
|
1586
|
+
return imports;
|
|
1587
|
+
}
|
|
1588
|
+
function extractExports(code) {
|
|
1589
|
+
const exports3 = [];
|
|
1590
|
+
const publicPatterns = [
|
|
1591
|
+
// public/open class, struct, enum, protocol, actor, extension
|
|
1592
|
+
/^(?:(?:public|open|final)\s+)*(?:class|struct|enum|protocol|actor)\s+([A-Za-z_]\w*)/gm,
|
|
1593
|
+
// public extension is not really an export, but surfaces it for graph linking
|
|
1594
|
+
/^(?:public\s+)?extension\s+([A-Za-z_]\w*)/gm,
|
|
1595
|
+
// public func
|
|
1596
|
+
/^[ \t]*(?:public|open)\s+(?:(?:static|class|override|mutating|async|throws|nonisolated)\s+)*func\s+([A-Za-z_]\w*)/gm,
|
|
1597
|
+
// public var / let
|
|
1598
|
+
/^[ \t]*(?:public|open)\s+(?:(?:static|class|lazy|private\(set\)|internal\(set\))\s+)*(?:var|let)\s+([A-Za-z_]\w*)/gm,
|
|
1599
|
+
// public init
|
|
1600
|
+
/^[ \t]*(?:public|open)\s+(?:required\s+|convenience\s+)?init/gm
|
|
1601
|
+
];
|
|
1602
|
+
for (const pattern of publicPatterns) {
|
|
1603
|
+
let match;
|
|
1604
|
+
while ((match = pattern.exec(code)) !== null) {
|
|
1605
|
+
if (match[1])
|
|
1606
|
+
exports3.push(match[1]);
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
return [...new Set(exports3)];
|
|
1610
|
+
}
|
|
1611
|
+
var SwiftParser = {
|
|
1612
|
+
lang: "swift",
|
|
1613
|
+
extensions: [".swift"],
|
|
1614
|
+
extractEntities,
|
|
1615
|
+
extractImports,
|
|
1616
|
+
extractExports,
|
|
1617
|
+
entityPatterns: exports2.swiftEntityPatterns
|
|
1618
|
+
};
|
|
1619
|
+
(0, registry_1.registerParser)(SwiftParser);
|
|
1620
|
+
}
|
|
1621
|
+
});
|
|
1622
|
+
|
|
412
1623
|
// dist/stages/collector.js
|
|
413
1624
|
var require_collector = __commonJS({
|
|
414
1625
|
"dist/stages/collector.js"(exports2) {
|
|
@@ -483,13 +1694,17 @@ var require_parser = __commonJS({
|
|
|
483
1694
|
const parser = (0, registry_1.getLanguageParser)(ext);
|
|
484
1695
|
if (!parser)
|
|
485
1696
|
return null;
|
|
1697
|
+
const isHashCommentLang = [".py", ".rb", ".sh", ".bash", ".ps1"].includes(ext);
|
|
1698
|
+
const commentChar = isHashCommentLang ? "#" : "//";
|
|
486
1699
|
const cleanCode = code.split("\n").map((line) => {
|
|
487
|
-
const commentIndex = line.indexOf(
|
|
1700
|
+
const commentIndex = line.indexOf(commentChar);
|
|
488
1701
|
if (commentIndex === -1)
|
|
489
1702
|
return line;
|
|
490
1703
|
const before = line.slice(0, commentIndex);
|
|
491
|
-
const
|
|
492
|
-
|
|
1704
|
+
const inDouble = (before.match(/"/g) || []).length % 2 !== 0;
|
|
1705
|
+
const inSingle = (before.match(/'/g) || []).length % 2 !== 0;
|
|
1706
|
+
const inBacktick = (before.match(/`/g) || []).length % 2 !== 0;
|
|
1707
|
+
return inDouble || inSingle || inBacktick ? line : line.slice(0, commentIndex);
|
|
493
1708
|
}).join("\n");
|
|
494
1709
|
const lines = code.split("\n").length;
|
|
495
1710
|
const entities = parser.extractEntities(cleanCode, filePath);
|
|
@@ -1049,6 +2264,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
1049
2264
|
require_javascript();
|
|
1050
2265
|
require_python();
|
|
1051
2266
|
require_go();
|
|
2267
|
+
require_csharp();
|
|
2268
|
+
require_java();
|
|
2269
|
+
require_kotlin();
|
|
2270
|
+
require_php();
|
|
2271
|
+
require_ruby();
|
|
2272
|
+
require_swift();
|
|
1052
2273
|
var fs_1 = __importDefault(require("fs"));
|
|
1053
2274
|
var collector_1 = require_collector();
|
|
1054
2275
|
var parser_1 = require_parser();
|
|
@@ -1077,7 +2298,7 @@ function getFlag(flag) {
|
|
|
1077
2298
|
}
|
|
1078
2299
|
function printHelp() {
|
|
1079
2300
|
console.log(`
|
|
1080
|
-
${bold("DepGraph")} ${dim("v1.
|
|
2301
|
+
${bold("DepGraph")} ${dim("v1.5.1")}
|
|
1081
2302
|
${dim("Dependency mapping \xB7 Impact simulation \xB7 Developer intelligence")}
|
|
1082
2303
|
|
|
1083
2304
|
${bold("USAGE")}
|