claude-crap 0.4.5 → 0.4.7
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/CHANGELOG.md +25 -0
- package/README.md +22 -25
- package/dist/dashboard/file-detail.d.ts +6 -0
- package/dist/dashboard/file-detail.d.ts.map +1 -1
- package/dist/dashboard/file-detail.js +1 -0
- package/dist/dashboard/file-detail.js.map +1 -1
- package/dist/monorepo/project-map.d.ts.map +1 -1
- package/dist/monorepo/project-map.js +135 -6
- package/dist/monorepo/project-map.js.map +1 -1
- package/dist/scanner/bootstrap.d.ts.map +1 -1
- package/dist/scanner/bootstrap.js +2 -2
- package/dist/scanner/bootstrap.js.map +1 -1
- package/dist/shared/exclusions.d.ts.map +1 -1
- package/dist/shared/exclusions.js +22 -0
- package/dist/shared/exclusions.js.map +1 -1
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/bundle/dashboard/public/index.html +216 -7
- package/plugin/bundle/mcp-server.mjs +145 -31
- package/plugin/bundle/mcp-server.mjs.map +3 -3
- package/plugin/hooks/lib/gatekeeper-rules.mjs +274 -45
- package/plugin/hooks/lib/quality-gate.mjs +3 -0
- package/plugin/package-lock.json +8 -8
- package/plugin/package.json +1 -1
- package/src/dashboard/file-detail.ts +7 -0
- package/src/dashboard/public/index.html +216 -7
- package/src/monorepo/project-map.ts +144 -6
- package/src/scanner/bootstrap.ts +7 -2
- package/src/shared/exclusions.ts +26 -0
- package/src/tests/exclusions.test.ts +53 -0
- package/src/tests/file-detail-api.test.ts +38 -0
- package/src/tests/gatekeeper-rules.test.ts +173 -0
- package/src/tests/project-map.test.ts +216 -0
- package/src/tests/workspace-walker.test.ts +94 -0
|
@@ -317,6 +317,95 @@
|
|
|
317
317
|
opacity: 0.7;
|
|
318
318
|
margin-left: 12px;
|
|
319
319
|
}
|
|
320
|
+
/* CC heat bar — ReportGenerator-style visual severity */
|
|
321
|
+
.heat-bar {
|
|
322
|
+
position: relative;
|
|
323
|
+
width: 140px;
|
|
324
|
+
height: 8px;
|
|
325
|
+
background: rgba(255, 255, 255, 0.06);
|
|
326
|
+
border-radius: 4px;
|
|
327
|
+
overflow: hidden;
|
|
328
|
+
}
|
|
329
|
+
.heat-bar-fill {
|
|
330
|
+
height: 100%;
|
|
331
|
+
border-radius: 4px;
|
|
332
|
+
transition: width 120ms ease-out;
|
|
333
|
+
}
|
|
334
|
+
.heat-bar-threshold {
|
|
335
|
+
position: absolute;
|
|
336
|
+
top: -2px;
|
|
337
|
+
bottom: -2px;
|
|
338
|
+
width: 1px;
|
|
339
|
+
background: rgba(255, 255, 255, 0.35);
|
|
340
|
+
}
|
|
341
|
+
/* CC chip — numeric value + % of threshold */
|
|
342
|
+
.cc-chip {
|
|
343
|
+
display: inline-flex;
|
|
344
|
+
align-items: baseline;
|
|
345
|
+
gap: 6px;
|
|
346
|
+
font-variant-numeric: tabular-nums;
|
|
347
|
+
}
|
|
348
|
+
.cc-chip .cc-value {
|
|
349
|
+
font-weight: 700;
|
|
350
|
+
font-size: 13px;
|
|
351
|
+
}
|
|
352
|
+
.cc-chip .cc-ratio {
|
|
353
|
+
font-size: 11px;
|
|
354
|
+
color: var(--muted);
|
|
355
|
+
}
|
|
356
|
+
/* Method name button — scrolls source view to fn start line */
|
|
357
|
+
.method-jump {
|
|
358
|
+
background: none;
|
|
359
|
+
border: none;
|
|
360
|
+
padding: 0;
|
|
361
|
+
font: inherit;
|
|
362
|
+
font-family: "SF Mono", "Fira Code", "Consolas", monospace;
|
|
363
|
+
font-size: 13px;
|
|
364
|
+
color: var(--accent);
|
|
365
|
+
cursor: pointer;
|
|
366
|
+
}
|
|
367
|
+
.method-jump:hover { text-decoration: underline; }
|
|
368
|
+
/* "Open in editor" icon link */
|
|
369
|
+
.editor-link {
|
|
370
|
+
display: inline-flex;
|
|
371
|
+
align-items: center;
|
|
372
|
+
justify-content: center;
|
|
373
|
+
width: 24px;
|
|
374
|
+
height: 24px;
|
|
375
|
+
border-radius: 4px;
|
|
376
|
+
color: var(--muted);
|
|
377
|
+
text-decoration: none;
|
|
378
|
+
font-size: 13px;
|
|
379
|
+
transition: background 120ms, color 120ms;
|
|
380
|
+
}
|
|
381
|
+
.editor-link:hover {
|
|
382
|
+
background: rgba(62, 166, 255, 0.12);
|
|
383
|
+
color: var(--accent);
|
|
384
|
+
text-decoration: none;
|
|
385
|
+
}
|
|
386
|
+
/* "Show all / show fewer" toggle under the methods table */
|
|
387
|
+
.show-all-btn {
|
|
388
|
+
display: inline-block;
|
|
389
|
+
margin: 12px 0 0 0;
|
|
390
|
+
padding: 6px 12px;
|
|
391
|
+
background: transparent;
|
|
392
|
+
border: 1px solid var(--border);
|
|
393
|
+
border-radius: 6px;
|
|
394
|
+
color: var(--accent);
|
|
395
|
+
font-size: 12px;
|
|
396
|
+
cursor: pointer;
|
|
397
|
+
}
|
|
398
|
+
.show-all-btn:hover {
|
|
399
|
+
background: rgba(62, 166, 255, 0.08);
|
|
400
|
+
}
|
|
401
|
+
/* Line-flash animation when the user jumps to a source line */
|
|
402
|
+
.source-line.jump-target {
|
|
403
|
+
animation: jumpFlash 1.2s ease-out;
|
|
404
|
+
}
|
|
405
|
+
@keyframes jumpFlash {
|
|
406
|
+
0% { background: rgba(62, 166, 255, 0.35); }
|
|
407
|
+
100% { background: transparent; }
|
|
408
|
+
}
|
|
320
409
|
</style>
|
|
321
410
|
</head>
|
|
322
411
|
<body>
|
|
@@ -372,33 +461,74 @@
|
|
|
372
461
|
</div>
|
|
373
462
|
</div>
|
|
374
463
|
|
|
375
|
-
<!-- Methods table -->
|
|
376
|
-
<div v-if="fileDetail.functions.length" class="section-title">
|
|
464
|
+
<!-- Methods table — top-5 by CC with heat bar + jump + editor link -->
|
|
465
|
+
<div v-if="fileDetail.functions.length" class="section-title">
|
|
466
|
+
Methods
|
|
467
|
+
<span style="color: var(--muted); font-weight: 400; text-transform: none; letter-spacing: 0; margin-left: 8px;">
|
|
468
|
+
threshold CC {{ fileDetail.cyclomaticMax }}
|
|
469
|
+
</span>
|
|
470
|
+
</div>
|
|
377
471
|
<div v-if="fileDetail.functions.length" class="card">
|
|
378
472
|
<table>
|
|
379
473
|
<thead>
|
|
380
474
|
<tr>
|
|
381
475
|
<th>Method</th>
|
|
382
476
|
<th style="text-align: right">Line</th>
|
|
383
|
-
<th style="
|
|
477
|
+
<th style="width: 180px;">CC</th>
|
|
384
478
|
<th style="text-align: right">Lines</th>
|
|
385
479
|
<th>Status</th>
|
|
480
|
+
<th style="width: 32px;"></th>
|
|
386
481
|
</tr>
|
|
387
482
|
</thead>
|
|
388
483
|
<tbody>
|
|
389
|
-
<tr v-for="fn in
|
|
390
|
-
<td
|
|
484
|
+
<tr v-for="fn in visibleFunctions" :key="fn.startLine">
|
|
485
|
+
<td>
|
|
486
|
+
<button
|
|
487
|
+
class="method-jump"
|
|
488
|
+
@click="jumpToLine(fn.startLine)"
|
|
489
|
+
:title="'Jump to line ' + fn.startLine"
|
|
490
|
+
>{{ fn.name }}</button>
|
|
491
|
+
</td>
|
|
391
492
|
<td style="text-align: right; font-variant-numeric: tabular-nums;">{{ fn.startLine }}</td>
|
|
392
|
-
<td
|
|
493
|
+
<td>
|
|
494
|
+
<div class="cc-chip">
|
|
495
|
+
<div class="heat-bar" :title="ccTooltip(fn)">
|
|
496
|
+
<div
|
|
497
|
+
class="heat-bar-fill"
|
|
498
|
+
:style="heatBarStyle(fn)"
|
|
499
|
+
></div>
|
|
500
|
+
<div
|
|
501
|
+
class="heat-bar-threshold"
|
|
502
|
+
:style="{ left: thresholdMarker() + '%' }"
|
|
503
|
+
></div>
|
|
504
|
+
</div>
|
|
505
|
+
<span class="cc-value" :style="{ color: ccColor(fn) }">{{ fn.cyclomaticComplexity }}</span>
|
|
506
|
+
<span class="cc-ratio">{{ ccRatio(fn) }}%</span>
|
|
507
|
+
</div>
|
|
508
|
+
</td>
|
|
393
509
|
<td style="text-align: right; font-variant-numeric: tabular-nums;">{{ fn.lineCount }}</td>
|
|
394
510
|
<td>
|
|
395
511
|
<span v-if="fn.cyclomaticComplexity >= fileDetail.cyclomaticMax * 2" class="pill pill-error">error</span>
|
|
396
512
|
<span v-else-if="fn.cyclomaticComplexity > fileDetail.cyclomaticMax" class="pill pill-warning">warning</span>
|
|
397
513
|
<span v-else class="pill pill-note">ok</span>
|
|
398
514
|
</td>
|
|
515
|
+
<td>
|
|
516
|
+
<a
|
|
517
|
+
class="editor-link"
|
|
518
|
+
:href="editorLink(fn)"
|
|
519
|
+
:title="'Open ' + fileDetail.filePath + ':' + fn.startLine + ' in VS Code'"
|
|
520
|
+
>↗</a>
|
|
521
|
+
</td>
|
|
399
522
|
</tr>
|
|
400
523
|
</tbody>
|
|
401
524
|
</table>
|
|
525
|
+
<button
|
|
526
|
+
v-if="fileDetail.functions.length > topMethodsLimit"
|
|
527
|
+
class="show-all-btn"
|
|
528
|
+
@click="toggleShowAllMethods()"
|
|
529
|
+
>
|
|
530
|
+
{{ showAllMethods ? 'Show top ' + topMethodsLimit : 'Show all ' + fileDetail.functions.length + ' methods' }}
|
|
531
|
+
</button>
|
|
402
532
|
</div>
|
|
403
533
|
|
|
404
534
|
<!-- Findings table -->
|
|
@@ -622,11 +752,87 @@
|
|
|
622
752
|
});
|
|
623
753
|
|
|
624
754
|
// ── File detail computed ──
|
|
755
|
+
const topMethodsLimit = 5;
|
|
756
|
+
const showAllMethods = ref(false);
|
|
757
|
+
|
|
625
758
|
const sortedFunctions = computed(() => {
|
|
626
759
|
if (!fileDetail.value) return [];
|
|
627
760
|
return [...fileDetail.value.functions].sort((a, b) => b.cyclomaticComplexity - a.cyclomaticComplexity);
|
|
628
761
|
});
|
|
629
762
|
|
|
763
|
+
const visibleFunctions = computed(() => {
|
|
764
|
+
if (showAllMethods.value) return sortedFunctions.value;
|
|
765
|
+
return sortedFunctions.value.slice(0, topMethodsLimit);
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
function toggleShowAllMethods() {
|
|
769
|
+
showAllMethods.value = !showAllMethods.value;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// ── CC heat-bar helpers ──
|
|
773
|
+
// Fill width is clamped at 3× threshold so a CC of 80 with
|
|
774
|
+
// threshold 15 still produces a visually meaningful bar rather
|
|
775
|
+
// than overflowing. The threshold marker sits at the "1.0×"
|
|
776
|
+
// position (i.e. threshold/3threshold = 33%).
|
|
777
|
+
function ccRatio(fn) {
|
|
778
|
+
const max = fileDetail.value?.cyclomaticMax || 1;
|
|
779
|
+
return Math.round((fn.cyclomaticComplexity / max) * 100);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
function heatBarStyle(fn) {
|
|
783
|
+
const max = fileDetail.value?.cyclomaticMax || 1;
|
|
784
|
+
const cap = max * 3;
|
|
785
|
+
const pct = Math.min(100, (fn.cyclomaticComplexity / cap) * 100);
|
|
786
|
+
return { width: pct + "%", background: ccColor(fn) };
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
function thresholdMarker() {
|
|
790
|
+
// threshold sits at 1/3 of the bar (since we cap at 3× threshold)
|
|
791
|
+
return 33.33;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
function ccColor(fn) {
|
|
795
|
+
const max = fileDetail.value?.cyclomaticMax || 1;
|
|
796
|
+
const r = fn.cyclomaticComplexity / max;
|
|
797
|
+
if (r >= 2) return "var(--rating-E)"; // red — error (≥ 2×)
|
|
798
|
+
if (r > 1) return "var(--rating-C)"; // yellow — warning
|
|
799
|
+
if (r > 0.66) return "var(--rating-B)"; // yellow-green — near threshold
|
|
800
|
+
return "var(--rating-A)"; // green — healthy
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
function ccTooltip(fn) {
|
|
804
|
+
const max = fileDetail.value?.cyclomaticMax || 1;
|
|
805
|
+
return (
|
|
806
|
+
"CC " + fn.cyclomaticComplexity +
|
|
807
|
+
" / threshold " + max +
|
|
808
|
+
" (" + ccRatio(fn) + "% of threshold)"
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// ── Editor deep-link + jump-to-line ──
|
|
813
|
+
// vscode:// handler accepts absolute paths. The `absolutePath`
|
|
814
|
+
// field is resolved server-side through the workspace-traversal
|
|
815
|
+
// guard, so this is safe to paste into an href.
|
|
816
|
+
function editorLink(fn) {
|
|
817
|
+
const abs = fileDetail.value?.absolutePath;
|
|
818
|
+
if (!abs) return "#";
|
|
819
|
+
return "vscode://file" + abs + ":" + fn.startLine + ":1";
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
function jumpToLine(lineNum) {
|
|
823
|
+
// Source lines are keyed by 0-based index, so line N lives at
|
|
824
|
+
// child index N-1 of `.source-view`.
|
|
825
|
+
const view = document.querySelector(".source-view");
|
|
826
|
+
if (!view) return;
|
|
827
|
+
const row = view.children[lineNum - 1];
|
|
828
|
+
if (!row) return;
|
|
829
|
+
row.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
830
|
+
row.classList.remove("jump-target");
|
|
831
|
+
// force reflow so the animation restarts on repeat clicks
|
|
832
|
+
void row.offsetWidth;
|
|
833
|
+
row.classList.add("jump-target");
|
|
834
|
+
}
|
|
835
|
+
|
|
630
836
|
const sortedFindings = computed(() => {
|
|
631
837
|
if (!fileDetail.value) return [];
|
|
632
838
|
return [...fileDetail.value.findings].sort((a, b) => a.startLine - b.startLine);
|
|
@@ -777,7 +983,10 @@
|
|
|
777
983
|
currentView, selectedFile, fileDetail,
|
|
778
984
|
score, complexity, loading, error,
|
|
779
985
|
toolEntries, fileEntries, formatTimestamp,
|
|
780
|
-
sortedFunctions, sortedFindings,
|
|
986
|
+
sortedFunctions, sortedFindings, visibleFunctions,
|
|
987
|
+
showAllMethods, toggleShowAllMethods, topMethodsLimit,
|
|
988
|
+
ccRatio, ccColor, ccTooltip, heatBarStyle, thresholdMarker,
|
|
989
|
+
editorLink, jumpToLine,
|
|
781
990
|
navigateToFile, goBack,
|
|
782
991
|
lineFindings, lineClass, gutterClass, lineFnLabel,
|
|
783
992
|
};
|
|
@@ -3585,49 +3585,49 @@ var require_fast_uri = __commonJS({
|
|
|
3585
3585
|
schemelessOptions.skipEscape = true;
|
|
3586
3586
|
return serialize(resolved, schemelessOptions);
|
|
3587
3587
|
}
|
|
3588
|
-
function resolveComponent(base,
|
|
3588
|
+
function resolveComponent(base, relative7, options, skipNormalization) {
|
|
3589
3589
|
const target = {};
|
|
3590
3590
|
if (!skipNormalization) {
|
|
3591
3591
|
base = parse(serialize(base, options), options);
|
|
3592
|
-
|
|
3592
|
+
relative7 = parse(serialize(relative7, options), options);
|
|
3593
3593
|
}
|
|
3594
3594
|
options = options || {};
|
|
3595
|
-
if (!options.tolerant &&
|
|
3596
|
-
target.scheme =
|
|
3597
|
-
target.userinfo =
|
|
3598
|
-
target.host =
|
|
3599
|
-
target.port =
|
|
3600
|
-
target.path = removeDotSegments(
|
|
3601
|
-
target.query =
|
|
3595
|
+
if (!options.tolerant && relative7.scheme) {
|
|
3596
|
+
target.scheme = relative7.scheme;
|
|
3597
|
+
target.userinfo = relative7.userinfo;
|
|
3598
|
+
target.host = relative7.host;
|
|
3599
|
+
target.port = relative7.port;
|
|
3600
|
+
target.path = removeDotSegments(relative7.path || "");
|
|
3601
|
+
target.query = relative7.query;
|
|
3602
3602
|
} else {
|
|
3603
|
-
if (
|
|
3604
|
-
target.userinfo =
|
|
3605
|
-
target.host =
|
|
3606
|
-
target.port =
|
|
3607
|
-
target.path = removeDotSegments(
|
|
3608
|
-
target.query =
|
|
3603
|
+
if (relative7.userinfo !== void 0 || relative7.host !== void 0 || relative7.port !== void 0) {
|
|
3604
|
+
target.userinfo = relative7.userinfo;
|
|
3605
|
+
target.host = relative7.host;
|
|
3606
|
+
target.port = relative7.port;
|
|
3607
|
+
target.path = removeDotSegments(relative7.path || "");
|
|
3608
|
+
target.query = relative7.query;
|
|
3609
3609
|
} else {
|
|
3610
|
-
if (!
|
|
3610
|
+
if (!relative7.path) {
|
|
3611
3611
|
target.path = base.path;
|
|
3612
|
-
if (
|
|
3613
|
-
target.query =
|
|
3612
|
+
if (relative7.query !== void 0) {
|
|
3613
|
+
target.query = relative7.query;
|
|
3614
3614
|
} else {
|
|
3615
3615
|
target.query = base.query;
|
|
3616
3616
|
}
|
|
3617
3617
|
} else {
|
|
3618
|
-
if (
|
|
3619
|
-
target.path = removeDotSegments(
|
|
3618
|
+
if (relative7.path[0] === "/") {
|
|
3619
|
+
target.path = removeDotSegments(relative7.path);
|
|
3620
3620
|
} else {
|
|
3621
3621
|
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
|
|
3622
|
-
target.path = "/" +
|
|
3622
|
+
target.path = "/" + relative7.path;
|
|
3623
3623
|
} else if (!base.path) {
|
|
3624
|
-
target.path =
|
|
3624
|
+
target.path = relative7.path;
|
|
3625
3625
|
} else {
|
|
3626
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
3626
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative7.path;
|
|
3627
3627
|
}
|
|
3628
3628
|
target.path = removeDotSegments(target.path);
|
|
3629
3629
|
}
|
|
3630
|
-
target.query =
|
|
3630
|
+
target.query = relative7.query;
|
|
3631
3631
|
}
|
|
3632
3632
|
target.userinfo = base.userinfo;
|
|
3633
3633
|
target.host = base.host;
|
|
@@ -3635,7 +3635,7 @@ var require_fast_uri = __commonJS({
|
|
|
3635
3635
|
}
|
|
3636
3636
|
target.scheme = base.scheme;
|
|
3637
3637
|
}
|
|
3638
|
-
target.fragment =
|
|
3638
|
+
target.fragment = relative7.fragment;
|
|
3639
3639
|
return target;
|
|
3640
3640
|
}
|
|
3641
3641
|
function equal(uriA, uriB, options) {
|
|
@@ -7454,6 +7454,42 @@ var DEFAULT_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
7454
7454
|
"out",
|
|
7455
7455
|
"target",
|
|
7456
7456
|
"coverage",
|
|
7457
|
+
"artifacts",
|
|
7458
|
+
// CI artefact staging, Maven
|
|
7459
|
+
"publish",
|
|
7460
|
+
// `dotnet publish` output
|
|
7461
|
+
// Test coverage report bundles (generated HTML/JS from coverage tools;
|
|
7462
|
+
// walking them floods the complexity scanner with synthetic minified
|
|
7463
|
+
// functions like `coverage-report/main.js::gG` at CC 80+).
|
|
7464
|
+
"coverage-report",
|
|
7465
|
+
// ReportGenerator default (.NET)
|
|
7466
|
+
"CoverageReport",
|
|
7467
|
+
// ReportGenerator PascalCase variant
|
|
7468
|
+
"coveragereport",
|
|
7469
|
+
// ReportGenerator lowercase fallback
|
|
7470
|
+
"TestResults",
|
|
7471
|
+
// `dotnet test` default output
|
|
7472
|
+
"cobertura",
|
|
7473
|
+
// Cobertura XML reporter
|
|
7474
|
+
"lcov-report",
|
|
7475
|
+
// Istanbul HTML reporter
|
|
7476
|
+
"htmlcov",
|
|
7477
|
+
// coverage.py HTML output
|
|
7478
|
+
// Desktop / mobile packaging outputs
|
|
7479
|
+
"dist-electron",
|
|
7480
|
+
// Electron-builder
|
|
7481
|
+
"release",
|
|
7482
|
+
// Electron-builder, Tauri
|
|
7483
|
+
// .NET per-project build outputs (conventional at any depth)
|
|
7484
|
+
"bin",
|
|
7485
|
+
"obj",
|
|
7486
|
+
// iOS / macOS dependency + build caches
|
|
7487
|
+
"Pods",
|
|
7488
|
+
// CocoaPods
|
|
7489
|
+
"DerivedData",
|
|
7490
|
+
// Xcode
|
|
7491
|
+
"Carthage",
|
|
7492
|
+
// Swift
|
|
7457
7493
|
// Framework build outputs
|
|
7458
7494
|
".next",
|
|
7459
7495
|
// Next.js
|
|
@@ -7816,6 +7852,7 @@ async function buildFileDetail(input) {
|
|
|
7816
7852
|
) / 100 : 0;
|
|
7817
7853
|
return {
|
|
7818
7854
|
filePath: relativePath,
|
|
7855
|
+
absolutePath,
|
|
7819
7856
|
language,
|
|
7820
7857
|
physicalLoc,
|
|
7821
7858
|
logicalLoc,
|
|
@@ -9124,7 +9161,9 @@ function detectProjectType(workspaceRoot) {
|
|
|
9124
9161
|
if (has("Directory.Build.props")) return "csharp";
|
|
9125
9162
|
try {
|
|
9126
9163
|
const entries = readdirSync2(workspaceRoot);
|
|
9127
|
-
if (entries.some(
|
|
9164
|
+
if (entries.some(
|
|
9165
|
+
(e) => e.endsWith(".csproj") || e.endsWith(".sln") || e.endsWith(".slnx")
|
|
9166
|
+
)) {
|
|
9128
9167
|
return "csharp";
|
|
9129
9168
|
}
|
|
9130
9169
|
} catch {
|
|
@@ -9716,7 +9755,7 @@ async function autoScan(workspaceRoot, sarifStore, logger2, options) {
|
|
|
9716
9755
|
// src/monorepo/project-map.ts
|
|
9717
9756
|
import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync3 } from "node:fs";
|
|
9718
9757
|
import { promises as fs8 } from "node:fs";
|
|
9719
|
-
import { join as join12, basename as basename2, resolve as resolve8 } from "node:path";
|
|
9758
|
+
import { join as join12, basename as basename2, resolve as resolve8, relative as relative6, isAbsolute as isAbsolute5 } from "node:path";
|
|
9720
9759
|
import { execFile as execFile4 } from "node:child_process";
|
|
9721
9760
|
var MONOREPO_DIRS2 = ["apps", "packages", "libs", "modules", "services"];
|
|
9722
9761
|
var SCANNER_FOR_TYPE = {
|
|
@@ -9758,7 +9797,9 @@ function detectProjectType2(dir) {
|
|
|
9758
9797
|
if (has("Directory.Build.props")) return "csharp";
|
|
9759
9798
|
try {
|
|
9760
9799
|
const entries = readdirSync3(dir);
|
|
9761
|
-
if (entries.some(
|
|
9800
|
+
if (entries.some(
|
|
9801
|
+
(e) => e.endsWith(".csproj") || e.endsWith(".sln") || e.endsWith(".slnx")
|
|
9802
|
+
)) {
|
|
9762
9803
|
return "csharp";
|
|
9763
9804
|
}
|
|
9764
9805
|
} catch {
|
|
@@ -9776,17 +9817,66 @@ function extractWorkspacePatterns(workspaces) {
|
|
|
9776
9817
|
}
|
|
9777
9818
|
return [];
|
|
9778
9819
|
}
|
|
9820
|
+
function stripYamlComment(line) {
|
|
9821
|
+
let inSingle = false;
|
|
9822
|
+
let inDouble = false;
|
|
9823
|
+
for (let i = 0; i < line.length; i++) {
|
|
9824
|
+
const ch = line[i];
|
|
9825
|
+
if (ch === "\\" && inDouble && i + 1 < line.length) {
|
|
9826
|
+
i++;
|
|
9827
|
+
continue;
|
|
9828
|
+
}
|
|
9829
|
+
if (!inDouble && ch === "'") {
|
|
9830
|
+
inSingle = !inSingle;
|
|
9831
|
+
} else if (!inSingle && ch === '"') {
|
|
9832
|
+
inDouble = !inDouble;
|
|
9833
|
+
} else if (!inSingle && !inDouble && ch === "#") {
|
|
9834
|
+
return line.slice(0, i);
|
|
9835
|
+
}
|
|
9836
|
+
}
|
|
9837
|
+
return line;
|
|
9838
|
+
}
|
|
9839
|
+
function parsePnpmWorkspaceYaml(yaml) {
|
|
9840
|
+
const patterns = [];
|
|
9841
|
+
const lines = yaml.split(/\r?\n/);
|
|
9842
|
+
let inPackages = false;
|
|
9843
|
+
for (const rawLine of lines) {
|
|
9844
|
+
const line = stripYamlComment(rawLine).replace(/\s+$/, "");
|
|
9845
|
+
if (line.length === 0) continue;
|
|
9846
|
+
if (/^packages\s*:\s*$/.test(line)) {
|
|
9847
|
+
inPackages = true;
|
|
9848
|
+
continue;
|
|
9849
|
+
}
|
|
9850
|
+
if (inPackages && /^[^\s-]/.test(line)) {
|
|
9851
|
+
inPackages = false;
|
|
9852
|
+
continue;
|
|
9853
|
+
}
|
|
9854
|
+
if (!inPackages) continue;
|
|
9855
|
+
const m = /^\s*-\s*("([^"]*)"|'([^']*)'|(\S+))\s*$/.exec(line);
|
|
9856
|
+
if (m) {
|
|
9857
|
+
const value = m[2] ?? m[3] ?? m[4] ?? "";
|
|
9858
|
+
if (value.length > 0) patterns.push(value);
|
|
9859
|
+
}
|
|
9860
|
+
}
|
|
9861
|
+
return patterns;
|
|
9862
|
+
}
|
|
9779
9863
|
function expandWorkspacePattern(workspaceRoot, pattern) {
|
|
9864
|
+
const isInsideWorkspace = (candidate) => {
|
|
9865
|
+
const rel = relative6(workspaceRoot, candidate);
|
|
9866
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute5(rel);
|
|
9867
|
+
};
|
|
9780
9868
|
if (pattern.endsWith("/*")) {
|
|
9781
9869
|
const parentDir = join12(workspaceRoot, pattern.slice(0, -2));
|
|
9870
|
+
if (!isInsideWorkspace(parentDir)) return [];
|
|
9782
9871
|
try {
|
|
9783
9872
|
const entries = readdirSync3(parentDir, { withFileTypes: true });
|
|
9784
|
-
return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => join12(parentDir, e.name));
|
|
9873
|
+
return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => join12(parentDir, e.name)).filter(isInsideWorkspace);
|
|
9785
9874
|
} catch {
|
|
9786
9875
|
return [];
|
|
9787
9876
|
}
|
|
9788
9877
|
}
|
|
9789
9878
|
const full = resolve8(workspaceRoot, pattern);
|
|
9879
|
+
if (!isInsideWorkspace(full)) return [];
|
|
9790
9880
|
try {
|
|
9791
9881
|
const entries = readdirSync3(full, { withFileTypes: true });
|
|
9792
9882
|
void entries;
|
|
@@ -9811,12 +9901,24 @@ function collectSubdirectories(workspaceRoot, extraDirs) {
|
|
|
9811
9901
|
} catch {
|
|
9812
9902
|
}
|
|
9813
9903
|
}
|
|
9904
|
+
const pnpmPath = join12(workspaceRoot, "pnpm-workspace.yaml");
|
|
9905
|
+
if (existsSync6(pnpmPath)) {
|
|
9906
|
+
try {
|
|
9907
|
+
const raw = readFileSync5(pnpmPath, "utf-8");
|
|
9908
|
+
const patterns = parsePnpmWorkspaceYaml(raw);
|
|
9909
|
+
for (const pattern of patterns) {
|
|
9910
|
+
for (const absPath of expandWorkspacePattern(workspaceRoot, pattern)) {
|
|
9911
|
+
subdirs.add(absPath);
|
|
9912
|
+
}
|
|
9913
|
+
}
|
|
9914
|
+
} catch {
|
|
9915
|
+
}
|
|
9916
|
+
}
|
|
9814
9917
|
if (extraDirs && extraDirs.length > 0) {
|
|
9815
9918
|
for (const dir of extraDirs) {
|
|
9816
9919
|
const absDir = resolve8(workspaceRoot, dir);
|
|
9817
9920
|
if (!existsSync6(absDir)) continue;
|
|
9818
|
-
|
|
9819
|
-
if (hasMarker) {
|
|
9921
|
+
if (directoryIsProjectRoot(absDir)) {
|
|
9820
9922
|
subdirs.add(absDir);
|
|
9821
9923
|
continue;
|
|
9822
9924
|
}
|
|
@@ -9857,6 +9959,18 @@ var PROJECT_MARKERS = [
|
|
|
9857
9959
|
"build.gradle.kts",
|
|
9858
9960
|
"Directory.Build.props"
|
|
9859
9961
|
];
|
|
9962
|
+
var DOTNET_PROJECT_EXTENSIONS = [".csproj", ".sln", ".slnx"];
|
|
9963
|
+
function directoryIsProjectRoot(absDir) {
|
|
9964
|
+
if (PROJECT_MARKERS.some((m) => existsSync6(join12(absDir, m)))) return true;
|
|
9965
|
+
try {
|
|
9966
|
+
const entries = readdirSync3(absDir);
|
|
9967
|
+
return entries.some(
|
|
9968
|
+
(e) => DOTNET_PROJECT_EXTENSIONS.some((ext) => e.endsWith(ext))
|
|
9969
|
+
);
|
|
9970
|
+
} catch {
|
|
9971
|
+
return false;
|
|
9972
|
+
}
|
|
9973
|
+
}
|
|
9860
9974
|
async function discoverProjectMap(workspaceRoot, options) {
|
|
9861
9975
|
const subdirs = collectSubdirectories(workspaceRoot, options?.projectDirs);
|
|
9862
9976
|
const binaryCache = /* @__PURE__ */ new Map();
|