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/cli/index.js
CHANGED
|
@@ -182,12 +182,50 @@ function stripComments(content, style) {
|
|
|
182
182
|
return stripRuby(content);
|
|
183
183
|
case "php":
|
|
184
184
|
return stripPhp(content);
|
|
185
|
+
default: {
|
|
186
|
+
const _exhaustive = style;
|
|
187
|
+
throw new Error(`Unknown comment style: ${_exhaustive}`);
|
|
188
|
+
}
|
|
185
189
|
}
|
|
186
190
|
}
|
|
187
191
|
function stripCStyle(content) {
|
|
188
192
|
let result = "";
|
|
189
193
|
let i = 0;
|
|
190
194
|
while (i < content.length) {
|
|
195
|
+
if (content[i] === "r" && i + 1 < content.length) {
|
|
196
|
+
let hashes = 0;
|
|
197
|
+
let j = i + 1;
|
|
198
|
+
while (j < content.length && content[j] === "#") {
|
|
199
|
+
hashes++;
|
|
200
|
+
j++;
|
|
201
|
+
}
|
|
202
|
+
if (j < content.length && content[j] === '"') {
|
|
203
|
+
for (let k = i; k <= j; k++) {
|
|
204
|
+
result += " ";
|
|
205
|
+
}
|
|
206
|
+
i = j + 1;
|
|
207
|
+
while (i < content.length) {
|
|
208
|
+
if (content[i] === '"') {
|
|
209
|
+
let matchHashes = 0;
|
|
210
|
+
let m = i + 1;
|
|
211
|
+
while (m < content.length && content[m] === "#" && matchHashes < hashes) {
|
|
212
|
+
matchHashes++;
|
|
213
|
+
m++;
|
|
214
|
+
}
|
|
215
|
+
if (matchHashes === hashes) {
|
|
216
|
+
for (let k = i; k < m; k++) {
|
|
217
|
+
result += " ";
|
|
218
|
+
}
|
|
219
|
+
i = m;
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
result += content[i] === "\n" ? "\n" : " ";
|
|
224
|
+
i++;
|
|
225
|
+
}
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
191
229
|
if (content[i] === "/" && content[i + 1] === "/") {
|
|
192
230
|
while (i < content.length && content[i] !== "\n") {
|
|
193
231
|
result += " ";
|
|
@@ -230,6 +268,24 @@ function stripCStyle(content) {
|
|
|
230
268
|
result += content[i];
|
|
231
269
|
i++;
|
|
232
270
|
}
|
|
271
|
+
} else if (content[i] === "'") {
|
|
272
|
+
result += content[i];
|
|
273
|
+
i++;
|
|
274
|
+
while (i < content.length && content[i] !== "'") {
|
|
275
|
+
if (content[i] === "\\" && i + 1 < content.length) {
|
|
276
|
+
result += content[i];
|
|
277
|
+
i++;
|
|
278
|
+
result += content[i];
|
|
279
|
+
i++;
|
|
280
|
+
} else {
|
|
281
|
+
result += content[i];
|
|
282
|
+
i++;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (i < content.length) {
|
|
286
|
+
result += content[i];
|
|
287
|
+
i++;
|
|
288
|
+
}
|
|
233
289
|
} else if (content[i] === "`") {
|
|
234
290
|
result += " ";
|
|
235
291
|
i++;
|
|
@@ -298,31 +354,62 @@ function stripPython(content) {
|
|
|
298
354
|
let result = "";
|
|
299
355
|
let i = 0;
|
|
300
356
|
while (i < content.length) {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
357
|
+
let prefixLen = 0;
|
|
358
|
+
let isRaw = false;
|
|
359
|
+
if (i < content.length) {
|
|
360
|
+
const c0 = content[i];
|
|
361
|
+
const c1 = i + 1 < content.length ? content[i + 1] : "";
|
|
362
|
+
const c2 = i + 2 < content.length ? content[i + 2] : "";
|
|
363
|
+
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 === "'")) {
|
|
364
|
+
const pair = (c0 + c1).toLowerCase();
|
|
365
|
+
if (pair === "rb" || pair === "br" || pair === "fr" || pair === "rf") {
|
|
366
|
+
prefixLen = 2;
|
|
367
|
+
isRaw = pair.includes("r");
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (prefixLen === 0 && (c0 === "r" || c0 === "R" || c0 === "b" || c0 === "B" || c0 === "f" || c0 === "F") && (c1 === '"' || c1 === "'")) {
|
|
371
|
+
prefixLen = 1;
|
|
372
|
+
isRaw = c0 === "r" || c0 === "R";
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
const quoteStart = i + prefixLen;
|
|
376
|
+
if (quoteStart + 2 < content.length && (content[quoteStart] === '"' || content[quoteStart] === "'") && content[quoteStart + 1] === content[quoteStart] && content[quoteStart + 2] === content[quoteStart]) {
|
|
377
|
+
const quote = content[quoteStart];
|
|
378
|
+
for (let k = 0; k < prefixLen; k++) {
|
|
379
|
+
result += " ";
|
|
380
|
+
}
|
|
304
381
|
result += " ";
|
|
305
|
-
i
|
|
382
|
+
i = quoteStart + 3;
|
|
306
383
|
while (i < content.length) {
|
|
307
384
|
if (content[i] === quote && content[i + 1] === quote && content[i + 2] === quote) {
|
|
308
385
|
result += " ";
|
|
309
386
|
i += 3;
|
|
310
387
|
break;
|
|
311
388
|
}
|
|
312
|
-
|
|
313
|
-
|
|
389
|
+
if (!isRaw && content[i] === "\\" && i + 1 < content.length) {
|
|
390
|
+
result += " ";
|
|
391
|
+
i++;
|
|
392
|
+
result += content[i] === "\n" ? "\n" : " ";
|
|
393
|
+
i++;
|
|
394
|
+
} else {
|
|
395
|
+
result += content[i] === "\n" ? "\n" : " ";
|
|
396
|
+
i++;
|
|
397
|
+
}
|
|
314
398
|
}
|
|
315
|
-
} else if (content[i] === "#") {
|
|
399
|
+
} else if (prefixLen === 0 && content[i] === "#") {
|
|
316
400
|
while (i < content.length && content[i] !== "\n") {
|
|
317
401
|
result += " ";
|
|
318
402
|
i++;
|
|
319
403
|
}
|
|
320
|
-
} else if (content[i] === '"' || content[i] === "'") {
|
|
321
|
-
const quote = content[
|
|
322
|
-
|
|
323
|
-
|
|
404
|
+
} else if (quoteStart < content.length && (content[quoteStart] === '"' || content[quoteStart] === "'") && (prefixLen > 0 || content[i] === '"' || content[i] === "'")) {
|
|
405
|
+
const quote = content[quoteStart];
|
|
406
|
+
for (let k = i; k < quoteStart; k++) {
|
|
407
|
+
result += content[k];
|
|
408
|
+
}
|
|
409
|
+
result += content[quoteStart];
|
|
410
|
+
i = quoteStart + 1;
|
|
324
411
|
while (i < content.length && content[i] !== quote) {
|
|
325
|
-
if (content[i] === "\\" && i + 1 < content.length) {
|
|
412
|
+
if (!isRaw && content[i] === "\\" && i + 1 < content.length) {
|
|
326
413
|
result += content[i++];
|
|
327
414
|
result += content[i++];
|
|
328
415
|
} else if (content[i] === "\n") {
|
|
@@ -349,13 +436,13 @@ function stripRuby(content) {
|
|
|
349
436
|
const result = [];
|
|
350
437
|
let inBlock = false;
|
|
351
438
|
for (const line of lines) {
|
|
352
|
-
if (!inBlock &&
|
|
439
|
+
if (!inBlock && /^=begin(\s|$)/.test(line)) {
|
|
353
440
|
inBlock = true;
|
|
354
441
|
result.push(" ".repeat(line.length));
|
|
355
442
|
continue;
|
|
356
443
|
}
|
|
357
444
|
if (inBlock) {
|
|
358
|
-
if (
|
|
445
|
+
if (/^=end(\s|$)/.test(line)) {
|
|
359
446
|
inBlock = false;
|
|
360
447
|
}
|
|
361
448
|
result.push(" ".repeat(line.length));
|
|
@@ -370,18 +457,8 @@ function stripRuby(content) {
|
|
|
370
457
|
} else if (line[i] === '"') {
|
|
371
458
|
processed += line[i];
|
|
372
459
|
i++;
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
processed += line[i++];
|
|
376
|
-
processed += line[i++];
|
|
377
|
-
} else {
|
|
378
|
-
processed += line[i++];
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
if (i < line.length) {
|
|
382
|
-
processed += line[i];
|
|
383
|
-
i++;
|
|
384
|
-
}
|
|
460
|
+
processed = scanRubyDoubleQuotedString(line, i, processed);
|
|
461
|
+
i = scanRubyDoubleQuotedStringIndex(line, i);
|
|
385
462
|
} else if (line[i] === "'") {
|
|
386
463
|
processed += line[i];
|
|
387
464
|
i++;
|
|
@@ -406,10 +483,141 @@ function stripRuby(content) {
|
|
|
406
483
|
}
|
|
407
484
|
return result.join("\n");
|
|
408
485
|
}
|
|
486
|
+
function scanRubyDoubleQuotedString(line, startI, processed) {
|
|
487
|
+
let i = startI;
|
|
488
|
+
while (i < line.length && line[i] !== '"') {
|
|
489
|
+
if (line[i] === "\\" && i + 1 < line.length) {
|
|
490
|
+
processed += line[i++];
|
|
491
|
+
processed += line[i++];
|
|
492
|
+
} else if (line[i] === "#" && i + 1 < line.length && line[i + 1] === "{") {
|
|
493
|
+
processed += line[i++];
|
|
494
|
+
processed += line[i++];
|
|
495
|
+
let depth = 1;
|
|
496
|
+
while (i < line.length && depth > 0) {
|
|
497
|
+
if (line[i] === "{") {
|
|
498
|
+
depth++;
|
|
499
|
+
processed += line[i++];
|
|
500
|
+
} else if (line[i] === "}") {
|
|
501
|
+
depth--;
|
|
502
|
+
processed += line[i++];
|
|
503
|
+
} else if (line[i] === "\\" && i + 1 < line.length) {
|
|
504
|
+
processed += line[i++];
|
|
505
|
+
processed += line[i++];
|
|
506
|
+
} else {
|
|
507
|
+
processed += line[i++];
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
} else {
|
|
511
|
+
processed += line[i++];
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
if (i < line.length) {
|
|
515
|
+
processed += line[i];
|
|
516
|
+
}
|
|
517
|
+
return processed;
|
|
518
|
+
}
|
|
519
|
+
function scanRubyDoubleQuotedStringIndex(line, startI) {
|
|
520
|
+
let i = startI;
|
|
521
|
+
while (i < line.length && line[i] !== '"') {
|
|
522
|
+
if (line[i] === "\\" && i + 1 < line.length) {
|
|
523
|
+
i += 2;
|
|
524
|
+
} else if (line[i] === "#" && i + 1 < line.length && line[i + 1] === "{") {
|
|
525
|
+
i += 2;
|
|
526
|
+
let depth = 1;
|
|
527
|
+
while (i < line.length && depth > 0) {
|
|
528
|
+
if (line[i] === "{") {
|
|
529
|
+
depth++;
|
|
530
|
+
i++;
|
|
531
|
+
} else if (line[i] === "}") {
|
|
532
|
+
depth--;
|
|
533
|
+
i++;
|
|
534
|
+
} else if (line[i] === "\\" && i + 1 < line.length) {
|
|
535
|
+
i += 2;
|
|
536
|
+
} else {
|
|
537
|
+
i++;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
} else {
|
|
541
|
+
i++;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
if (i < line.length) {
|
|
545
|
+
i++;
|
|
546
|
+
}
|
|
547
|
+
return i;
|
|
548
|
+
}
|
|
409
549
|
function stripPhp(content) {
|
|
410
550
|
let result = "";
|
|
411
551
|
let i = 0;
|
|
412
552
|
while (i < content.length) {
|
|
553
|
+
if (content[i] === "<" && content[i + 1] === "<" && content[i + 2] === "<") {
|
|
554
|
+
const heredocStart = i;
|
|
555
|
+
let j = i + 3;
|
|
556
|
+
let isNowdoc = false;
|
|
557
|
+
if (j < content.length && content[j] === "'") {
|
|
558
|
+
isNowdoc = true;
|
|
559
|
+
j++;
|
|
560
|
+
}
|
|
561
|
+
const identStart = j;
|
|
562
|
+
while (j < content.length && /[A-Za-z0-9_]/.test(content[j])) {
|
|
563
|
+
j++;
|
|
564
|
+
}
|
|
565
|
+
const identifier = content.slice(identStart, j);
|
|
566
|
+
if (identifier.length > 0) {
|
|
567
|
+
if (isNowdoc && j < content.length && content[j] === "'") {
|
|
568
|
+
j++;
|
|
569
|
+
}
|
|
570
|
+
let validHeredoc = false;
|
|
571
|
+
let lineEnd = j;
|
|
572
|
+
if (lineEnd < content.length && content[lineEnd] === "\n") {
|
|
573
|
+
validHeredoc = true;
|
|
574
|
+
}
|
|
575
|
+
if (validHeredoc) {
|
|
576
|
+
for (let k = heredocStart; k <= lineEnd; k++) {
|
|
577
|
+
if (content[k] === "\n") {
|
|
578
|
+
result += "\n";
|
|
579
|
+
} else {
|
|
580
|
+
result += content[k];
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
i = lineEnd + 1;
|
|
584
|
+
let found = false;
|
|
585
|
+
while (i < content.length && !found) {
|
|
586
|
+
const lineStart = i;
|
|
587
|
+
let lineEndIdx = i;
|
|
588
|
+
while (lineEndIdx < content.length && content[lineEndIdx] !== "\n") {
|
|
589
|
+
lineEndIdx++;
|
|
590
|
+
}
|
|
591
|
+
const currentLine = content.slice(lineStart, lineEndIdx);
|
|
592
|
+
const trimmedLine = currentLine.trimStart();
|
|
593
|
+
if (trimmedLine === identifier || trimmedLine === identifier + ";" || trimmedLine === identifier + "," || trimmedLine === identifier + ");" || trimmedLine === identifier + ")" || trimmedLine.startsWith(identifier + ";") || trimmedLine === identifier) {
|
|
594
|
+
for (let k = lineStart; k < lineEndIdx; k++) {
|
|
595
|
+
result += content[k];
|
|
596
|
+
}
|
|
597
|
+
i = lineEndIdx;
|
|
598
|
+
if (i < content.length && content[i] === "\n") {
|
|
599
|
+
result += "\n";
|
|
600
|
+
i++;
|
|
601
|
+
}
|
|
602
|
+
found = true;
|
|
603
|
+
} else {
|
|
604
|
+
for (let k = lineStart; k < lineEndIdx; k++) {
|
|
605
|
+
result += " ";
|
|
606
|
+
}
|
|
607
|
+
i = lineEndIdx;
|
|
608
|
+
if (i < content.length && content[i] === "\n") {
|
|
609
|
+
result += "\n";
|
|
610
|
+
i++;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
result += content[i];
|
|
618
|
+
i++;
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
413
621
|
if (content[i] === "/" && content[i + 1] === "/" || content[i] === "#") {
|
|
414
622
|
while (i < content.length && content[i] !== "\n") {
|
|
415
623
|
result += " ";
|
|
@@ -482,6 +690,7 @@ var RegexEngine = class {
|
|
|
482
690
|
"\\.archtracker"
|
|
483
691
|
].map((p) => new RegExp(p));
|
|
484
692
|
const projectFiles = await this.collectFiles(
|
|
693
|
+
absRootDir,
|
|
485
694
|
absRootDir,
|
|
486
695
|
excludePatterns,
|
|
487
696
|
options.maxDepth ?? 0
|
|
@@ -505,6 +714,7 @@ var RegexEngine = class {
|
|
|
505
714
|
try {
|
|
506
715
|
content = await readFile(filePath, "utf-8");
|
|
507
716
|
} catch {
|
|
717
|
+
if (files[relSource]) files[relSource].exists = false;
|
|
508
718
|
continue;
|
|
509
719
|
}
|
|
510
720
|
const stripped = stripComments(content, this.config.commentStyle);
|
|
@@ -548,7 +758,8 @@ var RegexEngine = class {
|
|
|
548
758
|
}
|
|
549
759
|
const imports = [];
|
|
550
760
|
for (const pattern of this.config.importPatterns) {
|
|
551
|
-
const
|
|
761
|
+
const flags = pattern.regex.flags.includes("g") ? pattern.regex.flags : pattern.regex.flags + "g";
|
|
762
|
+
const regex = new RegExp(pattern.regex.source, flags);
|
|
552
763
|
let match;
|
|
553
764
|
while ((match = regex.exec(content)) !== null) {
|
|
554
765
|
if (match[1]) {
|
|
@@ -558,7 +769,7 @@ var RegexEngine = class {
|
|
|
558
769
|
}
|
|
559
770
|
return imports;
|
|
560
771
|
}
|
|
561
|
-
async collectFiles(dir, excludePatterns, maxDepth, currentDepth = 0) {
|
|
772
|
+
async collectFiles(dir, absRootDir, excludePatterns, maxDepth, currentDepth = 0) {
|
|
562
773
|
if (maxDepth > 0 && currentDepth >= maxDepth) return [];
|
|
563
774
|
const results = [];
|
|
564
775
|
let entries;
|
|
@@ -569,7 +780,7 @@ var RegexEngine = class {
|
|
|
569
780
|
}
|
|
570
781
|
for (const entry of entries) {
|
|
571
782
|
const fullPath = join(dir, entry.name);
|
|
572
|
-
const relPath = relative(
|
|
783
|
+
const relPath = relative(absRootDir, fullPath);
|
|
573
784
|
if (excludePatterns.some(
|
|
574
785
|
(p) => p.test(entry.name) || p.test(relPath) || p.test(fullPath)
|
|
575
786
|
)) {
|
|
@@ -579,6 +790,7 @@ var RegexEngine = class {
|
|
|
579
790
|
if (entry.name.startsWith(".")) continue;
|
|
580
791
|
const sub = await this.collectFiles(
|
|
581
792
|
fullPath,
|
|
793
|
+
absRootDir,
|
|
582
794
|
excludePatterns,
|
|
583
795
|
maxDepth,
|
|
584
796
|
currentDepth + 1
|
|
@@ -611,14 +823,21 @@ var MARKERS = [
|
|
|
611
823
|
{ file: "pom.xml", language: "java" },
|
|
612
824
|
{ file: "build.gradle", language: "java" },
|
|
613
825
|
{ file: "build.gradle.kts", language: "kotlin" },
|
|
826
|
+
{ file: "build.sbt", language: "scala" },
|
|
827
|
+
{ file: "build.sc", language: "scala" },
|
|
614
828
|
{ file: "Package.swift", language: "swift" },
|
|
615
829
|
{ file: "Gemfile", language: "ruby" },
|
|
616
830
|
{ file: "composer.json", language: "php" },
|
|
831
|
+
{ file: "pubspec.yaml", language: "dart" },
|
|
617
832
|
{ file: "CMakeLists.txt", language: "c-cpp" },
|
|
618
833
|
{ file: "Makefile", language: "c-cpp" },
|
|
619
834
|
{ file: "package.json", language: "javascript" },
|
|
620
835
|
{ file: "tsconfig.json", language: "javascript" }
|
|
621
836
|
];
|
|
837
|
+
var EXT_MARKERS = [
|
|
838
|
+
[".sln", "c-sharp"],
|
|
839
|
+
[".csproj", "c-sharp"]
|
|
840
|
+
];
|
|
622
841
|
var EXT_MAP = {
|
|
623
842
|
".ts": "javascript",
|
|
624
843
|
".tsx": "javascript",
|
|
@@ -640,7 +859,11 @@ var EXT_MAP = {
|
|
|
640
859
|
".php": "php",
|
|
641
860
|
".swift": "swift",
|
|
642
861
|
".kt": "kotlin",
|
|
643
|
-
".kts": "kotlin"
|
|
862
|
+
".kts": "kotlin",
|
|
863
|
+
".cs": "c-sharp",
|
|
864
|
+
".dart": "dart",
|
|
865
|
+
".scala": "scala",
|
|
866
|
+
".sc": "scala"
|
|
644
867
|
};
|
|
645
868
|
async function detectLanguage(rootDir) {
|
|
646
869
|
for (const marker of MARKERS) {
|
|
@@ -652,6 +875,16 @@ async function detectLanguage(rootDir) {
|
|
|
652
875
|
} catch {
|
|
653
876
|
}
|
|
654
877
|
}
|
|
878
|
+
try {
|
|
879
|
+
const topEntries = await readdir2(rootDir, { withFileTypes: true });
|
|
880
|
+
for (const entry of topEntries) {
|
|
881
|
+
if (!entry.isFile()) continue;
|
|
882
|
+
for (const [ext, lang] of EXT_MARKERS) {
|
|
883
|
+
if (entry.name.endsWith(ext)) return lang;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
} catch {
|
|
887
|
+
}
|
|
655
888
|
const counts = /* @__PURE__ */ new Map();
|
|
656
889
|
try {
|
|
657
890
|
await scanExtensions(rootDir, counts, 2, 0);
|
|
@@ -704,10 +937,27 @@ var python = {
|
|
|
704
937
|
commentStyle: "python",
|
|
705
938
|
importPatterns: [
|
|
706
939
|
// from package.module import something
|
|
707
|
-
{ regex: /^from\s+(\.[\w.]*|\w[\w.]*)\s+import\b/gm }
|
|
708
|
-
// import package.module
|
|
709
|
-
{ regex: /^import\s+([\w.]+)/gm }
|
|
940
|
+
{ regex: /^from\s+(\.[\w.]*|\w[\w.]*)\s+import\b/gm }
|
|
941
|
+
// import package.module (handled by extractImports for multi-module case)
|
|
710
942
|
],
|
|
943
|
+
// Bug #1 fix: custom extractImports to handle `import a, b, c`
|
|
944
|
+
extractImports(content) {
|
|
945
|
+
const imports = [];
|
|
946
|
+
const fromRegex = /^from\s+(\.[\w.]*|\w[\w.]*)\s+import\b/gm;
|
|
947
|
+
let match;
|
|
948
|
+
while ((match = fromRegex.exec(content)) !== null) {
|
|
949
|
+
imports.push(match[1]);
|
|
950
|
+
}
|
|
951
|
+
const importRegex = /^import\s+([\w.]+(?:\s*,\s*[\w.]+)*)/gm;
|
|
952
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
953
|
+
const modules = match[1].split(",");
|
|
954
|
+
for (const mod of modules) {
|
|
955
|
+
const trimmed = mod.trim();
|
|
956
|
+
if (trimmed) imports.push(trimmed);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
return imports;
|
|
960
|
+
},
|
|
711
961
|
resolveImport(importPath, sourceFile, rootDir, projectFiles) {
|
|
712
962
|
if (importPath.startsWith(".")) {
|
|
713
963
|
const dots = importPath.match(/^\.+/)?.[0].length ?? 1;
|
|
@@ -744,6 +994,16 @@ var rust = {
|
|
|
744
994
|
const body = match[1].trim();
|
|
745
995
|
extractRustUsePaths(body, "", imports);
|
|
746
996
|
}
|
|
997
|
+
const useSuperRegex = /\buse\s+super::([\s\S]*?);/gm;
|
|
998
|
+
while ((match = useSuperRegex.exec(content)) !== null) {
|
|
999
|
+
const body = match[1].trim();
|
|
1000
|
+
extractRustUsePaths(body, "", imports, "super");
|
|
1001
|
+
}
|
|
1002
|
+
const useSelfRegex = /\buse\s+self::([\s\S]*?);/gm;
|
|
1003
|
+
while ((match = useSelfRegex.exec(content)) !== null) {
|
|
1004
|
+
const body = match[1].trim();
|
|
1005
|
+
extractRustUsePaths(body, "", imports, "self");
|
|
1006
|
+
}
|
|
747
1007
|
return imports;
|
|
748
1008
|
},
|
|
749
1009
|
resolveImport(importPath, sourceFile, rootDir, projectFiles) {
|
|
@@ -756,6 +1016,30 @@ var rust = {
|
|
|
756
1016
|
if (projectFiles.has(asDir)) return asDir;
|
|
757
1017
|
return null;
|
|
758
1018
|
}
|
|
1019
|
+
if (importPath.startsWith("super::")) {
|
|
1020
|
+
const parentDir = dirname(dirname(sourceFile));
|
|
1021
|
+
const segments2 = importPath.slice("super::".length).split("::");
|
|
1022
|
+
for (let i = segments2.length; i > 0; i--) {
|
|
1023
|
+
const path = segments2.slice(0, i).join("/");
|
|
1024
|
+
const asFile = join3(parentDir, path + ".rs");
|
|
1025
|
+
if (projectFiles.has(asFile)) return asFile;
|
|
1026
|
+
const asDir = join3(parentDir, path, "mod.rs");
|
|
1027
|
+
if (projectFiles.has(asDir)) return asDir;
|
|
1028
|
+
}
|
|
1029
|
+
return null;
|
|
1030
|
+
}
|
|
1031
|
+
if (importPath.startsWith("self::")) {
|
|
1032
|
+
const selfDir = dirname(sourceFile);
|
|
1033
|
+
const segments2 = importPath.slice("self::".length).split("::");
|
|
1034
|
+
for (let i = segments2.length; i > 0; i--) {
|
|
1035
|
+
const path = segments2.slice(0, i).join("/");
|
|
1036
|
+
const asFile = join3(selfDir, path + ".rs");
|
|
1037
|
+
if (projectFiles.has(asFile)) return asFile;
|
|
1038
|
+
const asDir = join3(selfDir, path, "mod.rs");
|
|
1039
|
+
if (projectFiles.has(asDir)) return asDir;
|
|
1040
|
+
}
|
|
1041
|
+
return null;
|
|
1042
|
+
}
|
|
759
1043
|
const segments = importPath.split("::");
|
|
760
1044
|
for (let i = segments.length; i > 0; i--) {
|
|
761
1045
|
const path = segments.slice(0, i).join("/");
|
|
@@ -768,11 +1052,14 @@ var rust = {
|
|
|
768
1052
|
},
|
|
769
1053
|
defaultExclude: ["target"]
|
|
770
1054
|
};
|
|
771
|
-
function extractRustUsePaths(body, prefix, results) {
|
|
1055
|
+
function extractRustUsePaths(body, prefix, results, rootPrefix) {
|
|
772
1056
|
const trimmed = body.trim();
|
|
773
1057
|
const braceStart = trimmed.indexOf("{");
|
|
774
1058
|
if (braceStart === -1) {
|
|
775
|
-
|
|
1059
|
+
let path = prefix ? `${prefix}::${trimmed}` : trimmed;
|
|
1060
|
+
if (rootPrefix) {
|
|
1061
|
+
path = `${rootPrefix}::${path}`;
|
|
1062
|
+
}
|
|
776
1063
|
if (path && !path.includes("{")) {
|
|
777
1064
|
results.push(path);
|
|
778
1065
|
}
|
|
@@ -801,9 +1088,10 @@ function extractRustUsePaths(body, prefix, results) {
|
|
|
801
1088
|
for (const item of items) {
|
|
802
1089
|
const cleaned = item.trim();
|
|
803
1090
|
if (cleaned === "self") {
|
|
804
|
-
|
|
1091
|
+
const selfPath = rootPrefix ? `${rootPrefix}::${fullPrefix}` : fullPrefix;
|
|
1092
|
+
results.push(selfPath);
|
|
805
1093
|
} else if (cleaned) {
|
|
806
|
-
extractRustUsePaths(cleaned, fullPrefix, results);
|
|
1094
|
+
extractRustUsePaths(cleaned, fullPrefix, results, rootPrefix);
|
|
807
1095
|
}
|
|
808
1096
|
}
|
|
809
1097
|
}
|
|
@@ -877,14 +1165,21 @@ var java = {
|
|
|
877
1165
|
extensions: [".java"],
|
|
878
1166
|
commentStyle: "c-style",
|
|
879
1167
|
importPatterns: [
|
|
880
|
-
// import com.example.ClassName;
|
|
881
|
-
|
|
1168
|
+
// Bug #5 fix: import com.example.ClassName; and import com.example.*; (wildcard)
|
|
1169
|
+
// Bug #6: static imports also captured here
|
|
1170
|
+
{ regex: /^import\s+(?:static\s+)?([\w.*]+);/gm }
|
|
882
1171
|
],
|
|
883
1172
|
resolveImport(importPath, _sourceFile, rootDir, projectFiles) {
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
1173
|
+
if (importPath.endsWith(".*")) {
|
|
1174
|
+
return null;
|
|
1175
|
+
}
|
|
1176
|
+
const segments = importPath.split(".");
|
|
1177
|
+
for (let i = segments.length; i > 0; i--) {
|
|
1178
|
+
const filePath = segments.slice(0, i).join("/") + ".java";
|
|
1179
|
+
for (const srcRoot of ["", "src/main/java/", "src/", "app/src/main/java/"]) {
|
|
1180
|
+
const full = join3(rootDir, srcRoot, filePath);
|
|
1181
|
+
if (projectFiles.has(full)) return full;
|
|
1182
|
+
}
|
|
888
1183
|
}
|
|
889
1184
|
return null;
|
|
890
1185
|
},
|
|
@@ -940,8 +1235,8 @@ var php = {
|
|
|
940
1235
|
importPatterns: [
|
|
941
1236
|
// require/include/require_once/include_once 'path'
|
|
942
1237
|
{ regex: /\b(?:require|include)(?:_once)?\s+['"]([^'"]+)['"]/gm },
|
|
943
|
-
// use Namespace\Class
|
|
944
|
-
{ regex: /^use\s+([\w\\]+)/gm }
|
|
1238
|
+
// Bug #9 fix: use Namespace\Class — skip `function` and `const` keywords
|
|
1239
|
+
{ regex: /^use\s+(?:function\s+|const\s+)?([\w\\]+)/gm }
|
|
945
1240
|
],
|
|
946
1241
|
resolveImport(importPath, sourceFile, rootDir, projectFiles) {
|
|
947
1242
|
if (importPath.includes("/") || importPath.endsWith(".php")) {
|
|
@@ -966,8 +1261,8 @@ var swift = {
|
|
|
966
1261
|
extensions: [".swift"],
|
|
967
1262
|
commentStyle: "c-style",
|
|
968
1263
|
importPatterns: [
|
|
969
|
-
// import ModuleName
|
|
970
|
-
{ regex: /^import\s+(?:class\s+|struct\s+|enum\s+|protocol\s+|func\s+|var\s+|let\s+|typealias\s+)?(\w+)/gm }
|
|
1264
|
+
// Bug #10 fix: import ModuleName and @testable import ModuleName
|
|
1265
|
+
{ regex: /^(?:@testable\s+)?import\s+(?:class\s+|struct\s+|enum\s+|protocol\s+|func\s+|var\s+|let\s+|typealias\s+)?(\w+)/gm }
|
|
971
1266
|
],
|
|
972
1267
|
resolveImport(importPath, sourceFile, rootDir, projectFiles) {
|
|
973
1268
|
const spmDir = join3(rootDir, "Sources", importPath);
|
|
@@ -983,11 +1278,18 @@ var kotlin = {
|
|
|
983
1278
|
extensions: [".kt", ".kts"],
|
|
984
1279
|
commentStyle: "c-style",
|
|
985
1280
|
importPatterns: [
|
|
986
|
-
// import com.example.ClassName
|
|
987
|
-
{ regex: /^import\s+([\w
|
|
1281
|
+
// Bug #7/#8 fix: import com.example.ClassName and import com.example.*
|
|
1282
|
+
{ regex: /^import\s+([\w.*]+)/gm }
|
|
988
1283
|
],
|
|
989
1284
|
resolveImport(importPath, _sourceFile, rootDir, projectFiles) {
|
|
990
|
-
|
|
1285
|
+
if (importPath.endsWith(".*")) {
|
|
1286
|
+
return null;
|
|
1287
|
+
}
|
|
1288
|
+
let cleanPath = importPath;
|
|
1289
|
+
if (cleanPath.endsWith(".")) {
|
|
1290
|
+
cleanPath = cleanPath.slice(0, -1);
|
|
1291
|
+
}
|
|
1292
|
+
const filePath = cleanPath.replace(/\./g, "/");
|
|
991
1293
|
for (const ext of [".kt", ".kts"]) {
|
|
992
1294
|
for (const srcRoot of [
|
|
993
1295
|
"",
|
|
@@ -1005,6 +1307,124 @@ var kotlin = {
|
|
|
1005
1307
|
},
|
|
1006
1308
|
defaultExclude: ["build", "\\.gradle", "\\.idea"]
|
|
1007
1309
|
};
|
|
1310
|
+
var cSharp = {
|
|
1311
|
+
id: "c-sharp",
|
|
1312
|
+
extensions: [".cs"],
|
|
1313
|
+
commentStyle: "c-style",
|
|
1314
|
+
importPatterns: [
|
|
1315
|
+
// using Namespace; and using Namespace.SubNamespace;
|
|
1316
|
+
// using static Namespace.Class;
|
|
1317
|
+
// Skip: using Alias = Namespace.Class; (captured but resolved same way)
|
|
1318
|
+
{ regex: /^using\s+(?:static\s+)?([\w.]+)\s*;/gm }
|
|
1319
|
+
],
|
|
1320
|
+
resolveImport(importPath, _sourceFile, rootDir, projectFiles) {
|
|
1321
|
+
const segments = importPath.split(".");
|
|
1322
|
+
for (let i = segments.length; i > 0; i--) {
|
|
1323
|
+
const filePath = segments.slice(0, i).join("/") + ".cs";
|
|
1324
|
+
for (const srcRoot of ["", "src/", "lib/"]) {
|
|
1325
|
+
const full = join3(rootDir, srcRoot, filePath);
|
|
1326
|
+
if (projectFiles.has(full)) return full;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
for (let start = 0; start < segments.length; start++) {
|
|
1330
|
+
const dirPath = segments.slice(start).join("/");
|
|
1331
|
+
for (const srcRoot of ["", "src/", "lib/"]) {
|
|
1332
|
+
const prefix = join3(rootDir, srcRoot, dirPath) + "/";
|
|
1333
|
+
for (const f of projectFiles) {
|
|
1334
|
+
if (f.startsWith(prefix) && f.endsWith(".cs")) return f;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
return null;
|
|
1339
|
+
},
|
|
1340
|
+
defaultExclude: ["bin", "obj", "\\.vs", "packages", "TestResults"]
|
|
1341
|
+
};
|
|
1342
|
+
var dart = {
|
|
1343
|
+
id: "dart",
|
|
1344
|
+
extensions: [".dart"],
|
|
1345
|
+
commentStyle: "c-style",
|
|
1346
|
+
importPatterns: [
|
|
1347
|
+
// import 'package:pkg/file.dart'; or import 'relative/path.dart';
|
|
1348
|
+
{ regex: /^import\s+['"]([^'"]+)['"]/gm },
|
|
1349
|
+
// export 'file.dart'; (re-exports create dependencies too)
|
|
1350
|
+
{ regex: /^export\s+['"]([^'"]+)['"]/gm }
|
|
1351
|
+
],
|
|
1352
|
+
resolveImport(importPath, sourceFile, rootDir, projectFiles) {
|
|
1353
|
+
if (importPath.startsWith("dart:")) return null;
|
|
1354
|
+
if (importPath.startsWith("package:")) {
|
|
1355
|
+
const ownPackage = dartPackageName(rootDir);
|
|
1356
|
+
if (!ownPackage) return null;
|
|
1357
|
+
const prefix = `package:${ownPackage}/`;
|
|
1358
|
+
if (!importPath.startsWith(prefix)) return null;
|
|
1359
|
+
const relPath = importPath.slice(prefix.length);
|
|
1360
|
+
const full = join3(rootDir, "lib", relPath);
|
|
1361
|
+
if (projectFiles.has(full)) return full;
|
|
1362
|
+
return null;
|
|
1363
|
+
}
|
|
1364
|
+
const resolved = resolve3(dirname(sourceFile), importPath);
|
|
1365
|
+
if (projectFiles.has(resolved)) return resolved;
|
|
1366
|
+
return null;
|
|
1367
|
+
},
|
|
1368
|
+
defaultExclude: ["\\.dart_tool", "build", "\\.packages"]
|
|
1369
|
+
};
|
|
1370
|
+
var dartPackageCache = /* @__PURE__ */ new Map();
|
|
1371
|
+
function dartPackageName(rootDir) {
|
|
1372
|
+
if (dartPackageCache.has(rootDir)) return dartPackageCache.get(rootDir);
|
|
1373
|
+
try {
|
|
1374
|
+
const content = readFileSync(join3(rootDir, "pubspec.yaml"), "utf-8");
|
|
1375
|
+
const match = content.match(/^name:\s*(\S+)/m);
|
|
1376
|
+
const name = match ? match[1] : null;
|
|
1377
|
+
dartPackageCache.set(rootDir, name);
|
|
1378
|
+
return name;
|
|
1379
|
+
} catch {
|
|
1380
|
+
dartPackageCache.set(rootDir, null);
|
|
1381
|
+
return null;
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
var scala = {
|
|
1385
|
+
id: "scala",
|
|
1386
|
+
extensions: [".scala", ".sc"],
|
|
1387
|
+
commentStyle: "c-style",
|
|
1388
|
+
importPatterns: [],
|
|
1389
|
+
// handled by extractImports for grouped syntax
|
|
1390
|
+
extractImports(content) {
|
|
1391
|
+
const imports = [];
|
|
1392
|
+
const importRegex = /\bimport\s+([\w.]+(?:\.\{[^}]+\}|\.\w+|\._))/gm;
|
|
1393
|
+
let match;
|
|
1394
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
1395
|
+
const full = match[1];
|
|
1396
|
+
const braceMatch = full.match(/^([\w.]+)\.\{([^}]+)\}$/);
|
|
1397
|
+
if (braceMatch) {
|
|
1398
|
+
const prefix = braceMatch[1];
|
|
1399
|
+
const items = braceMatch[2].split(",");
|
|
1400
|
+
for (const item of items) {
|
|
1401
|
+
const trimmed = item.trim().split(/\s+/)[0];
|
|
1402
|
+
if (trimmed === "_") continue;
|
|
1403
|
+
imports.push(`${prefix}.${trimmed}`);
|
|
1404
|
+
}
|
|
1405
|
+
} else if (full.endsWith("._")) {
|
|
1406
|
+
continue;
|
|
1407
|
+
} else {
|
|
1408
|
+
imports.push(full);
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
return imports;
|
|
1412
|
+
},
|
|
1413
|
+
resolveImport(importPath, _sourceFile, rootDir, projectFiles) {
|
|
1414
|
+
const segments = importPath.split(".");
|
|
1415
|
+
for (let i = segments.length; i > 0; i--) {
|
|
1416
|
+
const filePath = segments.slice(0, i).join("/");
|
|
1417
|
+
for (const ext of [".scala", ".sc"]) {
|
|
1418
|
+
for (const srcRoot of ["", "src/main/scala/", "src/", "app/"]) {
|
|
1419
|
+
const full = join3(rootDir, srcRoot, filePath + ext);
|
|
1420
|
+
if (projectFiles.has(full)) return full;
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
return null;
|
|
1425
|
+
},
|
|
1426
|
+
defaultExclude: ["target", "\\.bsp", "\\.metals", "\\.bloop"]
|
|
1427
|
+
};
|
|
1008
1428
|
var LANGUAGE_CONFIGS = {
|
|
1009
1429
|
javascript: null,
|
|
1010
1430
|
// handled by DependencyCruiserEngine
|
|
@@ -1013,10 +1433,13 @@ var LANGUAGE_CONFIGS = {
|
|
|
1013
1433
|
go,
|
|
1014
1434
|
java,
|
|
1015
1435
|
"c-cpp": cCpp,
|
|
1436
|
+
"c-sharp": cSharp,
|
|
1016
1437
|
ruby,
|
|
1017
1438
|
php,
|
|
1018
1439
|
swift,
|
|
1019
|
-
kotlin
|
|
1440
|
+
kotlin,
|
|
1441
|
+
dart,
|
|
1442
|
+
scala
|
|
1020
1443
|
};
|
|
1021
1444
|
function getLanguageConfig(id) {
|
|
1022
1445
|
return LANGUAGE_CONFIGS[id] ?? null;
|