depgraph-core 1.0.2 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/depgraph.js CHANGED
@@ -83,205 +83,1481 @@ var require_javascript = __commonJS({
83
83
  "dist/languages/javascript.js"(exports2) {
84
84
  "use strict";
85
85
  Object.defineProperty(exports2, "__esModule", { value: true });
86
+ exports2.jsEntityPatterns = void 0;
86
87
  var registry_1 = require_registry();
87
88
  var constants_1 = require_constants();
88
89
  function estimateComplexity(code, name) {
89
- const bodyMatch = code.match(new RegExp(`function\\s+${name}[^{]*{([\\s\\S]*?)
90
- }`, "m"));
91
- if (!bodyMatch)
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)
100
+ return "low";
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;
121
+ if (branches <= constants_1.COMPLEXITY_THRESHOLDS.low)
122
+ return "low";
123
+ if (branches <= constants_1.COMPLEXITY_THRESHOLDS.medium)
124
+ return "medium";
125
+ return "high";
126
+ }
127
+ exports2.jsEntityPatterns = [
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
139
+ {
140
+ regex: /^(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s+([A-Z]\w*)\s*(?:<[^>]*>)?\s*\(/gm,
141
+ type: "component"
142
+ },
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)
149
+ {
150
+ regex: /^(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s+(use[A-Z]\w*)\s*(?:<[^>]*>)?\s*\(/gm,
151
+ type: "hook"
152
+ },
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
159
+ {
160
+ regex: /^(?:export\s+)?(?:const|let|var)\s+([A-Za-z_]\w*)\s*=\s*(?:async\s*)?(?:\([^)]*\)|[A-Za-z_]\w*)\s*=>/gm,
161
+ type: "function"
162
+ },
163
+ // function expressions assigned to const / let / var
164
+ {
165
+ regex: /^(?:export\s+)?(?:const|let|var)\s+([A-Za-z_]\w*)\s*=\s*(?:async\s*)?function/gm,
166
+ type: "function"
167
+ },
168
+ // classes (regular, exported, abstract)
169
+ {
170
+ regex: /^(?:export\s+)?(?:default\s+)?(?:abstract\s+)?class\s+([A-Za-z_]\w*)/gm,
171
+ type: "class"
172
+ },
173
+ // TypeScript interfaces
174
+ {
175
+ regex: /^(?:export\s+)?(?:default\s+)?interface\s+([A-Za-z_]\w*)/gm,
176
+ type: "interface"
177
+ },
178
+ // TypeScript types
179
+ {
180
+ regex: /^(?:export\s+)?(?:default\s+)?type\s+([A-Za-z_]\w*)\s*(?:<[^>]*>)?\s*=/gm,
181
+ type: "type"
182
+ },
183
+ // TypeScript enums (regular or const enum)
184
+ {
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,
191
+ type: "api"
192
+ }
193
+ ];
194
+ function extractEntities(code, filePath) {
195
+ const entities = [];
196
+ for (const { regex, type } of exports2.jsEntityPatterns) {
197
+ let match;
198
+ regex.lastIndex = 0;
199
+ while ((match = regex.exec(code)) !== null) {
200
+ const upToMatch = code.slice(0, match.index);
201
+ const line = upToMatch.split("\n").length;
202
+ if (type === "api") {
203
+ entities.push({
204
+ name: `${match[1].toUpperCase()} ${match[2]}`,
205
+ type: "api",
206
+ line,
207
+ complexity: "low"
208
+ });
209
+ } else {
210
+ const name = match[1];
211
+ if (entities.some((e) => e.name === name))
212
+ continue;
213
+ entities.push({
214
+ name,
215
+ type,
216
+ line,
217
+ complexity: estimateComplexity(code, name)
218
+ });
219
+ }
220
+ }
221
+ }
222
+ return entities;
223
+ }
224
+ function extractImports(code) {
225
+ const imports = [];
226
+ const combinedPattern = /^import\s+(?:type\s+)?([A-Za-z_$]\w*)\s*,\s*(?:\{([^}]+)\}|\*\s+as\s+([A-Za-z_$]\w*))\s+from\s+['"]([^'"]+)['"]/gm;
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;
248
+ while ((match = namedPattern.exec(code)) !== null) {
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);
253
+ imports.push({
254
+ source,
255
+ names: [...new Set(names)],
256
+ isLocal: source.startsWith(".") || source.startsWith("/")
257
+ });
258
+ }
259
+ const defaultPattern = /^import\s+(?:type\s+)?([A-Za-z_$]\w*)\s+from\s+['"]([^'"]+)['"]/gm;
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;
275
+ imports.push({
276
+ source,
277
+ names: [match[1]],
278
+ isLocal: source.startsWith(".") || source.startsWith("/")
279
+ });
280
+ }
281
+ const requirePattern = /(?:const|let|var)\s+\{?([^}=]+)\}?\s*=\s*require\s*\(\s*['"]([^'"]+)['"]\s*\)/gm;
282
+ while ((match = requirePattern.exec(code)) !== null) {
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
+ }
305
+ imports.push({
306
+ source,
307
+ names: [...new Set(names)],
308
+ isLocal: source.startsWith(".") || source.startsWith("/")
309
+ });
310
+ }
311
+ return imports;
312
+ }
313
+ function extractExports(code) {
314
+ const exports3 = [];
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;
316
+ let match;
317
+ while ((match = namedPattern.exec(code)) !== null) {
318
+ exports3.push(match[1]);
319
+ }
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;
327
+ while ((match = listPattern.exec(code)) !== null) {
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);
334
+ exports3.push(...names);
335
+ }
336
+ return [...new Set(exports3)];
337
+ }
338
+ var JavaScriptParser = {
339
+ lang: "js",
340
+ extensions: [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"],
341
+ extractEntities,
342
+ extractImports,
343
+ extractExports,
344
+ entityPatterns: exports2.jsEntityPatterns
345
+ };
346
+ (0, registry_1.registerParser)(JavaScriptParser);
347
+ }
348
+ });
349
+
350
+ // dist/languages/python.js
351
+ var require_python = __commonJS({
352
+ "dist/languages/python.js"(exports2) {
353
+ "use strict";
354
+ Object.defineProperty(exports2, "__esModule", { value: true });
355
+ exports2.pyEntityPatterns = void 0;
356
+ var registry_1 = require_registry();
357
+ var constants_1 = require_constants();
358
+ function escapeRegex(s) {
359
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
360
+ }
361
+ function estimateComplexity(code, name) {
362
+ const lines = code.split("\n");
363
+ const defRegex = new RegExp(`^[ \\t]*(?:async\\s+)?def\\s+${escapeRegex(name)}\\s*\\(`, "m");
364
+ const defLine = lines.findIndex((l) => defRegex.test(l));
365
+ if (defLine === -1)
366
+ return "low";
367
+ let bodyStart = defLine;
368
+ while (bodyStart < lines.length && !lines[bodyStart].includes(":")) {
369
+ bodyStart++;
370
+ }
371
+ bodyStart++;
372
+ const bodyLines = [];
373
+ for (let i = bodyStart; i < lines.length; i++) {
374
+ const line = lines[i];
375
+ if (line.trim() === "")
376
+ continue;
377
+ if (!line.match(/^\s+/))
378
+ break;
379
+ bodyLines.push(line);
380
+ }
381
+ const branches = (bodyLines.join("\n").match(/\b(if|elif|else|for|while|except|and|or|match|case)\b/g) || []).length;
382
+ if (branches <= constants_1.COMPLEXITY_THRESHOLDS.low)
383
+ return "low";
384
+ if (branches <= constants_1.COMPLEXITY_THRESHOLDS.medium)
385
+ return "medium";
386
+ return "high";
387
+ }
388
+ exports2.pyEntityPatterns = [
389
+ // functions and methods (including async def)
390
+ {
391
+ regex: /^[ \t]*(?:async\s+)?def\s+([A-Za-z_]\w*)\s*\(/gm,
392
+ type: "function"
393
+ },
394
+ // classes (with optional generic parameters [T] and base classes (Base))
395
+ {
396
+ regex: /^[ \t]*class\s+([A-Za-z_]\w*)(?:\s*\[[^\]]*\])?(?:\s*\([^)]*\))?\s*:/gm,
397
+ type: "class"
398
+ }
399
+ ];
400
+ function extractEntities(code, filePath) {
401
+ const entities = [];
402
+ for (const { regex, type } of exports2.pyEntityPatterns) {
403
+ regex.lastIndex = 0;
404
+ let match;
405
+ while ((match = regex.exec(code)) !== null) {
406
+ const name = match[1];
407
+ if (type === "function" && name.startsWith("__") && name.endsWith("__"))
408
+ continue;
409
+ const upToMatch = code.slice(0, match.index);
410
+ const line = upToMatch.split("\n").length;
411
+ if (entities.some((e) => e.name === name && e.line === line))
412
+ continue;
413
+ entities.push({
414
+ name,
415
+ type,
416
+ line,
417
+ complexity: type === "function" ? estimateComplexity(code, name) : "low"
418
+ });
419
+ }
420
+ }
421
+ return entities;
422
+ }
423
+ function stripAlias(name) {
424
+ return name.replace(/\s+as\s+[A-Za-z_]\w*$/, "").trim();
425
+ }
426
+ function extractImports(code) {
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, ", "));
429
+ const fromPattern = /^from\s+([\w.]+)\s+import\s+(.+)$/gm;
430
+ let match;
431
+ while ((match = fromPattern.exec(normalised)) !== null) {
432
+ const source = match[1];
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(".") });
436
+ }
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
+ }
452
+ }
453
+ return imports;
454
+ }
455
+ function extractExports(code) {
456
+ const allMatch = code.match(/__all__\s*=\s*[\[\(]([\s\S]*?)[\]\)]/);
457
+ if (!allMatch)
458
+ return [];
459
+ return allMatch[1].split(",").map((n) => n.trim().replace(/['"]/g, "").replace(/#.*$/, "").trim()).filter((n) => n.length > 0);
460
+ }
461
+ var PythonParser = {
462
+ lang: "py",
463
+ extensions: [".py"],
464
+ extractEntities,
465
+ extractImports,
466
+ extractExports,
467
+ entityPatterns: exports2.pyEntityPatterns
468
+ };
469
+ (0, registry_1.registerParser)(PythonParser);
470
+ }
471
+ });
472
+
473
+ // dist/languages/go.js
474
+ var require_go = __commonJS({
475
+ "dist/languages/go.js"(exports2) {
476
+ "use strict";
477
+ Object.defineProperty(exports2, "__esModule", { value: true });
478
+ exports2.goEntityPatterns = void 0;
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
+ }
519
+ exports2.goEntityPatterns = [
520
+ // functions and methods with optional receiver and type parameters (generics)
521
+ {
522
+ regex: /^func\s+(?:\([^)]*\)\s+)?([A-Za-z_]\w*)\s*(?:\[[^\]]*\])?\s*\(/gm,
523
+ type: "function"
524
+ },
525
+ // type declarations (structs, interfaces) with optional type parameters
526
+ {
527
+ regex: /^type\s+([A-Za-z_]\w*)\s*(?:\[[^\]]*\])?\s+(?:struct|interface)/gm,
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"
534
+ }
535
+ ];
536
+ function extractEntities(code, filePath) {
537
+ const entities = [];
538
+ for (const { regex, type } of exports2.goEntityPatterns) {
539
+ regex.lastIndex = 0;
540
+ let match;
541
+ while ((match = regex.exec(code)) !== null) {
542
+ const name = match[1];
543
+ if (entities.some((e) => e.name === name))
544
+ continue;
545
+ const upToMatch = code.slice(0, match.index);
546
+ const line = upToMatch.split("\n").length;
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
+ }
597
+ }
598
+ }
599
+ return entities;
600
+ }
601
+ function extractImports(code) {
602
+ const imports = [];
603
+ const singlePattern = /^import\s+(?:([A-Za-z_.\w]+)\s+)?["']([^"']+)["']/gm;
604
+ let match;
605
+ while ((match = singlePattern.exec(code)) !== null) {
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
+ });
614
+ }
615
+ const blockPattern = /import\s*\(\s*([\s\S]*?)\s*\)/gm;
616
+ while ((match = blockPattern.exec(code)) !== null) {
617
+ const lines = match[1].split("\n");
618
+ for (const line of lines) {
619
+ const cleanLine = line.replace(/\/\/.*$/, "").trim();
620
+ const pkgMatch = cleanLine.match(/^(?:([A-Za-z_.\w]+)\s+)?["']([^"']+)["']/);
621
+ if (pkgMatch) {
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
+ });
630
+ }
631
+ }
632
+ }
633
+ return imports;
634
+ }
635
+ function extractExports(code) {
636
+ const exports3 = [];
637
+ const funcPattern = /^func\s+(?:\([^)]*\)\s+)?([A-Z]\w*)\s*(?:\[[^\]]*\])?\s*\(/gm;
638
+ let 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) {
668
+ exports3.push(match[1]);
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
+ }
682
+ return [...new Set(exports3)];
683
+ }
684
+ var GoParser = {
685
+ lang: "go",
686
+ extensions: [".go"],
687
+ extractEntities,
688
+ extractImports,
689
+ extractExports,
690
+ entityPatterns: exports2.goEntityPatterns
691
+ };
692
+ (0, registry_1.registerParser)(GoParser);
693
+ }
694
+ });
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)
92
1154
  return "low";
93
- const body = bodyMatch[1];
94
- const branches = (body.match(/\b(if|else|for|while|switch|catch|&&|\|\|)\b/g) || []).length;
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;
95
1174
  if (branches <= constants_1.COMPLEXITY_THRESHOLDS.low)
96
1175
  return "low";
97
1176
  if (branches <= constants_1.COMPLEXITY_THRESHOLDS.medium)
98
1177
  return "medium";
99
1178
  return "high";
100
1179
  }
101
- function extractEntities(code, filePath) {
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) {
102
1208
  const entities = [];
103
- const lines = code.split("\n");
104
- const patterns = [
105
- // React components (PascalCase arrow functions)
106
- {
107
- regex: /^(?:export\s+)?const\s+([A-Z]\w+)\s*=\s*(?:\([^)]*\)|[^=])\s*=>/gm,
108
- type: "component"
109
- },
110
- // React hooks (camelCase starting with "use")
111
- {
112
- regex: /^(?:export\s+)?(?:const\s+)?(use[A-Z]\w+)\s*=/gm,
113
- type: "hook"
114
- },
115
- // regular functions
116
- {
117
- regex: /^(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s+(\w+)\s*\(/gm,
118
- type: "function"
119
- },
120
- // arrow functions assigned to const
121
- {
122
- regex: /^(?:export\s+)?const\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>/gm,
123
- type: "function"
124
- },
125
- // classes
126
- {
127
- regex: /^(?:export\s+)?(?:default\s+)?class\s+(\w+)/gm,
128
- type: "class"
129
- },
130
- // TypeScript interfaces
131
- {
132
- regex: /^(?:export\s+)?interface\s+(\w+)/gm,
133
- type: "interface"
134
- },
135
- // TypeScript types
136
- {
137
- regex: /^(?:export\s+)?type\s+(\w+)\s*=/gm,
138
- type: "type"
139
- },
140
- // Express routes
141
- {
142
- regex: /(?:app|router)\.(get|post|put|delete|patch)\s*\(\s*['"]([^'"]+)['"]/gm,
143
- type: "api"
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 });
144
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
145
1271
  ];
146
- for (const { regex, type } of patterns) {
1272
+ for (const pattern of patterns) {
147
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) {
148
1369
  regex.lastIndex = 0;
1370
+ let match;
149
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;
150
1376
  const upToMatch = code.slice(0, match.index);
151
1377
  const line = upToMatch.split("\n").length;
152
- if (type === "api") {
153
- entities.push({
154
- name: `${match[1].toUpperCase()} ${match[2]}`,
155
- type: "api",
156
- line,
157
- complexity: "low"
158
- });
159
- } else {
160
- const name = match[1];
161
- if (entities.some((e) => e.name === name))
162
- continue;
163
- entities.push({
164
- name,
165
- type,
166
- line,
167
- complexity: estimateComplexity(code, name)
168
- });
169
- }
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
+ });
170
1386
  }
171
1387
  }
172
1388
  return entities;
173
1389
  }
174
1390
  function extractImports(code) {
175
1391
  const imports = [];
176
- const namedPattern = /^import\s+\{([^}]+)\}\s+from\s+['"]([^'"]+)['"]/gm;
1392
+ const requirePattern = /^[ \t]*require\s+['"]([^'"]+)['"]/gm;
177
1393
  let match;
178
- while ((match = namedPattern.exec(code)) !== null) {
179
- const names = match[1].split(",").map((n) => n.trim().replace(/\s+as\s+\w+/, ""));
180
- const source = match[2];
181
- imports.push({
182
- source,
183
- names,
184
- isLocal: source.startsWith(".")
185
- });
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 });
186
1398
  }
187
- const defaultPattern = /^import\s+(\w+)\s+from\s+['"]([^'"]+)['"]/gm;
188
- while ((match = defaultPattern.exec(code)) !== null) {
189
- imports.push({
190
- source: match[2],
191
- names: [match[1]],
192
- isLocal: match[2].startsWith(".")
193
- });
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 });
194
1404
  }
195
- const requirePattern = /(?:const|let|var)\s+\{?([^}=]+)\}?\s*=\s*require\s*\(\s*['"]([^'"]+)['"]\s*\)/gm;
196
- while ((match = requirePattern.exec(code)) !== null) {
197
- const names = match[1].split(",").map((n) => n.trim());
198
- imports.push({
199
- source: match[2],
200
- names,
201
- isLocal: match[2].startsWith(".")
202
- });
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 });
203
1416
  }
204
1417
  return imports;
205
1418
  }
206
1419
  function extractExports(code) {
207
1420
  const exports3 = [];
208
- const namedPattern = /^export\s+(?:default\s+)?(?:async\s+)?(?:function|class|const|let|var|type|interface)\s+(\w+)/gm;
1421
+ const typePattern = /^(?:class|module)\s+([A-Z]\w*(?:::[A-Z]\w*)*)/gm;
209
1422
  let match;
210
- while ((match = namedPattern.exec(code)) !== null) {
1423
+ while ((match = typePattern.exec(code)) !== null) {
211
1424
  exports3.push(match[1]);
212
1425
  }
213
- const listPattern = /^export\s+\{([^}]+)\}/gm;
214
- while ((match = listPattern.exec(code)) !== null) {
215
- const names = match[1].split(",").map((n) => n.trim());
216
- exports3.push(...names);
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]));
217
1434
  }
218
1435
  return [...new Set(exports3)];
219
1436
  }
220
- var JavaScriptParser = {
221
- lang: "js",
222
- extensions: [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx"],
1437
+ var RubyParser = {
1438
+ lang: "ruby",
1439
+ extensions: [".rb", ".rake", ".gemspec"],
223
1440
  extractEntities,
224
1441
  extractImports,
225
- extractExports
1442
+ extractExports,
1443
+ entityPatterns: exports2.rubyEntityPatterns
226
1444
  };
227
- (0, registry_1.registerParser)(JavaScriptParser);
1445
+ (0, registry_1.registerParser)(RubyParser);
228
1446
  }
229
1447
  });
230
1448
 
231
- // dist/languages/python.js
232
- var require_python = __commonJS({
233
- "dist/languages/python.js"(exports2) {
1449
+ // dist/languages/swift.js
1450
+ var require_swift = __commonJS({
1451
+ "dist/languages/swift.js"(exports2) {
234
1452
  "use strict";
235
1453
  Object.defineProperty(exports2, "__esModule", { value: true });
1454
+ exports2.swiftEntityPatterns = void 0;
236
1455
  var registry_1 = require_registry();
237
1456
  var constants_1 = require_constants();
1457
+ function escapeRegex(s) {
1458
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1459
+ }
238
1460
  function estimateComplexity(code, name) {
239
1461
  const lines = code.split("\n");
240
- const defLine = lines.findIndex((l) => l.match(new RegExp(`def\\s+${name}\\s*\\(`)));
241
- if (defLine === -1)
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)
242
1471
  return "low";
1472
+ let braceCount = 0;
1473
+ let started = false;
243
1474
  const bodyLines = [];
244
- for (let i = defLine + 1; i < lines.length; i++) {
1475
+ for (let i = startLine; i < lines.length; i++) {
245
1476
  const line = lines[i];
246
- if (line.trim() === "")
247
- continue;
248
- if (!line.match(/^\s+/))
249
- break;
1477
+ for (const ch of line) {
1478
+ if (ch === "{") {
1479
+ braceCount++;
1480
+ started = true;
1481
+ } else if (ch === "}") {
1482
+ braceCount--;
1483
+ }
1484
+ }
250
1485
  bodyLines.push(line);
1486
+ if (started && braceCount <= 0)
1487
+ break;
251
1488
  }
252
1489
  const body = bodyLines.join("\n");
253
- const branches = (body.match(/\b(if|elif|else|for|while|except|and|or)\b/g) || []).length;
1490
+ const branches = (body.match(/\b(if|else\s+if|else|for\s|while|repeat|switch|case|catch|guard|&&|\|\|)\b/g) || []).length;
254
1491
  if (branches <= constants_1.COMPLEXITY_THRESHOLDS.low)
255
1492
  return "low";
256
1493
  if (branches <= constants_1.COMPLEXITY_THRESHOLDS.medium)
257
1494
  return "medium";
258
1495
  return "high";
259
1496
  }
260
- function extractEntities(code, filePath) {
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) {
261
1551
  const entities = [];
262
- const patterns = [
263
- // regular functions
264
- {
265
- regex: /^(?:async\s+)?def\s+(\w+)\s*\(/gm,
266
- type: "function"
267
- },
268
- // classes
269
- {
270
- regex: /^class\s+(\w+)(?:\s*\([^)]*\))?\s*:/gm,
271
- type: "class"
272
- }
273
- ];
274
- for (const { regex, type } of patterns) {
1552
+ for (const { regex, type } of exports2.swiftEntityPatterns) {
275
1553
  regex.lastIndex = 0;
276
1554
  let match;
277
1555
  while ((match = regex.exec(code)) !== null) {
278
- const name = match[1];
279
- if (type === "function" && name.startsWith("__") && name.endsWith("__"))
280
- continue;
281
- if (entities.some((e) => e.name === name))
282
- continue;
1556
+ const name = match[1] ?? (/\binit\b/.test(match[0]) ? "init" : /\bdeinit\b/.test(match[0]) ? "deinit" : "subscript");
283
1557
  const upToMatch = code.slice(0, match.index);
284
1558
  const line = upToMatch.split("\n").length;
1559
+ if (entities.some((e) => e.name === name && e.line === line))
1560
+ continue;
285
1561
  entities.push({
286
1562
  name,
287
1563
  type,
@@ -294,40 +1570,53 @@ var require_python = __commonJS({
294
1570
  }
295
1571
  function extractImports(code) {
296
1572
  const imports = [];
297
- const fromPattern = /^from\s+([\w.]+)\s+import\s+(.+)$/gm;
1573
+ const importPattern = /^[ \t]*import\s+(?:(?:class|struct|enum|func|var|let|typealias)\s+)?([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)/gm;
298
1574
  let match;
299
- while ((match = fromPattern.exec(code)) !== null) {
300
- const source = match[1];
301
- const names = match[2].split(",").map((n) => n.trim()).filter((n) => n.length > 0);
302
- const isLocal = source.startsWith(".");
303
- imports.push({ source, names, isLocal });
304
- }
305
- const importPattern = /^import\s+([\w.]+)/gm;
306
1575
  while ((match = importPattern.exec(code)) !== null) {
307
- const source = match[1];
1576
+ const fullPath = match[1];
1577
+ const moduleName = fullPath.split(".")[0];
1578
+ if (["class", "struct", "enum", "func", "var", "let", "typealias"].includes(moduleName))
1579
+ continue;
308
1580
  imports.push({
309
- source,
310
- names: [source],
1581
+ source: moduleName,
1582
+ names: [moduleName],
311
1583
  isLocal: false
312
- // bare imports are always external
313
1584
  });
314
1585
  }
315
1586
  return imports;
316
1587
  }
317
1588
  function extractExports(code) {
318
- const allMatch = code.match(/__all__\s*=\s*\[([^\]]+)\]/);
319
- if (!allMatch)
320
- return [];
321
- return allMatch[1].split(",").map((n) => n.trim().replace(/['"]/g, "")).filter((n) => n.length > 0);
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)];
322
1610
  }
323
- var PythonParser = {
324
- lang: "py",
325
- extensions: [".py"],
1611
+ var SwiftParser = {
1612
+ lang: "swift",
1613
+ extensions: [".swift"],
326
1614
  extractEntities,
327
1615
  extractImports,
328
- extractExports
1616
+ extractExports,
1617
+ entityPatterns: exports2.swiftEntityPatterns
329
1618
  };
330
- (0, registry_1.registerParser)(PythonParser);
1619
+ (0, registry_1.registerParser)(SwiftParser);
331
1620
  }
332
1621
  });
333
1622
 
@@ -405,13 +1694,17 @@ var require_parser = __commonJS({
405
1694
  const parser = (0, registry_1.getLanguageParser)(ext);
406
1695
  if (!parser)
407
1696
  return null;
1697
+ const isHashCommentLang = [".py", ".rb", ".sh", ".bash", ".ps1"].includes(ext);
1698
+ const commentChar = isHashCommentLang ? "#" : "//";
408
1699
  const cleanCode = code.split("\n").map((line) => {
409
- const commentIndex = line.indexOf("//");
1700
+ const commentIndex = line.indexOf(commentChar);
410
1701
  if (commentIndex === -1)
411
1702
  return line;
412
1703
  const before = line.slice(0, commentIndex);
413
- const inString = (before.match(/"/g) || []).length % 2 !== 0 || (before.match(/'/g) || []).length % 2 !== 0;
414
- return inString ? line : line.slice(0, commentIndex);
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);
415
1708
  }).join("\n");
416
1709
  const lines = code.split("\n").length;
417
1710
  const entities = parser.extractEntities(cleanCode, filePath);
@@ -812,6 +2105,157 @@ var require_output = __commonJS({
812
2105
  }
813
2106
  });
814
2107
 
2108
+ // dist/stages/gitdiff.js
2109
+ var require_gitdiff = __commonJS({
2110
+ "dist/stages/gitdiff.js"(exports2) {
2111
+ "use strict";
2112
+ var __importDefault2 = exports2 && exports2.__importDefault || function(mod) {
2113
+ return mod && mod.__esModule ? mod : { "default": mod };
2114
+ };
2115
+ Object.defineProperty(exports2, "__esModule", { value: true });
2116
+ exports2.getChangedEntities = getChangedEntities;
2117
+ var child_process_1 = require("child_process");
2118
+ var path_1 = __importDefault2(require("path"));
2119
+ var registry_1 = require_registry();
2120
+ function getChangedEntities(options) {
2121
+ const diff = runGitDiff(options);
2122
+ if (!diff)
2123
+ return [];
2124
+ return parseDiff(diff, options.projectDir);
2125
+ }
2126
+ function runGitDiff(options) {
2127
+ const { projectDir: projectDir2, mode, commit, from, to } = options;
2128
+ let command;
2129
+ if (mode === "uncommitted") {
2130
+ command = "git diff HEAD";
2131
+ } else if (mode === "last-commit") {
2132
+ if (commit) {
2133
+ command = `git diff ${commit}~1 ${commit}`;
2134
+ } else {
2135
+ command = "git diff HEAD~1 HEAD";
2136
+ }
2137
+ } else if (mode === "branches") {
2138
+ if (!from || !to) {
2139
+ console.warn("\u26A0 --from and --to are required for branch comparison");
2140
+ return null;
2141
+ }
2142
+ command = `git diff ${from}...${to}`;
2143
+ } else {
2144
+ return null;
2145
+ }
2146
+ try {
2147
+ const result = (0, child_process_1.execSync)(command, {
2148
+ cwd: projectDir2,
2149
+ encoding: "utf-8",
2150
+ stdio: ["pipe", "pipe", "pipe"]
2151
+ });
2152
+ return result || null;
2153
+ } catch (err) {
2154
+ console.warn(`\u26A0 Git command failed: ${command}`);
2155
+ console.warn(` Make sure ${projectDir2} is a git repository`);
2156
+ return null;
2157
+ }
2158
+ }
2159
+ function parseDiff(diff, projectDir2) {
2160
+ const entities = [];
2161
+ const lines = diff.split("\n");
2162
+ let currentFile = "";
2163
+ let changeType = "modified";
2164
+ for (let i = 0; i < lines.length; i++) {
2165
+ const line = lines[i];
2166
+ if (line.startsWith("diff --git")) {
2167
+ const fileMatch = line.match(/b\/(.+)$/);
2168
+ if (fileMatch) {
2169
+ currentFile = fileMatch[1];
2170
+ }
2171
+ changeType = "modified";
2172
+ continue;
2173
+ }
2174
+ if (line.startsWith("new file mode")) {
2175
+ changeType = "added";
2176
+ continue;
2177
+ }
2178
+ if (line.startsWith("deleted file mode")) {
2179
+ changeType = "deleted";
2180
+ continue;
2181
+ }
2182
+ if (line.startsWith("index ")) {
2183
+ if (changeType === "modified")
2184
+ changeType = "modified";
2185
+ continue;
2186
+ }
2187
+ if (line.startsWith("@@")) {
2188
+ const contextMatch = line.match(/@@[^@]*@@\s*(.+)$/);
2189
+ if (contextMatch) {
2190
+ const context = contextMatch[1].trim();
2191
+ const entity = extractEntityFromContext(context, currentFile);
2192
+ if (entity) {
2193
+ const description = buildDescription(lines, i, entity.name);
2194
+ const alreadyFound = entities.some((e) => e.name === entity.name && e.file === currentFile);
2195
+ if (!alreadyFound) {
2196
+ entities.push({
2197
+ name: entity.name,
2198
+ file: currentFile,
2199
+ changeType,
2200
+ description
2201
+ });
2202
+ }
2203
+ }
2204
+ }
2205
+ continue;
2206
+ }
2207
+ }
2208
+ return entities;
2209
+ }
2210
+ function extractEntityFromContext(context, file) {
2211
+ const ext = path_1.default.extname(file).toLowerCase();
2212
+ const parser = (0, registry_1.getLanguageParser)(ext);
2213
+ if (parser?.entityPatterns) {
2214
+ for (const { regex, type } of parser.entityPatterns) {
2215
+ if (type === "api")
2216
+ continue;
2217
+ const singleLineRegex = new RegExp(regex.source, regex.flags.replace("g", ""));
2218
+ const m = singleLineRegex.exec(context);
2219
+ if (m?.[1])
2220
+ return { name: m[1], type };
2221
+ }
2222
+ return null;
2223
+ }
2224
+ if ([".java", ".cs"].includes(ext)) {
2225
+ const m = context.match(/(?:public|private|protected|static|override|async|virtual)\s+\S+\s+(\w+)\s*\(/);
2226
+ if (m)
2227
+ return { name: m[1], type: "method" };
2228
+ }
2229
+ return null;
2230
+ }
2231
+ function buildDescription(lines, contextIdx, entityName) {
2232
+ const added = [];
2233
+ const removed = [];
2234
+ for (let i = contextIdx + 1; i < Math.min(contextIdx + 20, lines.length); i++) {
2235
+ const line = lines[i];
2236
+ if (line.startsWith("@@") || line.startsWith("diff"))
2237
+ break;
2238
+ if (line.startsWith("+") && !line.startsWith("+++")) {
2239
+ added.push(line.slice(1).trim());
2240
+ }
2241
+ if (line.startsWith("-") && !line.startsWith("---")) {
2242
+ removed.push(line.slice(1).trim());
2243
+ }
2244
+ }
2245
+ if (added.length === 0 && removed.length > 0) {
2246
+ return `${entityName}: ${removed.length} line(s) removed`;
2247
+ }
2248
+ if (added.length > 0 && removed.length === 0) {
2249
+ return `${entityName}: ${added.length} line(s) added`;
2250
+ }
2251
+ if (added.length > 0 && removed.length > 0) {
2252
+ return `${entityName}: ${removed.length} line(s) changed to ${added.length} new line(s)`;
2253
+ }
2254
+ return `${entityName}: modified`;
2255
+ }
2256
+ }
2257
+ });
2258
+
815
2259
  // dist/main.js
816
2260
  var __importDefault = exports && exports.__importDefault || function(mod) {
817
2261
  return mod && mod.__esModule ? mod : { "default": mod };
@@ -819,6 +2263,13 @@ var __importDefault = exports && exports.__importDefault || function(mod) {
819
2263
  Object.defineProperty(exports, "__esModule", { value: true });
820
2264
  require_javascript();
821
2265
  require_python();
2266
+ require_go();
2267
+ require_csharp();
2268
+ require_java();
2269
+ require_kotlin();
2270
+ require_php();
2271
+ require_ruby();
2272
+ require_swift();
822
2273
  var fs_1 = __importDefault(require("fs"));
823
2274
  var collector_1 = require_collector();
824
2275
  var parser_1 = require_parser();
@@ -826,6 +2277,7 @@ var graph_1 = require_graph();
826
2277
  var metrics_1 = require_metrics();
827
2278
  var impact_1 = require_impact();
828
2279
  var output_1 = require_output();
2280
+ var gitdiff_1 = require_gitdiff();
829
2281
  var args = process.argv.slice(2);
830
2282
  var noColor = args.includes("--no-color");
831
2283
  var verbose = args.includes("--verbose");
@@ -846,11 +2298,11 @@ function getFlag(flag) {
846
2298
  }
847
2299
  function printHelp() {
848
2300
  console.log(`
849
- ${bold("DepGraph Compiler")} ${dim("v1.0.2")}
2301
+ ${bold("DepGraph")} ${dim("v1.5.0")}
850
2302
  ${dim("Dependency mapping \xB7 Impact simulation \xB7 Developer intelligence")}
851
2303
 
852
2304
  ${bold("USAGE")}
853
- node depgraph.js ${cyan("<projectDir>")} ${dim("[options]")}
2305
+ depgraph ${cyan("<projectDir>")} ${dim("[options]")}
854
2306
 
855
2307
  ${bold("OPTIONS")}
856
2308
  ${cyan("--output")} ${dim("<file>")} Output path ${dim("(default: ./depgraph-output.json)")}
@@ -859,24 +2311,43 @@ ${bold("OPTIONS")}
859
2311
  ${cyan("--no-color")} Disable colors ${dim("(for CI)")}
860
2312
  ${cyan("--help, -h")} Show this help message
861
2313
 
2314
+ ${bold("GIT FLAGS")}
2315
+ ${cyan("--git-impact")} Auto-detect changes from git diff
2316
+ ${cyan("--commit")} ${dim("<sha>")} Analyze a specific commit
2317
+ ${cyan("--from")} ${dim("<branch>")} Compare from this branch
2318
+ ${cyan("--to")} ${dim("<branch>")} Compare to this branch
2319
+
862
2320
  ${bold("EXAMPLES")}
863
2321
  ${dim("# Map a project")}
864
- node depgraph.js ./my-app
2322
+ depgraph ./my-app
865
2323
 
866
2324
  ${dim("# Map with custom output")}
867
- node depgraph.js ./my-app --output ./reports/graph.json
2325
+ depgraph ./my-app --output ./reports/graph.json
868
2326
 
869
2327
  ${dim("# Simulate a change")}
870
- node depgraph.js ./my-app --impact "getUserById" "removing userId param"
2328
+ depgraph ./my-app --impact "getUserById" "removing userId param"
871
2329
 
872
2330
  ${dim("# CI mode")}
873
- node depgraph.js ./src --no-color --output ./ci/depgraph.json
2331
+ depgraph ./src --no-color --output ./ci/depgraph.json
2332
+
2333
+ ${bold("GIT EXAMPLES")}
2334
+ ${dim("# Analyze uncommitted changes")}
2335
+ depgraph ./src --git-impact
2336
+
2337
+ ${dim("# Analyze last commit")}
2338
+ depgraph ./src --git-impact --commit HEAD
2339
+
2340
+ ${dim("# Compare two branches")}
2341
+ depgraph ./src --git-impact --from main --to feature/my-branch
2342
+
2343
+ ${dim("# Specific commit")}
2344
+ depgraph ./src --git-impact --commit abc1234
874
2345
  `);
875
2346
  }
876
2347
  function printBanner() {
877
2348
  console.log(`
878
2349
  ${bold("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501")}
879
- ${bold(" DepGraph Compiler")} ${dim("v1.0.0")}
2350
+ ${bold(" DepGraph")} ${dim("v1.0.0")}
880
2351
  ${bold("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501")}
881
2352
  `);
882
2353
  }
@@ -936,6 +2407,11 @@ var projectDir = args[0];
936
2407
  var outputPath = getFlag("--output") ?? "./depgraph-output.json";
937
2408
  var impactTarget = getFlag("--impact");
938
2409
  var impactDesc = impactTarget ? args[args.indexOf("--impact") + 2] ?? "no description provided" : void 0;
2410
+ var gitImpact = args.includes("--git-impact");
2411
+ var gitCommit = getFlag("--commit");
2412
+ var gitFrom = getFlag("--from");
2413
+ var gitTo = getFlag("--to");
2414
+ var gitMode = gitFrom && gitTo ? "branches" : gitCommit ? "last-commit" : "uncommitted";
939
2415
  printBanner();
940
2416
  console.log(`${bold("\u{1F50D} Scanning")} ${cyan(projectDir)}
941
2417
  `);
@@ -959,6 +2435,34 @@ try {
959
2435
  if (impactTarget) {
960
2436
  impact = (0, impact_1.simulateImpact)(metrics, impactTarget, impactDesc ?? "");
961
2437
  printImpact(impact);
2438
+ } else if (gitImpact) {
2439
+ console.log(`
2440
+ ${bold("\u{1F50D} Reading git diff...")}`);
2441
+ const changed = (0, gitdiff_1.getChangedEntities)({
2442
+ projectDir,
2443
+ mode: gitMode,
2444
+ commit: gitCommit,
2445
+ from: gitFrom,
2446
+ to: gitTo
2447
+ });
2448
+ if (changed.length === 0) {
2449
+ console.log(`
2450
+ ${green("\u2713")} No changed entities found in diff`);
2451
+ } else {
2452
+ console.log(`
2453
+ ${bold(`Found ${changed.length} changed entity(s):`)}`);
2454
+ for (const entity of changed) {
2455
+ console.log(` ${dim("\u2192")} ${entity.name} ${dim(`(${entity.file})`)}`);
2456
+ }
2457
+ console.log(`
2458
+ ${bold("Running impact simulation...")}`);
2459
+ for (const entity of changed) {
2460
+ console.log(`
2461
+ ${dim("\u2500".repeat(42))}`);
2462
+ const result = (0, impact_1.simulateImpact)(metrics, entity.name, entity.description);
2463
+ printImpact(result);
2464
+ }
2465
+ }
962
2466
  }
963
2467
  (0, output_1.writeOutput)(metrics, parsed, outputPath, impact);
964
2468
  } catch (err) {