archtracker-mcp 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +474 -51
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +474 -51
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.js +480 -57
- package/dist/mcp/index.js.map +1 -1
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -177,12 +177,50 @@ function stripComments(content, style) {
|
|
|
177
177
|
return stripRuby(content);
|
|
178
178
|
case "php":
|
|
179
179
|
return stripPhp(content);
|
|
180
|
+
default: {
|
|
181
|
+
const _exhaustive = style;
|
|
182
|
+
throw new Error(`Unknown comment style: ${_exhaustive}`);
|
|
183
|
+
}
|
|
180
184
|
}
|
|
181
185
|
}
|
|
182
186
|
function stripCStyle(content) {
|
|
183
187
|
let result = "";
|
|
184
188
|
let i = 0;
|
|
185
189
|
while (i < content.length) {
|
|
190
|
+
if (content[i] === "r" && i + 1 < content.length) {
|
|
191
|
+
let hashes = 0;
|
|
192
|
+
let j = i + 1;
|
|
193
|
+
while (j < content.length && content[j] === "#") {
|
|
194
|
+
hashes++;
|
|
195
|
+
j++;
|
|
196
|
+
}
|
|
197
|
+
if (j < content.length && content[j] === '"') {
|
|
198
|
+
for (let k = i; k <= j; k++) {
|
|
199
|
+
result += " ";
|
|
200
|
+
}
|
|
201
|
+
i = j + 1;
|
|
202
|
+
while (i < content.length) {
|
|
203
|
+
if (content[i] === '"') {
|
|
204
|
+
let matchHashes = 0;
|
|
205
|
+
let m = i + 1;
|
|
206
|
+
while (m < content.length && content[m] === "#" && matchHashes < hashes) {
|
|
207
|
+
matchHashes++;
|
|
208
|
+
m++;
|
|
209
|
+
}
|
|
210
|
+
if (matchHashes === hashes) {
|
|
211
|
+
for (let k = i; k < m; k++) {
|
|
212
|
+
result += " ";
|
|
213
|
+
}
|
|
214
|
+
i = m;
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
result += content[i] === "\n" ? "\n" : " ";
|
|
219
|
+
i++;
|
|
220
|
+
}
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
186
224
|
if (content[i] === "/" && content[i + 1] === "/") {
|
|
187
225
|
while (i < content.length && content[i] !== "\n") {
|
|
188
226
|
result += " ";
|
|
@@ -225,6 +263,24 @@ function stripCStyle(content) {
|
|
|
225
263
|
result += content[i];
|
|
226
264
|
i++;
|
|
227
265
|
}
|
|
266
|
+
} else if (content[i] === "'") {
|
|
267
|
+
result += content[i];
|
|
268
|
+
i++;
|
|
269
|
+
while (i < content.length && content[i] !== "'") {
|
|
270
|
+
if (content[i] === "\\" && i + 1 < content.length) {
|
|
271
|
+
result += content[i];
|
|
272
|
+
i++;
|
|
273
|
+
result += content[i];
|
|
274
|
+
i++;
|
|
275
|
+
} else {
|
|
276
|
+
result += content[i];
|
|
277
|
+
i++;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (i < content.length) {
|
|
281
|
+
result += content[i];
|
|
282
|
+
i++;
|
|
283
|
+
}
|
|
228
284
|
} else if (content[i] === "`") {
|
|
229
285
|
result += " ";
|
|
230
286
|
i++;
|
|
@@ -293,31 +349,62 @@ function stripPython(content) {
|
|
|
293
349
|
let result = "";
|
|
294
350
|
let i = 0;
|
|
295
351
|
while (i < content.length) {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
352
|
+
let prefixLen = 0;
|
|
353
|
+
let isRaw = false;
|
|
354
|
+
if (i < content.length) {
|
|
355
|
+
const c0 = content[i];
|
|
356
|
+
const c1 = i + 1 < content.length ? content[i + 1] : "";
|
|
357
|
+
const c2 = i + 2 < content.length ? content[i + 2] : "";
|
|
358
|
+
if ((c0 === "r" || c0 === "R" || c0 === "b" || c0 === "B" || c0 === "f" || c0 === "F") && (c1 === "r" || c1 === "R" || c1 === "b" || c1 === "B" || c1 === "f" || c1 === "F") && (c2 === '"' || c2 === "'")) {
|
|
359
|
+
const pair = (c0 + c1).toLowerCase();
|
|
360
|
+
if (pair === "rb" || pair === "br" || pair === "fr" || pair === "rf") {
|
|
361
|
+
prefixLen = 2;
|
|
362
|
+
isRaw = pair.includes("r");
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (prefixLen === 0 && (c0 === "r" || c0 === "R" || c0 === "b" || c0 === "B" || c0 === "f" || c0 === "F") && (c1 === '"' || c1 === "'")) {
|
|
366
|
+
prefixLen = 1;
|
|
367
|
+
isRaw = c0 === "r" || c0 === "R";
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
const quoteStart = i + prefixLen;
|
|
371
|
+
if (quoteStart + 2 < content.length && (content[quoteStart] === '"' || content[quoteStart] === "'") && content[quoteStart + 1] === content[quoteStart] && content[quoteStart + 2] === content[quoteStart]) {
|
|
372
|
+
const quote = content[quoteStart];
|
|
373
|
+
for (let k = 0; k < prefixLen; k++) {
|
|
374
|
+
result += " ";
|
|
375
|
+
}
|
|
299
376
|
result += " ";
|
|
300
|
-
i
|
|
377
|
+
i = quoteStart + 3;
|
|
301
378
|
while (i < content.length) {
|
|
302
379
|
if (content[i] === quote && content[i + 1] === quote && content[i + 2] === quote) {
|
|
303
380
|
result += " ";
|
|
304
381
|
i += 3;
|
|
305
382
|
break;
|
|
306
383
|
}
|
|
307
|
-
|
|
308
|
-
|
|
384
|
+
if (!isRaw && content[i] === "\\" && i + 1 < content.length) {
|
|
385
|
+
result += " ";
|
|
386
|
+
i++;
|
|
387
|
+
result += content[i] === "\n" ? "\n" : " ";
|
|
388
|
+
i++;
|
|
389
|
+
} else {
|
|
390
|
+
result += content[i] === "\n" ? "\n" : " ";
|
|
391
|
+
i++;
|
|
392
|
+
}
|
|
309
393
|
}
|
|
310
|
-
} else if (content[i] === "#") {
|
|
394
|
+
} else if (prefixLen === 0 && content[i] === "#") {
|
|
311
395
|
while (i < content.length && content[i] !== "\n") {
|
|
312
396
|
result += " ";
|
|
313
397
|
i++;
|
|
314
398
|
}
|
|
315
|
-
} else if (content[i] === '"' || content[i] === "'") {
|
|
316
|
-
const quote = content[
|
|
317
|
-
|
|
318
|
-
|
|
399
|
+
} else if (quoteStart < content.length && (content[quoteStart] === '"' || content[quoteStart] === "'") && (prefixLen > 0 || content[i] === '"' || content[i] === "'")) {
|
|
400
|
+
const quote = content[quoteStart];
|
|
401
|
+
for (let k = i; k < quoteStart; k++) {
|
|
402
|
+
result += content[k];
|
|
403
|
+
}
|
|
404
|
+
result += content[quoteStart];
|
|
405
|
+
i = quoteStart + 1;
|
|
319
406
|
while (i < content.length && content[i] !== quote) {
|
|
320
|
-
if (content[i] === "\\" && i + 1 < content.length) {
|
|
407
|
+
if (!isRaw && content[i] === "\\" && i + 1 < content.length) {
|
|
321
408
|
result += content[i++];
|
|
322
409
|
result += content[i++];
|
|
323
410
|
} else if (content[i] === "\n") {
|
|
@@ -344,13 +431,13 @@ function stripRuby(content) {
|
|
|
344
431
|
const result = [];
|
|
345
432
|
let inBlock = false;
|
|
346
433
|
for (const line of lines) {
|
|
347
|
-
if (!inBlock &&
|
|
434
|
+
if (!inBlock && /^=begin(\s|$)/.test(line)) {
|
|
348
435
|
inBlock = true;
|
|
349
436
|
result.push(" ".repeat(line.length));
|
|
350
437
|
continue;
|
|
351
438
|
}
|
|
352
439
|
if (inBlock) {
|
|
353
|
-
if (
|
|
440
|
+
if (/^=end(\s|$)/.test(line)) {
|
|
354
441
|
inBlock = false;
|
|
355
442
|
}
|
|
356
443
|
result.push(" ".repeat(line.length));
|
|
@@ -365,18 +452,8 @@ function stripRuby(content) {
|
|
|
365
452
|
} else if (line[i] === '"') {
|
|
366
453
|
processed += line[i];
|
|
367
454
|
i++;
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
processed += line[i++];
|
|
371
|
-
processed += line[i++];
|
|
372
|
-
} else {
|
|
373
|
-
processed += line[i++];
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
if (i < line.length) {
|
|
377
|
-
processed += line[i];
|
|
378
|
-
i++;
|
|
379
|
-
}
|
|
455
|
+
processed = scanRubyDoubleQuotedString(line, i, processed);
|
|
456
|
+
i = scanRubyDoubleQuotedStringIndex(line, i);
|
|
380
457
|
} else if (line[i] === "'") {
|
|
381
458
|
processed += line[i];
|
|
382
459
|
i++;
|
|
@@ -401,10 +478,141 @@ function stripRuby(content) {
|
|
|
401
478
|
}
|
|
402
479
|
return result.join("\n");
|
|
403
480
|
}
|
|
481
|
+
function scanRubyDoubleQuotedString(line, startI, processed) {
|
|
482
|
+
let i = startI;
|
|
483
|
+
while (i < line.length && line[i] !== '"') {
|
|
484
|
+
if (line[i] === "\\" && i + 1 < line.length) {
|
|
485
|
+
processed += line[i++];
|
|
486
|
+
processed += line[i++];
|
|
487
|
+
} else if (line[i] === "#" && i + 1 < line.length && line[i + 1] === "{") {
|
|
488
|
+
processed += line[i++];
|
|
489
|
+
processed += line[i++];
|
|
490
|
+
let depth = 1;
|
|
491
|
+
while (i < line.length && depth > 0) {
|
|
492
|
+
if (line[i] === "{") {
|
|
493
|
+
depth++;
|
|
494
|
+
processed += line[i++];
|
|
495
|
+
} else if (line[i] === "}") {
|
|
496
|
+
depth--;
|
|
497
|
+
processed += line[i++];
|
|
498
|
+
} else if (line[i] === "\\" && i + 1 < line.length) {
|
|
499
|
+
processed += line[i++];
|
|
500
|
+
processed += line[i++];
|
|
501
|
+
} else {
|
|
502
|
+
processed += line[i++];
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
} else {
|
|
506
|
+
processed += line[i++];
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
if (i < line.length) {
|
|
510
|
+
processed += line[i];
|
|
511
|
+
}
|
|
512
|
+
return processed;
|
|
513
|
+
}
|
|
514
|
+
function scanRubyDoubleQuotedStringIndex(line, startI) {
|
|
515
|
+
let i = startI;
|
|
516
|
+
while (i < line.length && line[i] !== '"') {
|
|
517
|
+
if (line[i] === "\\" && i + 1 < line.length) {
|
|
518
|
+
i += 2;
|
|
519
|
+
} else if (line[i] === "#" && i + 1 < line.length && line[i + 1] === "{") {
|
|
520
|
+
i += 2;
|
|
521
|
+
let depth = 1;
|
|
522
|
+
while (i < line.length && depth > 0) {
|
|
523
|
+
if (line[i] === "{") {
|
|
524
|
+
depth++;
|
|
525
|
+
i++;
|
|
526
|
+
} else if (line[i] === "}") {
|
|
527
|
+
depth--;
|
|
528
|
+
i++;
|
|
529
|
+
} else if (line[i] === "\\" && i + 1 < line.length) {
|
|
530
|
+
i += 2;
|
|
531
|
+
} else {
|
|
532
|
+
i++;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
} else {
|
|
536
|
+
i++;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (i < line.length) {
|
|
540
|
+
i++;
|
|
541
|
+
}
|
|
542
|
+
return i;
|
|
543
|
+
}
|
|
404
544
|
function stripPhp(content) {
|
|
405
545
|
let result = "";
|
|
406
546
|
let i = 0;
|
|
407
547
|
while (i < content.length) {
|
|
548
|
+
if (content[i] === "<" && content[i + 1] === "<" && content[i + 2] === "<") {
|
|
549
|
+
const heredocStart = i;
|
|
550
|
+
let j = i + 3;
|
|
551
|
+
let isNowdoc = false;
|
|
552
|
+
if (j < content.length && content[j] === "'") {
|
|
553
|
+
isNowdoc = true;
|
|
554
|
+
j++;
|
|
555
|
+
}
|
|
556
|
+
const identStart = j;
|
|
557
|
+
while (j < content.length && /[A-Za-z0-9_]/.test(content[j])) {
|
|
558
|
+
j++;
|
|
559
|
+
}
|
|
560
|
+
const identifier = content.slice(identStart, j);
|
|
561
|
+
if (identifier.length > 0) {
|
|
562
|
+
if (isNowdoc && j < content.length && content[j] === "'") {
|
|
563
|
+
j++;
|
|
564
|
+
}
|
|
565
|
+
let validHeredoc = false;
|
|
566
|
+
let lineEnd = j;
|
|
567
|
+
if (lineEnd < content.length && content[lineEnd] === "\n") {
|
|
568
|
+
validHeredoc = true;
|
|
569
|
+
}
|
|
570
|
+
if (validHeredoc) {
|
|
571
|
+
for (let k = heredocStart; k <= lineEnd; k++) {
|
|
572
|
+
if (content[k] === "\n") {
|
|
573
|
+
result += "\n";
|
|
574
|
+
} else {
|
|
575
|
+
result += content[k];
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
i = lineEnd + 1;
|
|
579
|
+
let found = false;
|
|
580
|
+
while (i < content.length && !found) {
|
|
581
|
+
const lineStart = i;
|
|
582
|
+
let lineEndIdx = i;
|
|
583
|
+
while (lineEndIdx < content.length && content[lineEndIdx] !== "\n") {
|
|
584
|
+
lineEndIdx++;
|
|
585
|
+
}
|
|
586
|
+
const currentLine = content.slice(lineStart, lineEndIdx);
|
|
587
|
+
const trimmedLine = currentLine.trimStart();
|
|
588
|
+
if (trimmedLine === identifier || trimmedLine === identifier + ";" || trimmedLine === identifier + "," || trimmedLine === identifier + ");" || trimmedLine === identifier + ")" || trimmedLine.startsWith(identifier + ";") || trimmedLine === identifier) {
|
|
589
|
+
for (let k = lineStart; k < lineEndIdx; k++) {
|
|
590
|
+
result += content[k];
|
|
591
|
+
}
|
|
592
|
+
i = lineEndIdx;
|
|
593
|
+
if (i < content.length && content[i] === "\n") {
|
|
594
|
+
result += "\n";
|
|
595
|
+
i++;
|
|
596
|
+
}
|
|
597
|
+
found = true;
|
|
598
|
+
} else {
|
|
599
|
+
for (let k = lineStart; k < lineEndIdx; k++) {
|
|
600
|
+
result += " ";
|
|
601
|
+
}
|
|
602
|
+
i = lineEndIdx;
|
|
603
|
+
if (i < content.length && content[i] === "\n") {
|
|
604
|
+
result += "\n";
|
|
605
|
+
i++;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
continue;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
result += content[i];
|
|
613
|
+
i++;
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
408
616
|
if (content[i] === "/" && content[i + 1] === "/" || content[i] === "#") {
|
|
409
617
|
while (i < content.length && content[i] !== "\n") {
|
|
410
618
|
result += " ";
|
|
@@ -477,6 +685,7 @@ var RegexEngine = class {
|
|
|
477
685
|
"\\.archtracker"
|
|
478
686
|
].map((p) => new RegExp(p));
|
|
479
687
|
const projectFiles = await this.collectFiles(
|
|
688
|
+
absRootDir,
|
|
480
689
|
absRootDir,
|
|
481
690
|
excludePatterns,
|
|
482
691
|
options.maxDepth ?? 0
|
|
@@ -500,6 +709,7 @@ var RegexEngine = class {
|
|
|
500
709
|
try {
|
|
501
710
|
content = await readFile(filePath, "utf-8");
|
|
502
711
|
} catch {
|
|
712
|
+
if (files[relSource]) files[relSource].exists = false;
|
|
503
713
|
continue;
|
|
504
714
|
}
|
|
505
715
|
const stripped = stripComments(content, this.config.commentStyle);
|
|
@@ -543,7 +753,8 @@ var RegexEngine = class {
|
|
|
543
753
|
}
|
|
544
754
|
const imports = [];
|
|
545
755
|
for (const pattern of this.config.importPatterns) {
|
|
546
|
-
const
|
|
756
|
+
const flags = pattern.regex.flags.includes("g") ? pattern.regex.flags : pattern.regex.flags + "g";
|
|
757
|
+
const regex = new RegExp(pattern.regex.source, flags);
|
|
547
758
|
let match;
|
|
548
759
|
while ((match = regex.exec(content)) !== null) {
|
|
549
760
|
if (match[1]) {
|
|
@@ -553,7 +764,7 @@ var RegexEngine = class {
|
|
|
553
764
|
}
|
|
554
765
|
return imports;
|
|
555
766
|
}
|
|
556
|
-
async collectFiles(dir, excludePatterns, maxDepth, currentDepth = 0) {
|
|
767
|
+
async collectFiles(dir, absRootDir, excludePatterns, maxDepth, currentDepth = 0) {
|
|
557
768
|
if (maxDepth > 0 && currentDepth >= maxDepth) return [];
|
|
558
769
|
const results = [];
|
|
559
770
|
let entries;
|
|
@@ -564,7 +775,7 @@ var RegexEngine = class {
|
|
|
564
775
|
}
|
|
565
776
|
for (const entry of entries) {
|
|
566
777
|
const fullPath = join(dir, entry.name);
|
|
567
|
-
const relPath = relative(
|
|
778
|
+
const relPath = relative(absRootDir, fullPath);
|
|
568
779
|
if (excludePatterns.some(
|
|
569
780
|
(p) => p.test(entry.name) || p.test(relPath) || p.test(fullPath)
|
|
570
781
|
)) {
|
|
@@ -574,6 +785,7 @@ var RegexEngine = class {
|
|
|
574
785
|
if (entry.name.startsWith(".")) continue;
|
|
575
786
|
const sub = await this.collectFiles(
|
|
576
787
|
fullPath,
|
|
788
|
+
absRootDir,
|
|
577
789
|
excludePatterns,
|
|
578
790
|
maxDepth,
|
|
579
791
|
currentDepth + 1
|
|
@@ -606,14 +818,21 @@ var MARKERS = [
|
|
|
606
818
|
{ file: "pom.xml", language: "java" },
|
|
607
819
|
{ file: "build.gradle", language: "java" },
|
|
608
820
|
{ file: "build.gradle.kts", language: "kotlin" },
|
|
821
|
+
{ file: "build.sbt", language: "scala" },
|
|
822
|
+
{ file: "build.sc", language: "scala" },
|
|
609
823
|
{ file: "Package.swift", language: "swift" },
|
|
610
824
|
{ file: "Gemfile", language: "ruby" },
|
|
611
825
|
{ file: "composer.json", language: "php" },
|
|
826
|
+
{ file: "pubspec.yaml", language: "dart" },
|
|
612
827
|
{ file: "CMakeLists.txt", language: "c-cpp" },
|
|
613
828
|
{ file: "Makefile", language: "c-cpp" },
|
|
614
829
|
{ file: "package.json", language: "javascript" },
|
|
615
830
|
{ file: "tsconfig.json", language: "javascript" }
|
|
616
831
|
];
|
|
832
|
+
var EXT_MARKERS = [
|
|
833
|
+
[".sln", "c-sharp"],
|
|
834
|
+
[".csproj", "c-sharp"]
|
|
835
|
+
];
|
|
617
836
|
var EXT_MAP = {
|
|
618
837
|
".ts": "javascript",
|
|
619
838
|
".tsx": "javascript",
|
|
@@ -635,7 +854,11 @@ var EXT_MAP = {
|
|
|
635
854
|
".php": "php",
|
|
636
855
|
".swift": "swift",
|
|
637
856
|
".kt": "kotlin",
|
|
638
|
-
".kts": "kotlin"
|
|
857
|
+
".kts": "kotlin",
|
|
858
|
+
".cs": "c-sharp",
|
|
859
|
+
".dart": "dart",
|
|
860
|
+
".scala": "scala",
|
|
861
|
+
".sc": "scala"
|
|
639
862
|
};
|
|
640
863
|
async function detectLanguage(rootDir) {
|
|
641
864
|
for (const marker of MARKERS) {
|
|
@@ -647,6 +870,16 @@ async function detectLanguage(rootDir) {
|
|
|
647
870
|
} catch {
|
|
648
871
|
}
|
|
649
872
|
}
|
|
873
|
+
try {
|
|
874
|
+
const topEntries = await readdir2(rootDir, { withFileTypes: true });
|
|
875
|
+
for (const entry of topEntries) {
|
|
876
|
+
if (!entry.isFile()) continue;
|
|
877
|
+
for (const [ext, lang] of EXT_MARKERS) {
|
|
878
|
+
if (entry.name.endsWith(ext)) return lang;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
} catch {
|
|
882
|
+
}
|
|
650
883
|
const counts = /* @__PURE__ */ new Map();
|
|
651
884
|
try {
|
|
652
885
|
await scanExtensions(rootDir, counts, 2, 0);
|
|
@@ -699,10 +932,27 @@ var python = {
|
|
|
699
932
|
commentStyle: "python",
|
|
700
933
|
importPatterns: [
|
|
701
934
|
// from package.module import something
|
|
702
|
-
{ regex: /^from\s+(\.[\w.]*|\w[\w.]*)\s+import\b/gm }
|
|
703
|
-
// import package.module
|
|
704
|
-
{ regex: /^import\s+([\w.]+)/gm }
|
|
935
|
+
{ regex: /^from\s+(\.[\w.]*|\w[\w.]*)\s+import\b/gm }
|
|
936
|
+
// import package.module (handled by extractImports for multi-module case)
|
|
705
937
|
],
|
|
938
|
+
// Bug #1 fix: custom extractImports to handle `import a, b, c`
|
|
939
|
+
extractImports(content) {
|
|
940
|
+
const imports = [];
|
|
941
|
+
const fromRegex = /^from\s+(\.[\w.]*|\w[\w.]*)\s+import\b/gm;
|
|
942
|
+
let match;
|
|
943
|
+
while ((match = fromRegex.exec(content)) !== null) {
|
|
944
|
+
imports.push(match[1]);
|
|
945
|
+
}
|
|
946
|
+
const importRegex = /^import\s+([\w.]+(?:\s*,\s*[\w.]+)*)/gm;
|
|
947
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
948
|
+
const modules = match[1].split(",");
|
|
949
|
+
for (const mod of modules) {
|
|
950
|
+
const trimmed = mod.trim();
|
|
951
|
+
if (trimmed) imports.push(trimmed);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
return imports;
|
|
955
|
+
},
|
|
706
956
|
resolveImport(importPath, sourceFile, rootDir, projectFiles) {
|
|
707
957
|
if (importPath.startsWith(".")) {
|
|
708
958
|
const dots = importPath.match(/^\.+/)?.[0].length ?? 1;
|
|
@@ -739,6 +989,16 @@ var rust = {
|
|
|
739
989
|
const body = match[1].trim();
|
|
740
990
|
extractRustUsePaths(body, "", imports);
|
|
741
991
|
}
|
|
992
|
+
const useSuperRegex = /\buse\s+super::([\s\S]*?);/gm;
|
|
993
|
+
while ((match = useSuperRegex.exec(content)) !== null) {
|
|
994
|
+
const body = match[1].trim();
|
|
995
|
+
extractRustUsePaths(body, "", imports, "super");
|
|
996
|
+
}
|
|
997
|
+
const useSelfRegex = /\buse\s+self::([\s\S]*?);/gm;
|
|
998
|
+
while ((match = useSelfRegex.exec(content)) !== null) {
|
|
999
|
+
const body = match[1].trim();
|
|
1000
|
+
extractRustUsePaths(body, "", imports, "self");
|
|
1001
|
+
}
|
|
742
1002
|
return imports;
|
|
743
1003
|
},
|
|
744
1004
|
resolveImport(importPath, sourceFile, rootDir, projectFiles) {
|
|
@@ -751,6 +1011,30 @@ var rust = {
|
|
|
751
1011
|
if (projectFiles.has(asDir)) return asDir;
|
|
752
1012
|
return null;
|
|
753
1013
|
}
|
|
1014
|
+
if (importPath.startsWith("super::")) {
|
|
1015
|
+
const parentDir = dirname(dirname(sourceFile));
|
|
1016
|
+
const segments2 = importPath.slice("super::".length).split("::");
|
|
1017
|
+
for (let i = segments2.length; i > 0; i--) {
|
|
1018
|
+
const path = segments2.slice(0, i).join("/");
|
|
1019
|
+
const asFile = join3(parentDir, path + ".rs");
|
|
1020
|
+
if (projectFiles.has(asFile)) return asFile;
|
|
1021
|
+
const asDir = join3(parentDir, path, "mod.rs");
|
|
1022
|
+
if (projectFiles.has(asDir)) return asDir;
|
|
1023
|
+
}
|
|
1024
|
+
return null;
|
|
1025
|
+
}
|
|
1026
|
+
if (importPath.startsWith("self::")) {
|
|
1027
|
+
const selfDir = dirname(sourceFile);
|
|
1028
|
+
const segments2 = importPath.slice("self::".length).split("::");
|
|
1029
|
+
for (let i = segments2.length; i > 0; i--) {
|
|
1030
|
+
const path = segments2.slice(0, i).join("/");
|
|
1031
|
+
const asFile = join3(selfDir, path + ".rs");
|
|
1032
|
+
if (projectFiles.has(asFile)) return asFile;
|
|
1033
|
+
const asDir = join3(selfDir, path, "mod.rs");
|
|
1034
|
+
if (projectFiles.has(asDir)) return asDir;
|
|
1035
|
+
}
|
|
1036
|
+
return null;
|
|
1037
|
+
}
|
|
754
1038
|
const segments = importPath.split("::");
|
|
755
1039
|
for (let i = segments.length; i > 0; i--) {
|
|
756
1040
|
const path = segments.slice(0, i).join("/");
|
|
@@ -763,11 +1047,14 @@ var rust = {
|
|
|
763
1047
|
},
|
|
764
1048
|
defaultExclude: ["target"]
|
|
765
1049
|
};
|
|
766
|
-
function extractRustUsePaths(body, prefix, results) {
|
|
1050
|
+
function extractRustUsePaths(body, prefix, results, rootPrefix) {
|
|
767
1051
|
const trimmed = body.trim();
|
|
768
1052
|
const braceStart = trimmed.indexOf("{");
|
|
769
1053
|
if (braceStart === -1) {
|
|
770
|
-
|
|
1054
|
+
let path = prefix ? `${prefix}::${trimmed}` : trimmed;
|
|
1055
|
+
if (rootPrefix) {
|
|
1056
|
+
path = `${rootPrefix}::${path}`;
|
|
1057
|
+
}
|
|
771
1058
|
if (path && !path.includes("{")) {
|
|
772
1059
|
results.push(path);
|
|
773
1060
|
}
|
|
@@ -796,9 +1083,10 @@ function extractRustUsePaths(body, prefix, results) {
|
|
|
796
1083
|
for (const item of items) {
|
|
797
1084
|
const cleaned = item.trim();
|
|
798
1085
|
if (cleaned === "self") {
|
|
799
|
-
|
|
1086
|
+
const selfPath = rootPrefix ? `${rootPrefix}::${fullPrefix}` : fullPrefix;
|
|
1087
|
+
results.push(selfPath);
|
|
800
1088
|
} else if (cleaned) {
|
|
801
|
-
extractRustUsePaths(cleaned, fullPrefix, results);
|
|
1089
|
+
extractRustUsePaths(cleaned, fullPrefix, results, rootPrefix);
|
|
802
1090
|
}
|
|
803
1091
|
}
|
|
804
1092
|
}
|
|
@@ -872,14 +1160,21 @@ var java = {
|
|
|
872
1160
|
extensions: [".java"],
|
|
873
1161
|
commentStyle: "c-style",
|
|
874
1162
|
importPatterns: [
|
|
875
|
-
// import com.example.ClassName;
|
|
876
|
-
|
|
1163
|
+
// Bug #5 fix: import com.example.ClassName; and import com.example.*; (wildcard)
|
|
1164
|
+
// Bug #6: static imports also captured here
|
|
1165
|
+
{ regex: /^import\s+(?:static\s+)?([\w.*]+);/gm }
|
|
877
1166
|
],
|
|
878
1167
|
resolveImport(importPath, _sourceFile, rootDir, projectFiles) {
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
1168
|
+
if (importPath.endsWith(".*")) {
|
|
1169
|
+
return null;
|
|
1170
|
+
}
|
|
1171
|
+
const segments = importPath.split(".");
|
|
1172
|
+
for (let i = segments.length; i > 0; i--) {
|
|
1173
|
+
const filePath = segments.slice(0, i).join("/") + ".java";
|
|
1174
|
+
for (const srcRoot of ["", "src/main/java/", "src/", "app/src/main/java/"]) {
|
|
1175
|
+
const full = join3(rootDir, srcRoot, filePath);
|
|
1176
|
+
if (projectFiles.has(full)) return full;
|
|
1177
|
+
}
|
|
883
1178
|
}
|
|
884
1179
|
return null;
|
|
885
1180
|
},
|
|
@@ -935,8 +1230,8 @@ var php = {
|
|
|
935
1230
|
importPatterns: [
|
|
936
1231
|
// require/include/require_once/include_once 'path'
|
|
937
1232
|
{ regex: /\b(?:require|include)(?:_once)?\s+['"]([^'"]+)['"]/gm },
|
|
938
|
-
// use Namespace\Class
|
|
939
|
-
{ regex: /^use\s+([\w\\]+)/gm }
|
|
1233
|
+
// Bug #9 fix: use Namespace\Class — skip `function` and `const` keywords
|
|
1234
|
+
{ regex: /^use\s+(?:function\s+|const\s+)?([\w\\]+)/gm }
|
|
940
1235
|
],
|
|
941
1236
|
resolveImport(importPath, sourceFile, rootDir, projectFiles) {
|
|
942
1237
|
if (importPath.includes("/") || importPath.endsWith(".php")) {
|
|
@@ -961,8 +1256,8 @@ var swift = {
|
|
|
961
1256
|
extensions: [".swift"],
|
|
962
1257
|
commentStyle: "c-style",
|
|
963
1258
|
importPatterns: [
|
|
964
|
-
// import ModuleName
|
|
965
|
-
{ regex: /^import\s+(?:class\s+|struct\s+|enum\s+|protocol\s+|func\s+|var\s+|let\s+|typealias\s+)?(\w+)/gm }
|
|
1259
|
+
// Bug #10 fix: import ModuleName and @testable import ModuleName
|
|
1260
|
+
{ regex: /^(?:@testable\s+)?import\s+(?:class\s+|struct\s+|enum\s+|protocol\s+|func\s+|var\s+|let\s+|typealias\s+)?(\w+)/gm }
|
|
966
1261
|
],
|
|
967
1262
|
resolveImport(importPath, sourceFile, rootDir, projectFiles) {
|
|
968
1263
|
const spmDir = join3(rootDir, "Sources", importPath);
|
|
@@ -978,11 +1273,18 @@ var kotlin = {
|
|
|
978
1273
|
extensions: [".kt", ".kts"],
|
|
979
1274
|
commentStyle: "c-style",
|
|
980
1275
|
importPatterns: [
|
|
981
|
-
// import com.example.ClassName
|
|
982
|
-
{ regex: /^import\s+([\w
|
|
1276
|
+
// Bug #7/#8 fix: import com.example.ClassName and import com.example.*
|
|
1277
|
+
{ regex: /^import\s+([\w.*]+)/gm }
|
|
983
1278
|
],
|
|
984
1279
|
resolveImport(importPath, _sourceFile, rootDir, projectFiles) {
|
|
985
|
-
|
|
1280
|
+
if (importPath.endsWith(".*")) {
|
|
1281
|
+
return null;
|
|
1282
|
+
}
|
|
1283
|
+
let cleanPath = importPath;
|
|
1284
|
+
if (cleanPath.endsWith(".")) {
|
|
1285
|
+
cleanPath = cleanPath.slice(0, -1);
|
|
1286
|
+
}
|
|
1287
|
+
const filePath = cleanPath.replace(/\./g, "/");
|
|
986
1288
|
for (const ext of [".kt", ".kts"]) {
|
|
987
1289
|
for (const srcRoot of [
|
|
988
1290
|
"",
|
|
@@ -1000,6 +1302,124 @@ var kotlin = {
|
|
|
1000
1302
|
},
|
|
1001
1303
|
defaultExclude: ["build", "\\.gradle", "\\.idea"]
|
|
1002
1304
|
};
|
|
1305
|
+
var cSharp = {
|
|
1306
|
+
id: "c-sharp",
|
|
1307
|
+
extensions: [".cs"],
|
|
1308
|
+
commentStyle: "c-style",
|
|
1309
|
+
importPatterns: [
|
|
1310
|
+
// using Namespace; and using Namespace.SubNamespace;
|
|
1311
|
+
// using static Namespace.Class;
|
|
1312
|
+
// Skip: using Alias = Namespace.Class; (captured but resolved same way)
|
|
1313
|
+
{ regex: /^using\s+(?:static\s+)?([\w.]+)\s*;/gm }
|
|
1314
|
+
],
|
|
1315
|
+
resolveImport(importPath, _sourceFile, rootDir, projectFiles) {
|
|
1316
|
+
const segments = importPath.split(".");
|
|
1317
|
+
for (let i = segments.length; i > 0; i--) {
|
|
1318
|
+
const filePath = segments.slice(0, i).join("/") + ".cs";
|
|
1319
|
+
for (const srcRoot of ["", "src/", "lib/"]) {
|
|
1320
|
+
const full = join3(rootDir, srcRoot, filePath);
|
|
1321
|
+
if (projectFiles.has(full)) return full;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
for (let start = 0; start < segments.length; start++) {
|
|
1325
|
+
const dirPath = segments.slice(start).join("/");
|
|
1326
|
+
for (const srcRoot of ["", "src/", "lib/"]) {
|
|
1327
|
+
const prefix = join3(rootDir, srcRoot, dirPath) + "/";
|
|
1328
|
+
for (const f of projectFiles) {
|
|
1329
|
+
if (f.startsWith(prefix) && f.endsWith(".cs")) return f;
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
return null;
|
|
1334
|
+
},
|
|
1335
|
+
defaultExclude: ["bin", "obj", "\\.vs", "packages", "TestResults"]
|
|
1336
|
+
};
|
|
1337
|
+
var dart = {
|
|
1338
|
+
id: "dart",
|
|
1339
|
+
extensions: [".dart"],
|
|
1340
|
+
commentStyle: "c-style",
|
|
1341
|
+
importPatterns: [
|
|
1342
|
+
// import 'package:pkg/file.dart'; or import 'relative/path.dart';
|
|
1343
|
+
{ regex: /^import\s+['"]([^'"]+)['"]/gm },
|
|
1344
|
+
// export 'file.dart'; (re-exports create dependencies too)
|
|
1345
|
+
{ regex: /^export\s+['"]([^'"]+)['"]/gm }
|
|
1346
|
+
],
|
|
1347
|
+
resolveImport(importPath, sourceFile, rootDir, projectFiles) {
|
|
1348
|
+
if (importPath.startsWith("dart:")) return null;
|
|
1349
|
+
if (importPath.startsWith("package:")) {
|
|
1350
|
+
const ownPackage = dartPackageName(rootDir);
|
|
1351
|
+
if (!ownPackage) return null;
|
|
1352
|
+
const prefix = `package:${ownPackage}/`;
|
|
1353
|
+
if (!importPath.startsWith(prefix)) return null;
|
|
1354
|
+
const relPath = importPath.slice(prefix.length);
|
|
1355
|
+
const full = join3(rootDir, "lib", relPath);
|
|
1356
|
+
if (projectFiles.has(full)) return full;
|
|
1357
|
+
return null;
|
|
1358
|
+
}
|
|
1359
|
+
const resolved = resolve3(dirname(sourceFile), importPath);
|
|
1360
|
+
if (projectFiles.has(resolved)) return resolved;
|
|
1361
|
+
return null;
|
|
1362
|
+
},
|
|
1363
|
+
defaultExclude: ["\\.dart_tool", "build", "\\.packages"]
|
|
1364
|
+
};
|
|
1365
|
+
var dartPackageCache = /* @__PURE__ */ new Map();
|
|
1366
|
+
function dartPackageName(rootDir) {
|
|
1367
|
+
if (dartPackageCache.has(rootDir)) return dartPackageCache.get(rootDir);
|
|
1368
|
+
try {
|
|
1369
|
+
const content = readFileSync(join3(rootDir, "pubspec.yaml"), "utf-8");
|
|
1370
|
+
const match = content.match(/^name:\s*(\S+)/m);
|
|
1371
|
+
const name = match ? match[1] : null;
|
|
1372
|
+
dartPackageCache.set(rootDir, name);
|
|
1373
|
+
return name;
|
|
1374
|
+
} catch {
|
|
1375
|
+
dartPackageCache.set(rootDir, null);
|
|
1376
|
+
return null;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
var scala = {
|
|
1380
|
+
id: "scala",
|
|
1381
|
+
extensions: [".scala", ".sc"],
|
|
1382
|
+
commentStyle: "c-style",
|
|
1383
|
+
importPatterns: [],
|
|
1384
|
+
// handled by extractImports for grouped syntax
|
|
1385
|
+
extractImports(content) {
|
|
1386
|
+
const imports = [];
|
|
1387
|
+
const importRegex = /\bimport\s+([\w.]+(?:\.\{[^}]+\}|\.\w+|\._))/gm;
|
|
1388
|
+
let match;
|
|
1389
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
1390
|
+
const full = match[1];
|
|
1391
|
+
const braceMatch = full.match(/^([\w.]+)\.\{([^}]+)\}$/);
|
|
1392
|
+
if (braceMatch) {
|
|
1393
|
+
const prefix = braceMatch[1];
|
|
1394
|
+
const items = braceMatch[2].split(",");
|
|
1395
|
+
for (const item of items) {
|
|
1396
|
+
const trimmed = item.trim().split(/\s+/)[0];
|
|
1397
|
+
if (trimmed === "_") continue;
|
|
1398
|
+
imports.push(`${prefix}.${trimmed}`);
|
|
1399
|
+
}
|
|
1400
|
+
} else if (full.endsWith("._")) {
|
|
1401
|
+
continue;
|
|
1402
|
+
} else {
|
|
1403
|
+
imports.push(full);
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
return imports;
|
|
1407
|
+
},
|
|
1408
|
+
resolveImport(importPath, _sourceFile, rootDir, projectFiles) {
|
|
1409
|
+
const segments = importPath.split(".");
|
|
1410
|
+
for (let i = segments.length; i > 0; i--) {
|
|
1411
|
+
const filePath = segments.slice(0, i).join("/");
|
|
1412
|
+
for (const ext of [".scala", ".sc"]) {
|
|
1413
|
+
for (const srcRoot of ["", "src/main/scala/", "src/", "app/"]) {
|
|
1414
|
+
const full = join3(rootDir, srcRoot, filePath + ext);
|
|
1415
|
+
if (projectFiles.has(full)) return full;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
return null;
|
|
1420
|
+
},
|
|
1421
|
+
defaultExclude: ["target", "\\.bsp", "\\.metals", "\\.bloop"]
|
|
1422
|
+
};
|
|
1003
1423
|
var LANGUAGE_CONFIGS = {
|
|
1004
1424
|
javascript: null,
|
|
1005
1425
|
// handled by DependencyCruiserEngine
|
|
@@ -1008,10 +1428,13 @@ var LANGUAGE_CONFIGS = {
|
|
|
1008
1428
|
go,
|
|
1009
1429
|
java,
|
|
1010
1430
|
"c-cpp": cCpp,
|
|
1431
|
+
"c-sharp": cSharp,
|
|
1011
1432
|
ruby,
|
|
1012
1433
|
php,
|
|
1013
1434
|
swift,
|
|
1014
|
-
kotlin
|
|
1435
|
+
kotlin,
|
|
1436
|
+
dart,
|
|
1437
|
+
scala
|
|
1015
1438
|
};
|
|
1016
1439
|
function getLanguageConfig(id) {
|
|
1017
1440
|
return LANGUAGE_CONFIGS[id] ?? null;
|