@codacy/analysis-runner 0.6.2 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +133 -7
- package/dist/index.js +1341 -48
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -34,15 +34,18 @@ __export(index_exports, {
|
|
|
34
34
|
applyCodacyYamlExcludes: () => applyCodacyYamlExcludes,
|
|
35
35
|
buildToolConfig: () => buildToolConfig,
|
|
36
36
|
clearAdapters: () => clearAdapters,
|
|
37
|
+
computePatternSummary: () => computePatternSummary,
|
|
37
38
|
configureApiToken: () => configureApiToken,
|
|
38
39
|
createCodacyConfig: () => createCodacyConfig,
|
|
39
40
|
createLogger: () => createLogger,
|
|
40
41
|
detectLanguages: () => detectLanguages,
|
|
42
|
+
discover: () => discover,
|
|
41
43
|
formatOutput: () => formatOutput,
|
|
42
44
|
formatTextSummary: () => formatTextSummary,
|
|
43
45
|
getAdapter: () => getAdapter,
|
|
44
46
|
getRegisteredAdapters: () => getRegisteredAdapters,
|
|
45
47
|
getRegisteredDescriptors: () => getRegisteredDescriptors,
|
|
48
|
+
initAutoConfig: () => initAutoConfig,
|
|
46
49
|
initContainerConfig: () => initContainerConfig,
|
|
47
50
|
initDefaultConfig: () => initDefaultConfig,
|
|
48
51
|
initLocalConfig: () => initLocalConfig,
|
|
@@ -51,6 +54,7 @@ __export(index_exports, {
|
|
|
51
54
|
listIgnoredFiles: () => listIgnoredFiles,
|
|
52
55
|
listPrFiles: () => listPrFiles,
|
|
53
56
|
listStagedFiles: () => listStagedFiles,
|
|
57
|
+
parseAutoFilters: () => parseAutoFilters,
|
|
54
58
|
readCodacyConfig: () => readCodacyConfig,
|
|
55
59
|
readCodacyYaml: () => readCodacyYaml,
|
|
56
60
|
registerAdapter: () => registerAdapter,
|
|
@@ -192,6 +196,7 @@ function createCodacyConfig(params = {}) {
|
|
|
192
196
|
source: params.source ?? "local",
|
|
193
197
|
provider: params.provider ?? null,
|
|
194
198
|
organization: params.organization ?? null,
|
|
199
|
+
autoFilters: params.autoFilters,
|
|
195
200
|
repositoryId: params.repositoryId ?? null,
|
|
196
201
|
repositoryName: params.repositoryName ?? null,
|
|
197
202
|
createdAt: now,
|
|
@@ -209,8 +214,10 @@ function createCodacyConfig(params = {}) {
|
|
|
209
214
|
|
|
210
215
|
// src/files.ts
|
|
211
216
|
var import_child_process = require("child_process");
|
|
217
|
+
var import_fs = __toESM(require("fs"));
|
|
212
218
|
var import_path2 = __toESM(require("path"));
|
|
213
219
|
var import_minimatch = require("minimatch");
|
|
220
|
+
var MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024;
|
|
214
221
|
function listRepoFiles(repositoryRoot) {
|
|
215
222
|
try {
|
|
216
223
|
const tracked = (0, import_child_process.execSync)("git ls-files", {
|
|
@@ -235,6 +242,30 @@ function listRepoFiles(repositoryRoot) {
|
|
|
235
242
|
return [];
|
|
236
243
|
}
|
|
237
244
|
}
|
|
245
|
+
function filterOversizedFiles(files, repositoryRoot, logger) {
|
|
246
|
+
const result = [];
|
|
247
|
+
let skipped = 0;
|
|
248
|
+
for (const f of files) {
|
|
249
|
+
try {
|
|
250
|
+
const stat = import_fs.default.statSync(import_path2.default.join(repositoryRoot, f));
|
|
251
|
+
if (stat.size > MAX_FILE_SIZE_BYTES) {
|
|
252
|
+
skipped++;
|
|
253
|
+
logger?.debug("Skipping oversized file", {
|
|
254
|
+
file: f,
|
|
255
|
+
sizeKB: Math.round(stat.size / 1024),
|
|
256
|
+
limitKB: MAX_FILE_SIZE_BYTES / 1024
|
|
257
|
+
});
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
} catch {
|
|
261
|
+
}
|
|
262
|
+
result.push(f);
|
|
263
|
+
}
|
|
264
|
+
if (skipped > 0) {
|
|
265
|
+
logger?.info("Filtered oversized files", { skipped, remaining: result.length });
|
|
266
|
+
}
|
|
267
|
+
return result;
|
|
268
|
+
}
|
|
238
269
|
function applyExclusions(files, globalExclude, toolExclude) {
|
|
239
270
|
const allExclusions = [...globalExclude, ...toolExclude];
|
|
240
271
|
if (allExclusions.length === 0) return files;
|
|
@@ -426,6 +457,10 @@ async function runTool(adapter, ctx, codacyToolConfig, timeoutMs) {
|
|
|
426
457
|
async function runToolsSequential(tools, timeoutMs, callbacks) {
|
|
427
458
|
const results = [];
|
|
428
459
|
for (const { adapter, ctx, codacyToolConfig } of tools) {
|
|
460
|
+
if (callbacks?.onToolProgress) {
|
|
461
|
+
const cb = callbacks.onToolProgress;
|
|
462
|
+
ctx.onProgress = (message) => cb(adapter.id, adapter.displayName, message);
|
|
463
|
+
}
|
|
429
464
|
callbacks?.onToolStart?.(adapter.id, adapter.displayName);
|
|
430
465
|
const result = await runTool(adapter, ctx, codacyToolConfig, timeoutMs);
|
|
431
466
|
callbacks?.onToolComplete?.(
|
|
@@ -445,6 +480,10 @@ async function runToolsParallel(tools, timeoutMs, concurrency, callbacks) {
|
|
|
445
480
|
while (nextIndex < tools.length) {
|
|
446
481
|
const index = nextIndex++;
|
|
447
482
|
const { adapter, ctx, codacyToolConfig } = tools[index];
|
|
483
|
+
if (callbacks?.onToolProgress) {
|
|
484
|
+
const cb = callbacks.onToolProgress;
|
|
485
|
+
ctx.onProgress = (message) => cb(adapter.id, adapter.displayName, message);
|
|
486
|
+
}
|
|
448
487
|
callbacks?.onToolStart?.(adapter.id, adapter.displayName);
|
|
449
488
|
results[index] = await runTool(adapter, ctx, codacyToolConfig, timeoutMs);
|
|
450
489
|
callbacks?.onToolComplete?.(
|
|
@@ -460,6 +499,116 @@ async function runToolsParallel(tools, timeoutMs, concurrency, callbacks) {
|
|
|
460
499
|
return results;
|
|
461
500
|
}
|
|
462
501
|
|
|
502
|
+
// src/summary.ts
|
|
503
|
+
var NOISY_THRESHOLD = 500;
|
|
504
|
+
var SEVERITY_ORDER = ["Error", "High", "Warning", "Info"];
|
|
505
|
+
var SEVERITY_LABELS = {
|
|
506
|
+
Error: "CRITICAL",
|
|
507
|
+
High: "HIGH",
|
|
508
|
+
Warning: "WARNING",
|
|
509
|
+
Info: "MINOR"
|
|
510
|
+
};
|
|
511
|
+
var CATEGORY_ORDER = [
|
|
512
|
+
"Security",
|
|
513
|
+
"ErrorProne",
|
|
514
|
+
"Performance",
|
|
515
|
+
"BestPractice",
|
|
516
|
+
"UnusedCode",
|
|
517
|
+
"Compatibility",
|
|
518
|
+
"Complexity",
|
|
519
|
+
"Comprehensibility",
|
|
520
|
+
"CodeStyle",
|
|
521
|
+
"Documentation"
|
|
522
|
+
];
|
|
523
|
+
function formatCategoryName(category) {
|
|
524
|
+
return category.replace(/([a-z])([A-Z])/g, "$1 $2");
|
|
525
|
+
}
|
|
526
|
+
function computePatternSummary(issues, lookup) {
|
|
527
|
+
if (issues.length === 0) {
|
|
528
|
+
return {
|
|
529
|
+
groups: [],
|
|
530
|
+
totalIssues: 0,
|
|
531
|
+
totalPatterns: 0,
|
|
532
|
+
noisyPatternCount: 0,
|
|
533
|
+
noisyThreshold: NOISY_THRESHOLD
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
const accumMap = /* @__PURE__ */ new Map();
|
|
537
|
+
for (const issue of issues) {
|
|
538
|
+
const key = `${issue.toolId}|${issue.severity}|${issue.category}|${issue.patternId}`;
|
|
539
|
+
let accum = accumMap.get(key);
|
|
540
|
+
if (!accum) {
|
|
541
|
+
accum = {
|
|
542
|
+
patternId: issue.patternId,
|
|
543
|
+
toolId: issue.toolId,
|
|
544
|
+
severity: issue.severity,
|
|
545
|
+
category: issue.category,
|
|
546
|
+
issueCount: 0,
|
|
547
|
+
files: /* @__PURE__ */ new Set()
|
|
548
|
+
};
|
|
549
|
+
accumMap.set(key, accum);
|
|
550
|
+
}
|
|
551
|
+
accum.issueCount++;
|
|
552
|
+
accum.files.add(issue.filePath);
|
|
553
|
+
}
|
|
554
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
555
|
+
for (const accum of accumMap.values()) {
|
|
556
|
+
const groupKey = `${accum.severity}|${accum.category}`;
|
|
557
|
+
let list = groupMap.get(groupKey);
|
|
558
|
+
if (!list) {
|
|
559
|
+
list = [];
|
|
560
|
+
groupMap.set(groupKey, list);
|
|
561
|
+
}
|
|
562
|
+
list.push(accum);
|
|
563
|
+
}
|
|
564
|
+
let totalPatterns = 0;
|
|
565
|
+
let noisyPatternCount = 0;
|
|
566
|
+
const groups = [];
|
|
567
|
+
for (const severity of SEVERITY_ORDER) {
|
|
568
|
+
for (const category of CATEGORY_ORDER) {
|
|
569
|
+
const accums = groupMap.get(`${severity}|${category}`);
|
|
570
|
+
if (!accums || accums.length === 0) continue;
|
|
571
|
+
accums.sort((a, b) => b.issueCount - a.issueCount);
|
|
572
|
+
let groupIssueCount = 0;
|
|
573
|
+
const patterns = [];
|
|
574
|
+
for (const acc of accums) {
|
|
575
|
+
const meta = lookup.get(`${acc.toolId}|${acc.patternId}`);
|
|
576
|
+
const noisy = acc.issueCount >= NOISY_THRESHOLD;
|
|
577
|
+
if (noisy) noisyPatternCount++;
|
|
578
|
+
totalPatterns++;
|
|
579
|
+
groupIssueCount += acc.issueCount;
|
|
580
|
+
patterns.push({
|
|
581
|
+
patternId: acc.patternId,
|
|
582
|
+
title: meta?.title ?? acc.patternId,
|
|
583
|
+
toolId: acc.toolId,
|
|
584
|
+
toolDisplayName: meta?.toolDisplayName ?? acc.toolId,
|
|
585
|
+
category,
|
|
586
|
+
severity,
|
|
587
|
+
issueCount: acc.issueCount,
|
|
588
|
+
fileCount: acc.files.size,
|
|
589
|
+
noisy
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
groups.push({
|
|
593
|
+
severity,
|
|
594
|
+
severityLabel: SEVERITY_LABELS[severity],
|
|
595
|
+
category,
|
|
596
|
+
categoryLabel: formatCategoryName(category),
|
|
597
|
+
patterns,
|
|
598
|
+
totalIssues: groupIssueCount,
|
|
599
|
+
totalPatterns: patterns.length
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
return {
|
|
604
|
+
groups,
|
|
605
|
+
totalIssues: issues.length,
|
|
606
|
+
totalPatterns,
|
|
607
|
+
noisyPatternCount,
|
|
608
|
+
noisyThreshold: NOISY_THRESHOLD
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
463
612
|
// src/analyze.ts
|
|
464
613
|
var execFileAsync = (0, import_util.promisify)(import_child_process2.execFile);
|
|
465
614
|
var nullLogger = {
|
|
@@ -478,6 +627,13 @@ async function analyze(options) {
|
|
|
478
627
|
const toolTimeout = options.toolTimeout || 6e5;
|
|
479
628
|
const parallelTools = options.parallelTools || 1;
|
|
480
629
|
const logger = options.logger ?? nullLogger;
|
|
630
|
+
const phaseTimings = {
|
|
631
|
+
availabilityMs: 0,
|
|
632
|
+
fileDiscoveryMs: 0,
|
|
633
|
+
fileRoutingMs: 0,
|
|
634
|
+
toolExecutionMs: 0,
|
|
635
|
+
installMs: 0
|
|
636
|
+
};
|
|
481
637
|
const emptyCapability = {
|
|
482
638
|
ready: [],
|
|
483
639
|
unavailable: [],
|
|
@@ -556,6 +712,7 @@ async function analyze(options) {
|
|
|
556
712
|
const ready = [];
|
|
557
713
|
const unavailable = [];
|
|
558
714
|
const availabilityTimeout = 6e4;
|
|
715
|
+
const availabilityStartMs = Date.now();
|
|
559
716
|
for (const adapter of adapters) {
|
|
560
717
|
const ctx = makeToolContext(baseCtx, adapter, []);
|
|
561
718
|
const checkStart = Date.now();
|
|
@@ -614,6 +771,7 @@ async function analyze(options) {
|
|
|
614
771
|
});
|
|
615
772
|
}
|
|
616
773
|
}
|
|
774
|
+
phaseTimings.availabilityMs = Date.now() - availabilityStartMs;
|
|
617
775
|
const capability = {
|
|
618
776
|
ready,
|
|
619
777
|
unavailable,
|
|
@@ -629,6 +787,7 @@ async function analyze(options) {
|
|
|
629
787
|
configuredToolCount: configuredIds.size
|
|
630
788
|
});
|
|
631
789
|
if (mode === "auto-install" && capability.unavailable.length > 0) {
|
|
790
|
+
const installStartMs = Date.now();
|
|
632
791
|
const installErrors = [];
|
|
633
792
|
for (let i = capability.unavailable.length - 1; i >= 0; i--) {
|
|
634
793
|
const status = capability.unavailable[i];
|
|
@@ -692,6 +851,7 @@ async function analyze(options) {
|
|
|
692
851
|
});
|
|
693
852
|
}
|
|
694
853
|
}
|
|
854
|
+
phaseTimings.installMs = Date.now() - installStartMs;
|
|
695
855
|
if (installErrors.length > 0) {
|
|
696
856
|
return buildResult(
|
|
697
857
|
startedAt,
|
|
@@ -700,7 +860,9 @@ async function analyze(options) {
|
|
|
700
860
|
capability,
|
|
701
861
|
[],
|
|
702
862
|
installErrors,
|
|
703
|
-
[]
|
|
863
|
+
[],
|
|
864
|
+
void 0,
|
|
865
|
+
phaseTimings
|
|
704
866
|
);
|
|
705
867
|
}
|
|
706
868
|
}
|
|
@@ -752,6 +914,7 @@ async function analyze(options) {
|
|
|
752
914
|
[]
|
|
753
915
|
);
|
|
754
916
|
}
|
|
917
|
+
const fileDiscoveryStartMs = Date.now();
|
|
755
918
|
const globalExclude = codacyConfig.exclude || [];
|
|
756
919
|
let allFiles = listRepoFiles(options.repositoryRoot);
|
|
757
920
|
logger.debug("File discovery", { totalFiles: allFiles.length });
|
|
@@ -773,6 +936,9 @@ async function analyze(options) {
|
|
|
773
936
|
allFiles = filterByTargetFiles(allFiles, options.files);
|
|
774
937
|
logger.debug("Filtered by target files", { afterFilter: allFiles.length });
|
|
775
938
|
}
|
|
939
|
+
allFiles = filterOversizedFiles(allFiles, options.repositoryRoot, logger);
|
|
940
|
+
phaseTimings.fileDiscoveryMs = Date.now() - fileDiscoveryStartMs;
|
|
941
|
+
const fileRoutingStartMs = Date.now();
|
|
776
942
|
const readyAdapters = adapters.filter((a) => capability.ready.some((r) => r.toolId === a.id));
|
|
777
943
|
const toolRuns = [];
|
|
778
944
|
for (const adapter of readyAdapters) {
|
|
@@ -813,6 +979,8 @@ async function analyze(options) {
|
|
|
813
979
|
});
|
|
814
980
|
toolRuns.push({ adapter, ctx: configCtx, codacyToolConfig });
|
|
815
981
|
}
|
|
982
|
+
phaseTimings.fileRoutingMs = Date.now() - fileRoutingStartMs;
|
|
983
|
+
const toolExecStartMs = Date.now();
|
|
816
984
|
logger.info("Starting tool execution", {
|
|
817
985
|
toolCount: toolRuns.length,
|
|
818
986
|
parallel: parallelTools > 1,
|
|
@@ -820,7 +988,8 @@ async function analyze(options) {
|
|
|
820
988
|
});
|
|
821
989
|
const callbacks = {
|
|
822
990
|
onToolStart: options.onToolStart,
|
|
823
|
-
onToolComplete: options.onToolComplete
|
|
991
|
+
onToolComplete: options.onToolComplete,
|
|
992
|
+
onToolProgress: options.onToolProgress
|
|
824
993
|
};
|
|
825
994
|
let results;
|
|
826
995
|
if (parallelTools > 1 && toolRuns.length > 1) {
|
|
@@ -828,13 +997,27 @@ async function analyze(options) {
|
|
|
828
997
|
} else {
|
|
829
998
|
results = await runToolsSequential(toolRuns, toolTimeout, callbacks);
|
|
830
999
|
}
|
|
1000
|
+
phaseTimings.toolExecutionMs = Date.now() - toolExecStartMs;
|
|
831
1001
|
const allIssues = normalizeIssues(results.flatMap((r) => r.issues));
|
|
832
1002
|
const allErrors = results.flatMap((r) => r.errors);
|
|
833
1003
|
const allSummaries = results.map((r) => r.summary);
|
|
1004
|
+
const patternLookup = /* @__PURE__ */ new Map();
|
|
1005
|
+
for (const { adapter } of toolRuns) {
|
|
1006
|
+
if (adapter.patternsMetadata) {
|
|
1007
|
+
for (const entry of adapter.patternsMetadata) {
|
|
1008
|
+
patternLookup.set(`${adapter.id}|${entry.id}`, {
|
|
1009
|
+
title: entry.title,
|
|
1010
|
+
toolDisplayName: adapter.displayName
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
const patternSummary = allIssues.length > 0 ? computePatternSummary(allIssues, patternLookup) : void 0;
|
|
834
1016
|
logger.info("Pipeline complete", {
|
|
835
1017
|
totalIssues: allIssues.length,
|
|
836
1018
|
totalErrors: allErrors.length,
|
|
837
|
-
durationMs: Date.now() - startedAt.getTime()
|
|
1019
|
+
durationMs: Date.now() - startedAt.getTime(),
|
|
1020
|
+
phaseTimings
|
|
838
1021
|
});
|
|
839
1022
|
return buildResult(
|
|
840
1023
|
startedAt,
|
|
@@ -843,7 +1026,9 @@ async function analyze(options) {
|
|
|
843
1026
|
capability,
|
|
844
1027
|
allIssues,
|
|
845
1028
|
allErrors,
|
|
846
|
-
allSummaries
|
|
1029
|
+
allSummaries,
|
|
1030
|
+
patternSummary,
|
|
1031
|
+
phaseTimings
|
|
847
1032
|
);
|
|
848
1033
|
}
|
|
849
1034
|
function makeToolContext(base, adapter, targetFiles) {
|
|
@@ -869,7 +1054,7 @@ function resolveGitScope(repositoryRoot, scope) {
|
|
|
869
1054
|
const base = scope.diff === true ? void 0 : scope.diff;
|
|
870
1055
|
return listDiffFiles(repositoryRoot, base);
|
|
871
1056
|
}
|
|
872
|
-
function buildResult(startedAt, repositoryRoot, mode, capability, issues, errors, toolResults) {
|
|
1057
|
+
function buildResult(startedAt, repositoryRoot, mode, capability, issues, errors, toolResults, patternSummary, phaseTimings) {
|
|
873
1058
|
const completedAt = /* @__PURE__ */ new Date();
|
|
874
1059
|
return {
|
|
875
1060
|
metadata: {
|
|
@@ -877,12 +1062,14 @@ function buildResult(startedAt, repositoryRoot, mode, capability, issues, errors
|
|
|
877
1062
|
completedAt: completedAt.toISOString(),
|
|
878
1063
|
durationMs: completedAt.getTime() - startedAt.getTime(),
|
|
879
1064
|
repositoryRoot,
|
|
880
|
-
executionMode: mode
|
|
1065
|
+
executionMode: mode,
|
|
1066
|
+
...phaseTimings ? { phaseTimings } : {}
|
|
881
1067
|
},
|
|
882
1068
|
capability,
|
|
883
1069
|
issues,
|
|
884
1070
|
errors,
|
|
885
|
-
toolResults
|
|
1071
|
+
toolResults,
|
|
1072
|
+
...patternSummary ? { patternSummary } : {}
|
|
886
1073
|
};
|
|
887
1074
|
}
|
|
888
1075
|
|
|
@@ -1045,11 +1232,7 @@ function formatText(result) {
|
|
|
1045
1232
|
if (result.errors.length > 0) {
|
|
1046
1233
|
lines.push("");
|
|
1047
1234
|
lines.push("Errors:");
|
|
1048
|
-
|
|
1049
|
-
const prefix = `[${error.toolId}/${error.phase}]`;
|
|
1050
|
-
const location = error.filePath ? ` ${error.filePath}:` : "";
|
|
1051
|
-
lines.push(import_ansis.default.red(` ${prefix}${location} ${error.message}`));
|
|
1052
|
-
}
|
|
1235
|
+
lines.push(...formatErrorSection(result));
|
|
1053
1236
|
lines.push("");
|
|
1054
1237
|
}
|
|
1055
1238
|
lines.push(formatTextSummary(result, isInspect));
|
|
@@ -1148,6 +1331,33 @@ function formatTextSummary(result, isInspect) {
|
|
|
1148
1331
|
lines.push(table.toString());
|
|
1149
1332
|
}
|
|
1150
1333
|
}
|
|
1334
|
+
if (!inspect && result.metadata.phaseTimings) {
|
|
1335
|
+
const pt = result.metadata.phaseTimings;
|
|
1336
|
+
const totalMs = result.metadata.durationMs;
|
|
1337
|
+
const toolSumMs = pt.toolExecutionMs;
|
|
1338
|
+
const overheadMs = totalMs - toolSumMs;
|
|
1339
|
+
if (overheadMs > 2e3 && overheadMs / totalMs > 0.2) {
|
|
1340
|
+
lines.push("");
|
|
1341
|
+
lines.push(import_ansis.default.bold("Pipeline timing:"));
|
|
1342
|
+
const entries2 = [
|
|
1343
|
+
["Availability checks", pt.availabilityMs],
|
|
1344
|
+
["File discovery", pt.fileDiscoveryMs],
|
|
1345
|
+
["File routing & config", pt.fileRoutingMs]
|
|
1346
|
+
];
|
|
1347
|
+
if (pt.installMs > 0) {
|
|
1348
|
+
entries2.push(["Install", pt.installMs]);
|
|
1349
|
+
}
|
|
1350
|
+
entries2.push(["Tool execution", pt.toolExecutionMs]);
|
|
1351
|
+
const accounted = entries2.reduce((sum, [, ms]) => sum + ms, 0);
|
|
1352
|
+
const other = totalMs - accounted;
|
|
1353
|
+
if (other > 500) {
|
|
1354
|
+
entries2.push(["Other", other]);
|
|
1355
|
+
}
|
|
1356
|
+
for (const [label, ms] of entries2) {
|
|
1357
|
+
lines.push(` ${label.padEnd(22)} ${(0, import_tooling4.formatDuration)(ms)}`);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1151
1361
|
const toolsWithHints = result.capability.unavailable.filter((t) => t.remediation);
|
|
1152
1362
|
if (toolsWithHints.length > 0) {
|
|
1153
1363
|
lines.push("");
|
|
@@ -1159,8 +1369,58 @@ function formatTextSummary(result, isInspect) {
|
|
|
1159
1369
|
}
|
|
1160
1370
|
}
|
|
1161
1371
|
}
|
|
1372
|
+
const patternSummaryText = formatPatternSummary(result.patternSummary);
|
|
1373
|
+
if (patternSummaryText) {
|
|
1374
|
+
lines.push("");
|
|
1375
|
+
lines.push(patternSummaryText);
|
|
1376
|
+
}
|
|
1162
1377
|
return lines.join("\n");
|
|
1163
1378
|
}
|
|
1379
|
+
var MAX_ERRORS_PER_GROUP = 5;
|
|
1380
|
+
function formatErrorSection(result) {
|
|
1381
|
+
const lines = [];
|
|
1382
|
+
const displayNameMap = /* @__PURE__ */ new Map();
|
|
1383
|
+
for (const t of result.capability.ready) {
|
|
1384
|
+
displayNameMap.set(t.toolId, t.displayName ?? t.toolId);
|
|
1385
|
+
}
|
|
1386
|
+
for (const t of result.capability.unavailable) {
|
|
1387
|
+
displayNameMap.set(t.toolId, t.displayName ?? t.toolId);
|
|
1388
|
+
}
|
|
1389
|
+
const displayName = (toolId) => displayNameMap.get(toolId) ?? toolId;
|
|
1390
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1391
|
+
for (const error of result.errors) {
|
|
1392
|
+
const key = `${displayName(error.toolId)}/${error.phase}`;
|
|
1393
|
+
const list = groups.get(key) ?? [];
|
|
1394
|
+
list.push(error);
|
|
1395
|
+
groups.set(key, list);
|
|
1396
|
+
}
|
|
1397
|
+
for (const [groupKey, groupErrors] of groups) {
|
|
1398
|
+
const deduped = [];
|
|
1399
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1400
|
+
for (const err of groupErrors) {
|
|
1401
|
+
const dedupKey = `${err.filePath ?? ""}|||${err.message}`;
|
|
1402
|
+
const idx = seen.get(dedupKey);
|
|
1403
|
+
if (idx !== void 0) {
|
|
1404
|
+
deduped[idx].count++;
|
|
1405
|
+
} else {
|
|
1406
|
+
seen.set(dedupKey, deduped.length);
|
|
1407
|
+
deduped.push({ filePath: err.filePath, message: err.message, count: 1 });
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
const shown = deduped.slice(0, MAX_ERRORS_PER_GROUP);
|
|
1411
|
+
const overflowCount = deduped.length - shown.length;
|
|
1412
|
+
for (const entry of shown) {
|
|
1413
|
+
const prefix = `[${groupKey}]`;
|
|
1414
|
+
const location = entry.filePath ? ` ${entry.filePath}:` : "";
|
|
1415
|
+
const suffix = entry.count > 1 ? ` (x${entry.count})` : "";
|
|
1416
|
+
lines.push(import_ansis.default.red(` ${prefix}${location} ${entry.message}${suffix}`));
|
|
1417
|
+
}
|
|
1418
|
+
if (overflowCount > 0) {
|
|
1419
|
+
lines.push(import_ansis.default.dim(` ... and ${overflowCount} more ${groupKey} errors`));
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
return lines;
|
|
1423
|
+
}
|
|
1164
1424
|
var MAX_LINE_LENGTH = 100;
|
|
1165
1425
|
var MAX_MULTILINE_DISPLAY = 10;
|
|
1166
1426
|
function formatMultilineIssue(output, issue) {
|
|
@@ -1241,23 +1501,801 @@ function colorSeverity(severity) {
|
|
|
1241
1501
|
return `[${severity}]`;
|
|
1242
1502
|
}
|
|
1243
1503
|
}
|
|
1504
|
+
function severityColorFn(severity) {
|
|
1505
|
+
switch (severity) {
|
|
1506
|
+
case "Error":
|
|
1507
|
+
return (s) => import_ansis.default.hex("#ff6347")(s);
|
|
1508
|
+
case "High":
|
|
1509
|
+
return (s) => import_ansis.default.hex("#ff8c00")(s);
|
|
1510
|
+
case "Warning":
|
|
1511
|
+
return (s) => import_ansis.default.hex("#ffd700")(s);
|
|
1512
|
+
case "Info":
|
|
1513
|
+
return (s) => import_ansis.default.cyan(s);
|
|
1514
|
+
default:
|
|
1515
|
+
return (s) => s;
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
var MAX_PATTERNS_PER_GROUP = 10;
|
|
1519
|
+
function formatPatternSummary(summary) {
|
|
1520
|
+
if (!summary || summary.groups.length === 0) return "";
|
|
1521
|
+
const lines = [];
|
|
1522
|
+
lines.push(import_ansis.default.bold("Pattern Summary"));
|
|
1523
|
+
lines.push("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
1524
|
+
if (summary.noisyPatternCount > 0) {
|
|
1525
|
+
const n = summary.noisyPatternCount;
|
|
1526
|
+
lines.push(
|
|
1527
|
+
import_ansis.default.hex("#ff6347")(
|
|
1528
|
+
`${n} noisy pattern${n !== 1 ? "s" : ""} detected (${summary.noisyThreshold}+ issues) \u2014 consider disabling or reconfiguring`
|
|
1529
|
+
)
|
|
1530
|
+
);
|
|
1531
|
+
}
|
|
1532
|
+
lines.push("");
|
|
1533
|
+
let maxLabelLen = 0;
|
|
1534
|
+
let maxCountLen = 0;
|
|
1535
|
+
for (const group of summary.groups) {
|
|
1536
|
+
const visible = group.patterns.slice(0, MAX_PATTERNS_PER_GROUP);
|
|
1537
|
+
for (const p of visible) {
|
|
1538
|
+
const label = `${p.title} (${p.toolDisplayName})`;
|
|
1539
|
+
maxLabelLen = Math.max(maxLabelLen, label.length);
|
|
1540
|
+
const countStr = `${p.issueCount} issue${p.issueCount !== 1 ? "s" : ""}`;
|
|
1541
|
+
maxCountLen = Math.max(maxCountLen, countStr.length);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
for (const group of summary.groups) {
|
|
1545
|
+
const color = severityColorFn(group.severity);
|
|
1546
|
+
lines.push(color(`${group.severityLabel} \u2014 ${group.categoryLabel}`));
|
|
1547
|
+
const visible = group.patterns.slice(0, MAX_PATTERNS_PER_GROUP);
|
|
1548
|
+
const remaining = group.patterns.length - visible.length;
|
|
1549
|
+
for (const pattern of visible) {
|
|
1550
|
+
const label = `${pattern.title} (${pattern.toolDisplayName})`;
|
|
1551
|
+
const countStr = `${pattern.issueCount} issue${pattern.issueCount !== 1 ? "s" : ""}`;
|
|
1552
|
+
const fileStr = `in ${pattern.fileCount} file${pattern.fileCount !== 1 ? "s" : ""}`;
|
|
1553
|
+
const noisyStr = pattern.noisy ? import_ansis.default.bold.red(" NOISY") : "";
|
|
1554
|
+
lines.push(
|
|
1555
|
+
` ${label.padEnd(maxLabelLen)} ${countStr.padStart(maxCountLen)} ${fileStr}${noisyStr}`
|
|
1556
|
+
);
|
|
1557
|
+
}
|
|
1558
|
+
if (remaining > 0) {
|
|
1559
|
+
lines.push(import_ansis.default.dim(` ... and ${remaining} other pattern${remaining !== 1 ? "s" : ""}`));
|
|
1560
|
+
}
|
|
1561
|
+
lines.push("");
|
|
1562
|
+
}
|
|
1563
|
+
return lines.join("\n");
|
|
1564
|
+
}
|
|
1244
1565
|
|
|
1245
1566
|
// src/init.ts
|
|
1246
|
-
var
|
|
1247
|
-
var
|
|
1567
|
+
var import_promises4 = __toESM(require("fs/promises"));
|
|
1568
|
+
var import_path7 = __toESM(require("path"));
|
|
1248
1569
|
var import_child_process3 = require("child_process");
|
|
1249
1570
|
var import_tooling5 = require("@codacy/tooling");
|
|
1250
1571
|
|
|
1251
|
-
// src/
|
|
1572
|
+
// src/discover.ts
|
|
1252
1573
|
var import_promises2 = __toESM(require("fs/promises"));
|
|
1253
1574
|
var import_path5 = __toESM(require("path"));
|
|
1575
|
+
|
|
1576
|
+
// src/discover-catalog.ts
|
|
1577
|
+
var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
|
|
1578
|
+
// --- JavaScript / TypeScript — Frameworks ---
|
|
1579
|
+
["react", { displayName: "React", kind: "framework" }],
|
|
1580
|
+
["react-dom", { displayName: "React DOM", kind: "framework" }],
|
|
1581
|
+
["react-native", { displayName: "React Native", kind: "framework" }],
|
|
1582
|
+
["vue", { displayName: "Vue.js", kind: "framework" }],
|
|
1583
|
+
["nuxt", { displayName: "Nuxt", kind: "framework" }],
|
|
1584
|
+
["@angular/core", { displayName: "Angular", kind: "framework" }],
|
|
1585
|
+
["svelte", { displayName: "Svelte", kind: "framework" }],
|
|
1586
|
+
["@sveltejs/kit", { displayName: "SvelteKit", kind: "framework" }],
|
|
1587
|
+
["next", { displayName: "Next.js", kind: "framework" }],
|
|
1588
|
+
["ember-source", { displayName: "Ember.js", kind: "framework" }],
|
|
1589
|
+
["lit", { displayName: "Lit", kind: "framework" }],
|
|
1590
|
+
["solid-js", { displayName: "Solid", kind: "framework" }],
|
|
1591
|
+
["astro", { displayName: "Astro", kind: "framework" }],
|
|
1592
|
+
["@builder.io/qwik", { displayName: "Qwik", kind: "framework" }],
|
|
1593
|
+
["@builder.io/qwik-city", { displayName: "Qwik", kind: "framework" }],
|
|
1594
|
+
["@remix-run/react", { displayName: "Remix", kind: "framework" }],
|
|
1595
|
+
["gatsby", { displayName: "Gatsby", kind: "framework" }],
|
|
1596
|
+
["preact", { displayName: "Preact", kind: "framework" }],
|
|
1597
|
+
["express", { displayName: "Express", kind: "framework" }],
|
|
1598
|
+
["@nestjs/core", { displayName: "NestJS", kind: "framework" }],
|
|
1599
|
+
["fastify", { displayName: "Fastify", kind: "framework" }],
|
|
1600
|
+
["koa", { displayName: "Koa", kind: "framework" }],
|
|
1601
|
+
["@hapi/hapi", { displayName: "Hapi", kind: "framework" }],
|
|
1602
|
+
["hono", { displayName: "Hono", kind: "framework" }],
|
|
1603
|
+
["electron", { displayName: "Electron", kind: "framework" }],
|
|
1604
|
+
// --- JavaScript / TypeScript — Libraries ---
|
|
1605
|
+
["lodash", { displayName: "Lodash", kind: "library" }],
|
|
1606
|
+
["underscore", { displayName: "Underscore.js", kind: "library" }],
|
|
1607
|
+
["ramda", { displayName: "Ramda", kind: "library" }],
|
|
1608
|
+
["jest", { displayName: "Jest", kind: "library" }],
|
|
1609
|
+
["vitest", { displayName: "Vitest", kind: "library" }],
|
|
1610
|
+
["@playwright/test", { displayName: "Playwright", kind: "library" }],
|
|
1611
|
+
["playwright", { displayName: "Playwright", kind: "library" }],
|
|
1612
|
+
["cypress", { displayName: "Cypress", kind: "library" }],
|
|
1613
|
+
["mocha", { displayName: "Mocha", kind: "library" }],
|
|
1614
|
+
["ava", { displayName: "AVA", kind: "library" }],
|
|
1615
|
+
["jasmine", { displayName: "Jasmine", kind: "library" }],
|
|
1616
|
+
["jasmine-core", { displayName: "Jasmine", kind: "library" }],
|
|
1617
|
+
["@testing-library/react", { displayName: "Testing Library", kind: "library" }],
|
|
1618
|
+
["@testing-library/vue", { displayName: "Testing Library", kind: "library" }],
|
|
1619
|
+
["@testing-library/dom", { displayName: "Testing Library", kind: "library" }],
|
|
1620
|
+
["tailwindcss", { displayName: "Tailwind CSS", kind: "library" }],
|
|
1621
|
+
["@storybook/react", { displayName: "Storybook", kind: "library" }],
|
|
1622
|
+
["@storybook/vue3", { displayName: "Storybook", kind: "library" }],
|
|
1623
|
+
["@storybook/svelte", { displayName: "Storybook", kind: "library" }],
|
|
1624
|
+
["redux", { displayName: "Redux", kind: "library" }],
|
|
1625
|
+
["@reduxjs/toolkit", { displayName: "Redux Toolkit", kind: "library" }],
|
|
1626
|
+
["mobx", { displayName: "MobX", kind: "library" }],
|
|
1627
|
+
["zustand", { displayName: "Zustand", kind: "library" }],
|
|
1628
|
+
["axios", { displayName: "Axios", kind: "library" }],
|
|
1629
|
+
["date-fns", { displayName: "date-fns", kind: "library" }],
|
|
1630
|
+
["dayjs", { displayName: "Day.js", kind: "library" }],
|
|
1631
|
+
["moment", { displayName: "Moment.js", kind: "library" }],
|
|
1632
|
+
["rxjs", { displayName: "RxJS", kind: "library" }],
|
|
1633
|
+
["zod", { displayName: "Zod", kind: "library" }],
|
|
1634
|
+
["joi", { displayName: "Joi", kind: "library" }],
|
|
1635
|
+
["yup", { displayName: "Yup", kind: "library" }],
|
|
1636
|
+
["prisma", { displayName: "Prisma", kind: "library" }],
|
|
1637
|
+
["@prisma/client", { displayName: "Prisma", kind: "library" }],
|
|
1638
|
+
["typeorm", { displayName: "TypeORM", kind: "library" }],
|
|
1639
|
+
["sequelize", { displayName: "Sequelize", kind: "library" }],
|
|
1640
|
+
["drizzle-orm", { displayName: "Drizzle ORM", kind: "library" }],
|
|
1641
|
+
["mongoose", { displayName: "Mongoose", kind: "library" }],
|
|
1642
|
+
["graphql", { displayName: "GraphQL", kind: "library" }],
|
|
1643
|
+
["@apollo/client", { displayName: "Apollo Client", kind: "library" }],
|
|
1644
|
+
["socket.io", { displayName: "Socket.IO", kind: "library" }],
|
|
1645
|
+
["three", { displayName: "Three.js", kind: "library" }],
|
|
1646
|
+
["d3", { displayName: "D3.js", kind: "library" }],
|
|
1647
|
+
["chart.js", { displayName: "Chart.js", kind: "library" }],
|
|
1648
|
+
["sass", { displayName: "Sass", kind: "library" }],
|
|
1649
|
+
["node-sass", { displayName: "Sass", kind: "library" }],
|
|
1650
|
+
["less", { displayName: "Less", kind: "library" }],
|
|
1651
|
+
["styled-components", { displayName: "styled-components", kind: "library" }],
|
|
1652
|
+
["@emotion/react", { displayName: "Emotion", kind: "library" }],
|
|
1653
|
+
["pino", { displayName: "Pino", kind: "library" }],
|
|
1654
|
+
["winston", { displayName: "Winston", kind: "library" }],
|
|
1655
|
+
["i18next", { displayName: "i18next", kind: "library" }],
|
|
1656
|
+
["react-intl", { displayName: "react-intl", kind: "library" }],
|
|
1657
|
+
["vue-i18n", { displayName: "vue-i18n", kind: "library" }],
|
|
1658
|
+
["next-intl", { displayName: "next-intl", kind: "library" }],
|
|
1659
|
+
// --- JavaScript / TypeScript — Tools ---
|
|
1660
|
+
["typescript", { displayName: "TypeScript", kind: "tool" }],
|
|
1661
|
+
["eslint", { displayName: "ESLint", kind: "tool" }],
|
|
1662
|
+
["prettier", { displayName: "Prettier", kind: "tool" }],
|
|
1663
|
+
["webpack", { displayName: "Webpack", kind: "tool" }],
|
|
1664
|
+
["vite", { displayName: "Vite", kind: "tool" }],
|
|
1665
|
+
["esbuild", { displayName: "esbuild", kind: "tool" }],
|
|
1666
|
+
["rollup", { displayName: "Rollup", kind: "tool" }],
|
|
1667
|
+
["turbo", { displayName: "Turborepo", kind: "tool" }],
|
|
1668
|
+
["tsup", { displayName: "tsup", kind: "tool" }],
|
|
1669
|
+
["@biomejs/biome", { displayName: "Biome", kind: "tool" }],
|
|
1670
|
+
["stylelint", { displayName: "Stylelint", kind: "tool" }],
|
|
1671
|
+
// --- JavaScript / TypeScript — Platforms ---
|
|
1672
|
+
["@lwc/engine-dom", { displayName: "Lightning Web Components", kind: "platform" }],
|
|
1673
|
+
["@salesforce/core", { displayName: "Salesforce", kind: "platform" }],
|
|
1674
|
+
["@shopify/polaris", { displayName: "Shopify Polaris", kind: "platform" }],
|
|
1675
|
+
// --- Python — Frameworks ---
|
|
1676
|
+
["django", { displayName: "Django", kind: "framework" }],
|
|
1677
|
+
["flask", { displayName: "Flask", kind: "framework" }],
|
|
1678
|
+
["fastapi", { displayName: "FastAPI", kind: "framework" }],
|
|
1679
|
+
["tornado", { displayName: "Tornado", kind: "framework" }],
|
|
1680
|
+
["pyramid", { displayName: "Pyramid", kind: "framework" }],
|
|
1681
|
+
["starlette", { displayName: "Starlette", kind: "framework" }],
|
|
1682
|
+
["sanic", { displayName: "Sanic", kind: "framework" }],
|
|
1683
|
+
["bottle", { displayName: "Bottle", kind: "framework" }],
|
|
1684
|
+
["streamlit", { displayName: "Streamlit", kind: "framework" }],
|
|
1685
|
+
["gradio", { displayName: "Gradio", kind: "framework" }],
|
|
1686
|
+
["apache-airflow", { displayName: "Airflow", kind: "framework" }],
|
|
1687
|
+
// --- Python — Libraries ---
|
|
1688
|
+
["flask-babel", { displayName: "Flask-Babel", kind: "library" }],
|
|
1689
|
+
["pytest", { displayName: "pytest", kind: "library" }],
|
|
1690
|
+
["numpy", { displayName: "NumPy", kind: "library" }],
|
|
1691
|
+
["pandas", { displayName: "pandas", kind: "library" }],
|
|
1692
|
+
["requests", { displayName: "Requests", kind: "library" }],
|
|
1693
|
+
["sqlalchemy", { displayName: "SQLAlchemy", kind: "library" }],
|
|
1694
|
+
["celery", { displayName: "Celery", kind: "library" }],
|
|
1695
|
+
["pydantic", { displayName: "Pydantic", kind: "library" }],
|
|
1696
|
+
["boto3", { displayName: "Boto3 (AWS SDK)", kind: "library" }],
|
|
1697
|
+
["scipy", { displayName: "SciPy", kind: "library" }],
|
|
1698
|
+
["scikit-learn", { displayName: "scikit-learn", kind: "library" }],
|
|
1699
|
+
["tensorflow", { displayName: "TensorFlow", kind: "library" }],
|
|
1700
|
+
["torch", { displayName: "PyTorch", kind: "library" }],
|
|
1701
|
+
["httpx", { displayName: "HTTPX", kind: "library" }],
|
|
1702
|
+
["aiohttp", { displayName: "aiohttp", kind: "library" }],
|
|
1703
|
+
["pillow", { displayName: "Pillow", kind: "library" }],
|
|
1704
|
+
["beautifulsoup4", { displayName: "Beautiful Soup", kind: "library" }],
|
|
1705
|
+
["scrapy", { displayName: "Scrapy", kind: "library" }],
|
|
1706
|
+
["alembic", { displayName: "Alembic", kind: "library" }],
|
|
1707
|
+
["marshmallow", { displayName: "Marshmallow", kind: "library" }],
|
|
1708
|
+
["click", { displayName: "Click", kind: "library" }],
|
|
1709
|
+
["typer", { displayName: "Typer", kind: "library" }],
|
|
1710
|
+
["rich", { displayName: "Rich", kind: "library" }],
|
|
1711
|
+
["polars", { displayName: "Polars", kind: "library" }],
|
|
1712
|
+
["dask", { displayName: "Dask", kind: "library" }],
|
|
1713
|
+
["matplotlib", { displayName: "Matplotlib", kind: "library" }],
|
|
1714
|
+
["seaborn", { displayName: "Seaborn", kind: "library" }],
|
|
1715
|
+
["plotly", { displayName: "Plotly", kind: "library" }],
|
|
1716
|
+
// --- Python — Tools ---
|
|
1717
|
+
["black", { displayName: "Black", kind: "tool" }],
|
|
1718
|
+
["ruff", { displayName: "Ruff", kind: "tool" }],
|
|
1719
|
+
["mypy", { displayName: "mypy", kind: "tool" }],
|
|
1720
|
+
["pylint", { displayName: "Pylint", kind: "tool" }],
|
|
1721
|
+
["flake8", { displayName: "Flake8", kind: "tool" }],
|
|
1722
|
+
["bandit", { displayName: "Bandit", kind: "tool" }],
|
|
1723
|
+
["isort", { displayName: "isort", kind: "tool" }],
|
|
1724
|
+
// --- Ruby — Frameworks ---
|
|
1725
|
+
["rails", { displayName: "Ruby on Rails", kind: "framework" }],
|
|
1726
|
+
["sinatra", { displayName: "Sinatra", kind: "framework" }],
|
|
1727
|
+
["hanami", { displayName: "Hanami", kind: "framework" }],
|
|
1728
|
+
["grape", { displayName: "Grape", kind: "framework" }],
|
|
1729
|
+
["padrino", { displayName: "Padrino", kind: "framework" }],
|
|
1730
|
+
// --- Ruby — Libraries ---
|
|
1731
|
+
["rspec", { displayName: "RSpec", kind: "library" }],
|
|
1732
|
+
["rspec-core", { displayName: "RSpec", kind: "library" }],
|
|
1733
|
+
["rspec-rails", { displayName: "RSpec Rails", kind: "library" }],
|
|
1734
|
+
["capybara", { displayName: "Capybara", kind: "library" }],
|
|
1735
|
+
["sidekiq", { displayName: "Sidekiq", kind: "library" }],
|
|
1736
|
+
["devise", { displayName: "Devise", kind: "library" }],
|
|
1737
|
+
["pundit", { displayName: "Pundit", kind: "library" }],
|
|
1738
|
+
["activeadmin", { displayName: "ActiveAdmin", kind: "library" }],
|
|
1739
|
+
["puma", { displayName: "Puma", kind: "library" }],
|
|
1740
|
+
["nokogiri", { displayName: "Nokogiri", kind: "library" }],
|
|
1741
|
+
["faraday", { displayName: "Faraday", kind: "library" }],
|
|
1742
|
+
["httparty", { displayName: "HTTParty", kind: "library" }],
|
|
1743
|
+
["minitest", { displayName: "Minitest", kind: "library" }],
|
|
1744
|
+
["factory_bot", { displayName: "FactoryBot", kind: "library" }],
|
|
1745
|
+
["dry-validation", { displayName: "dry-validation", kind: "library" }],
|
|
1746
|
+
// --- Ruby — Tools ---
|
|
1747
|
+
["rubocop", { displayName: "RuboCop", kind: "tool" }],
|
|
1748
|
+
["bundler", { displayName: "Bundler", kind: "tool" }],
|
|
1749
|
+
["solargraph", { displayName: "Solargraph", kind: "tool" }],
|
|
1750
|
+
// --- Java / Kotlin — Frameworks ---
|
|
1751
|
+
["spring-boot-starter", { displayName: "Spring Boot", kind: "framework" }],
|
|
1752
|
+
["spring-boot-starter-web", { displayName: "Spring Boot", kind: "framework" }],
|
|
1753
|
+
["spring-core", { displayName: "Spring Framework", kind: "framework" }],
|
|
1754
|
+
["quarkus-core", { displayName: "Quarkus", kind: "framework" }],
|
|
1755
|
+
["micronaut-core", { displayName: "Micronaut", kind: "framework" }],
|
|
1756
|
+
["ktor-server-core", { displayName: "Ktor", kind: "framework" }],
|
|
1757
|
+
["android", { displayName: "Android SDK", kind: "framework" }],
|
|
1758
|
+
// --- Java / Kotlin — Libraries ---
|
|
1759
|
+
["junit-jupiter", { displayName: "JUnit 5", kind: "library" }],
|
|
1760
|
+
["junit", { displayName: "JUnit", kind: "library" }],
|
|
1761
|
+
["junit-jupiter-api", { displayName: "JUnit 5", kind: "library" }],
|
|
1762
|
+
["mockito-core", { displayName: "Mockito", kind: "library" }],
|
|
1763
|
+
["lombok", { displayName: "Lombok", kind: "library" }],
|
|
1764
|
+
["jackson-databind", { displayName: "Jackson", kind: "library" }],
|
|
1765
|
+
["jackson-core", { displayName: "Jackson", kind: "library" }],
|
|
1766
|
+
["hibernate-core", { displayName: "Hibernate", kind: "library" }],
|
|
1767
|
+
["guava", { displayName: "Guava", kind: "library" }],
|
|
1768
|
+
["commons-lang3", { displayName: "Apache Commons Lang", kind: "library" }],
|
|
1769
|
+
["commons-io", { displayName: "Apache Commons IO", kind: "library" }],
|
|
1770
|
+
["slf4j-api", { displayName: "SLF4J", kind: "library" }],
|
|
1771
|
+
["log4j-core", { displayName: "Log4j", kind: "library" }],
|
|
1772
|
+
["logback-classic", { displayName: "Logback", kind: "library" }],
|
|
1773
|
+
["testng", { displayName: "TestNG", kind: "library" }],
|
|
1774
|
+
["assertj-core", { displayName: "AssertJ", kind: "library" }],
|
|
1775
|
+
["gson", { displayName: "Gson", kind: "library" }],
|
|
1776
|
+
["okhttp", { displayName: "OkHttp", kind: "library" }],
|
|
1777
|
+
["retrofit", { displayName: "Retrofit", kind: "library" }],
|
|
1778
|
+
// --- Go — Frameworks ---
|
|
1779
|
+
["github.com/gin-gonic/gin", { displayName: "Gin", kind: "framework" }],
|
|
1780
|
+
["github.com/labstack/echo", { displayName: "Echo", kind: "framework" }],
|
|
1781
|
+
["github.com/gofiber/fiber", { displayName: "Fiber", kind: "framework" }],
|
|
1782
|
+
["github.com/go-chi/chi", { displayName: "Chi", kind: "framework" }],
|
|
1783
|
+
["github.com/gorilla/mux", { displayName: "Gorilla Mux", kind: "framework" }],
|
|
1784
|
+
["github.com/beego/beego", { displayName: "Beego", kind: "framework" }],
|
|
1785
|
+
// --- Go — Libraries ---
|
|
1786
|
+
["github.com/stretchr/testify", { displayName: "Testify", kind: "library" }],
|
|
1787
|
+
["github.com/spf13/cobra", { displayName: "Cobra", kind: "library" }],
|
|
1788
|
+
["github.com/spf13/viper", { displayName: "Viper", kind: "library" }],
|
|
1789
|
+
["go.uber.org/zap", { displayName: "Zap", kind: "library" }],
|
|
1790
|
+
["github.com/sirupsen/logrus", { displayName: "Logrus", kind: "library" }],
|
|
1791
|
+
["gorm.io/gorm", { displayName: "GORM", kind: "library" }],
|
|
1792
|
+
["github.com/go-kit/kit", { displayName: "Go Kit", kind: "library" }],
|
|
1793
|
+
["google.golang.org/protobuf", { displayName: "Protocol Buffers", kind: "library" }],
|
|
1794
|
+
["google.golang.org/grpc", { displayName: "gRPC", kind: "library" }],
|
|
1795
|
+
["github.com/jackc/pgx", { displayName: "pgx", kind: "library" }],
|
|
1796
|
+
["github.com/redis/go-redis", { displayName: "go-redis", kind: "library" }],
|
|
1797
|
+
["github.com/aws/aws-sdk-go-v2", { displayName: "AWS SDK for Go", kind: "library" }],
|
|
1798
|
+
["github.com/prometheus/client_golang", { displayName: "Prometheus Client", kind: "library" }],
|
|
1799
|
+
// --- Rust — Frameworks ---
|
|
1800
|
+
["actix-web", { displayName: "Actix Web", kind: "framework" }],
|
|
1801
|
+
["axum", { displayName: "Axum", kind: "framework" }],
|
|
1802
|
+
["rocket", { displayName: "Rocket", kind: "framework" }],
|
|
1803
|
+
["warp", { displayName: "Warp", kind: "framework" }],
|
|
1804
|
+
["tauri", { displayName: "Tauri", kind: "framework" }],
|
|
1805
|
+
// --- Rust — Libraries ---
|
|
1806
|
+
["tokio", { displayName: "Tokio", kind: "library" }],
|
|
1807
|
+
["serde", { displayName: "Serde", kind: "library" }],
|
|
1808
|
+
["clap", { displayName: "clap", kind: "library" }],
|
|
1809
|
+
["reqwest", { displayName: "reqwest", kind: "library" }],
|
|
1810
|
+
["diesel", { displayName: "Diesel", kind: "library" }],
|
|
1811
|
+
["sqlx", { displayName: "SQLx", kind: "library" }],
|
|
1812
|
+
["tracing", { displayName: "Tracing", kind: "library" }],
|
|
1813
|
+
["anyhow", { displayName: "anyhow", kind: "library" }],
|
|
1814
|
+
["thiserror", { displayName: "thiserror", kind: "library" }],
|
|
1815
|
+
["rayon", { displayName: "Rayon", kind: "library" }],
|
|
1816
|
+
// --- PHP — Frameworks ---
|
|
1817
|
+
["laravel/framework", { displayName: "Laravel", kind: "framework" }],
|
|
1818
|
+
["symfony/framework-bundle", { displayName: "Symfony", kind: "framework" }],
|
|
1819
|
+
["codeigniter4/framework", { displayName: "CodeIgniter", kind: "framework" }],
|
|
1820
|
+
["slim/slim", { displayName: "Slim", kind: "framework" }],
|
|
1821
|
+
["cakephp/cakephp", { displayName: "CakePHP", kind: "framework" }],
|
|
1822
|
+
// --- PHP — Libraries ---
|
|
1823
|
+
["phpunit/phpunit", { displayName: "PHPUnit", kind: "library" }],
|
|
1824
|
+
["guzzlehttp/guzzle", { displayName: "Guzzle", kind: "library" }],
|
|
1825
|
+
["monolog/monolog", { displayName: "Monolog", kind: "library" }],
|
|
1826
|
+
["nesbot/carbon", { displayName: "Carbon", kind: "library" }],
|
|
1827
|
+
["doctrine/orm", { displayName: "Doctrine ORM", kind: "library" }],
|
|
1828
|
+
["doctrine/dbal", { displayName: "Doctrine DBAL", kind: "library" }],
|
|
1829
|
+
["league/flysystem", { displayName: "Flysystem", kind: "library" }],
|
|
1830
|
+
["php-amqplib/php-amqplib", { displayName: "php-amqplib", kind: "library" }],
|
|
1831
|
+
["predis/predis", { displayName: "Predis", kind: "library" }],
|
|
1832
|
+
// --- PHP — Tools ---
|
|
1833
|
+
["phpstan/phpstan", { displayName: "PHPStan", kind: "tool" }],
|
|
1834
|
+
["squizlabs/php_codesniffer", { displayName: "PHP_CodeSniffer", kind: "tool" }],
|
|
1835
|
+
// --- Swift — Frameworks ---
|
|
1836
|
+
["vapor", { displayName: "Vapor", kind: "framework" }],
|
|
1837
|
+
// --- Swift — Libraries ---
|
|
1838
|
+
["Alamofire", { displayName: "Alamofire", kind: "library" }],
|
|
1839
|
+
["SnapKit", { displayName: "SnapKit", kind: "library" }],
|
|
1840
|
+
["Kingfisher", { displayName: "Kingfisher", kind: "library" }],
|
|
1841
|
+
["Moya", { displayName: "Moya", kind: "library" }],
|
|
1842
|
+
["RxSwift", { displayName: "RxSwift", kind: "library" }],
|
|
1843
|
+
["RealmSwift", { displayName: "Realm", kind: "library" }],
|
|
1844
|
+
// --- Scala — Frameworks ---
|
|
1845
|
+
["play-server", { displayName: "Play Framework", kind: "framework" }],
|
|
1846
|
+
["akka-actor", { displayName: "Akka", kind: "framework" }],
|
|
1847
|
+
// --- Scala — Libraries ---
|
|
1848
|
+
["scalatest", { displayName: "ScalaTest", kind: "library" }],
|
|
1849
|
+
["specs2-core", { displayName: "Specs2", kind: "library" }],
|
|
1850
|
+
["cats-core", { displayName: "Cats", kind: "library" }],
|
|
1851
|
+
["circe-core", { displayName: "Circe", kind: "library" }]
|
|
1852
|
+
]);
|
|
1853
|
+
var DEPENDENCY_PREFIX_RULES = [
|
|
1854
|
+
{ prefix: "@angular/", classification: { displayName: "Angular", kind: "framework" } },
|
|
1855
|
+
{ prefix: "@nestjs/", classification: { displayName: "NestJS", kind: "framework" } },
|
|
1856
|
+
{ prefix: "@remix-run/", classification: { displayName: "Remix", kind: "framework" } },
|
|
1857
|
+
{ prefix: "@sveltejs/", classification: { displayName: "SvelteKit", kind: "framework" } },
|
|
1858
|
+
{ prefix: "@nuxt/", classification: { displayName: "Nuxt", kind: "framework" } },
|
|
1859
|
+
{ prefix: "@storybook/", classification: { displayName: "Storybook", kind: "library" } },
|
|
1860
|
+
{
|
|
1861
|
+
prefix: "@testing-library/",
|
|
1862
|
+
classification: { displayName: "Testing Library", kind: "library" }
|
|
1863
|
+
},
|
|
1864
|
+
{ prefix: "@tanstack/", classification: { displayName: "TanStack", kind: "library" } },
|
|
1865
|
+
{ prefix: "@reduxjs/", classification: { displayName: "Redux", kind: "library" } },
|
|
1866
|
+
{ prefix: "@prisma/", classification: { displayName: "Prisma", kind: "library" } },
|
|
1867
|
+
{ prefix: "@apollo/", classification: { displayName: "Apollo", kind: "library" } },
|
|
1868
|
+
{ prefix: "@formatjs/", classification: { displayName: "react-intl", kind: "library" } },
|
|
1869
|
+
{ prefix: "@lingui/", classification: { displayName: "Lingui", kind: "library" } },
|
|
1870
|
+
{ prefix: "@emotion/", classification: { displayName: "Emotion", kind: "library" } },
|
|
1871
|
+
{ prefix: "@mui/", classification: { displayName: "Material UI", kind: "library" } },
|
|
1872
|
+
{ prefix: "@chakra-ui/", classification: { displayName: "Chakra UI", kind: "library" } },
|
|
1873
|
+
{
|
|
1874
|
+
prefix: "@lwc/",
|
|
1875
|
+
classification: { displayName: "Lightning Web Components", kind: "platform" }
|
|
1876
|
+
},
|
|
1877
|
+
{ prefix: "@salesforce/", classification: { displayName: "Salesforce", kind: "platform" } },
|
|
1878
|
+
{ prefix: "@shopify/", classification: { displayName: "Shopify", kind: "platform" } },
|
|
1879
|
+
{ prefix: "@aws-sdk/", classification: { displayName: "AWS SDK", kind: "library" } },
|
|
1880
|
+
{ prefix: "@google-cloud/", classification: { displayName: "Google Cloud", kind: "library" } },
|
|
1881
|
+
{ prefix: "@azure/", classification: { displayName: "Azure SDK", kind: "library" } }
|
|
1882
|
+
];
|
|
1883
|
+
function classifyDependency(name) {
|
|
1884
|
+
const exact = DEPENDENCY_CATALOG.get(name);
|
|
1885
|
+
if (exact) return exact;
|
|
1886
|
+
for (const rule of DEPENDENCY_PREFIX_RULES) {
|
|
1887
|
+
if (name.startsWith(rule.prefix)) return rule.classification;
|
|
1888
|
+
}
|
|
1889
|
+
return void 0;
|
|
1890
|
+
}
|
|
1891
|
+
var NOTABLE_ANY_DEPTH = /* @__PURE__ */ new Set([
|
|
1892
|
+
"Dockerfile",
|
|
1893
|
+
"docker-compose.yml",
|
|
1894
|
+
"docker-compose.yaml",
|
|
1895
|
+
"compose.yml",
|
|
1896
|
+
"compose.yaml",
|
|
1897
|
+
"Makefile",
|
|
1898
|
+
"Justfile",
|
|
1899
|
+
"pom.xml",
|
|
1900
|
+
"build.gradle",
|
|
1901
|
+
"build.gradle.kts",
|
|
1902
|
+
"build.sbt",
|
|
1903
|
+
"go.mod",
|
|
1904
|
+
"Cargo.toml",
|
|
1905
|
+
"Gemfile",
|
|
1906
|
+
"Podfile",
|
|
1907
|
+
"Package.swift",
|
|
1908
|
+
"Jenkinsfile",
|
|
1909
|
+
"Vagrantfile",
|
|
1910
|
+
"Procfile"
|
|
1911
|
+
]);
|
|
1912
|
+
var NOTABLE_ROOT_ONLY = /* @__PURE__ */ new Set([
|
|
1913
|
+
".dockerignore",
|
|
1914
|
+
"Taskfile.yml",
|
|
1915
|
+
"Rakefile",
|
|
1916
|
+
"Gruntfile.js",
|
|
1917
|
+
"Gulpfile.js",
|
|
1918
|
+
"settings.gradle",
|
|
1919
|
+
"settings.gradle.kts",
|
|
1920
|
+
"go.sum",
|
|
1921
|
+
"Cargo.lock",
|
|
1922
|
+
"CLAUDE.md",
|
|
1923
|
+
"AGENTS.md",
|
|
1924
|
+
"agents.md",
|
|
1925
|
+
".cursorrules",
|
|
1926
|
+
"copilot-instructions.md",
|
|
1927
|
+
".travis.yml",
|
|
1928
|
+
".gitlab-ci.yml",
|
|
1929
|
+
"azure-pipelines.yml",
|
|
1930
|
+
"bitbucket-pipelines.yml",
|
|
1931
|
+
"appveyor.yml",
|
|
1932
|
+
"package.json",
|
|
1933
|
+
"requirements.txt",
|
|
1934
|
+
"pyproject.toml",
|
|
1935
|
+
"Pipfile",
|
|
1936
|
+
"composer.json",
|
|
1937
|
+
"tsconfig.json",
|
|
1938
|
+
"jsconfig.json",
|
|
1939
|
+
"serverless.yml",
|
|
1940
|
+
"serverless.yaml",
|
|
1941
|
+
"cdk.json",
|
|
1942
|
+
"pulumi.yaml",
|
|
1943
|
+
"README.md",
|
|
1944
|
+
"CHANGELOG.md",
|
|
1945
|
+
"CONTRIBUTING.md",
|
|
1946
|
+
"LICENSE",
|
|
1947
|
+
"renovate.json",
|
|
1948
|
+
".nvmrc",
|
|
1949
|
+
".node-version",
|
|
1950
|
+
".python-version",
|
|
1951
|
+
".ruby-version",
|
|
1952
|
+
".tool-versions"
|
|
1953
|
+
]);
|
|
1954
|
+
var NOTABLE_PATH_PREFIXES = [".github/workflows/", ".circleci/", ".gitlab/"];
|
|
1955
|
+
var CONFIG_HEURISTIC_PATTERNS = [
|
|
1956
|
+
/^\..*rc$/,
|
|
1957
|
+
// .eslintrc, .prettierrc, etc.
|
|
1958
|
+
/^\..*rc\.(json|js|cjs|mjs|yml|yaml|ts)$/,
|
|
1959
|
+
// .eslintrc.json, etc.
|
|
1960
|
+
/^.*\.config\.(js|ts|mjs|cjs)$/,
|
|
1961
|
+
// vite.config.ts, jest.config.js, etc.
|
|
1962
|
+
/^\.env(\..+)?$/
|
|
1963
|
+
// .env, .env.local, .env.production
|
|
1964
|
+
];
|
|
1965
|
+
function isNotableFile(filePath) {
|
|
1966
|
+
const basename = filePath.split("/").pop() ?? filePath;
|
|
1967
|
+
const isRoot = !filePath.includes("/");
|
|
1968
|
+
if (NOTABLE_ANY_DEPTH.has(basename)) return true;
|
|
1969
|
+
if (isRoot && NOTABLE_ROOT_ONLY.has(basename)) return true;
|
|
1970
|
+
for (const prefix of NOTABLE_PATH_PREFIXES) {
|
|
1971
|
+
if (filePath.startsWith(prefix)) return true;
|
|
1972
|
+
}
|
|
1973
|
+
if (!filePath.includes("/")) {
|
|
1974
|
+
for (const pattern of CONFIG_HEURISTIC_PATTERNS) {
|
|
1975
|
+
if (pattern.test(basename)) return true;
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
return false;
|
|
1979
|
+
}
|
|
1980
|
+
function parsePackageJson(content) {
|
|
1981
|
+
try {
|
|
1982
|
+
const pkg = JSON.parse(content);
|
|
1983
|
+
const deps = [];
|
|
1984
|
+
for (const section of ["dependencies", "devDependencies", "peerDependencies"]) {
|
|
1985
|
+
const entries2 = pkg[section];
|
|
1986
|
+
if (entries2 && typeof entries2 === "object") {
|
|
1987
|
+
for (const [name, version] of Object.entries(entries2)) {
|
|
1988
|
+
deps.push({ name, version: typeof version === "string" ? version : void 0 });
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
return deps;
|
|
1993
|
+
} catch {
|
|
1994
|
+
return [];
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
function parseRequirementsTxt(content) {
|
|
1998
|
+
const deps = [];
|
|
1999
|
+
for (const rawLine of content.split("\n")) {
|
|
2000
|
+
const line = rawLine.trim();
|
|
2001
|
+
if (!line || line.startsWith("#") || line.startsWith("-")) continue;
|
|
2002
|
+
const noComment = line.split("#")[0].trim();
|
|
2003
|
+
if (!noComment) continue;
|
|
2004
|
+
const match = noComment.match(/^([a-zA-Z0-9_.-]+(?:\[[^\]]*\])?)\s*([<>=!~@;].*)?$/);
|
|
2005
|
+
if (match) {
|
|
2006
|
+
const rawName = match[1].replace(/\[.*\]$/, "");
|
|
2007
|
+
const normalized = rawName.toLowerCase().replace(/_/g, "-");
|
|
2008
|
+
deps.push({ name: normalized, version: match[2]?.trim() });
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
return deps;
|
|
2012
|
+
}
|
|
2013
|
+
function parsePyprojectToml(content) {
|
|
2014
|
+
const deps = [];
|
|
2015
|
+
const projectDepsMatch = content.match(/\[project\][\s\S]*?dependencies\s*=\s*\[([\s\S]*?)\]/);
|
|
2016
|
+
if (projectDepsMatch) {
|
|
2017
|
+
const items = projectDepsMatch[1].match(/"([^"]+)"|'([^']+)'/g) ?? [];
|
|
2018
|
+
for (const item of items) {
|
|
2019
|
+
const raw = item.replace(/["']/g, "");
|
|
2020
|
+
const name = raw.split(/[<>=!~;\s]/)[0].replace(/\[.*\]$/, "");
|
|
2021
|
+
if (name) deps.push({ name: name.toLowerCase().replace(/_/g, "-") });
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
const poetryMatch = content.match(/\[tool\.poetry\.dependencies\]([\s\S]*?)(?:\n\[|\n$)/);
|
|
2025
|
+
if (poetryMatch) {
|
|
2026
|
+
const lines = poetryMatch[1].split("\n");
|
|
2027
|
+
for (const line of lines) {
|
|
2028
|
+
const m = line.match(/^([a-zA-Z0-9_.-]+)\s*=/);
|
|
2029
|
+
if (m && m[1] !== "python") {
|
|
2030
|
+
deps.push({ name: m[1].toLowerCase().replace(/_/g, "-") });
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
const poetryGroupRegex = /\[tool\.poetry\.group\.\w+\.dependencies\]([\s\S]*?)(?:\n\[|\n$)/g;
|
|
2035
|
+
let groupMatch;
|
|
2036
|
+
while ((groupMatch = poetryGroupRegex.exec(content)) !== null) {
|
|
2037
|
+
const lines = groupMatch[1].split("\n");
|
|
2038
|
+
for (const line of lines) {
|
|
2039
|
+
const m = line.match(/^([a-zA-Z0-9_.-]+)\s*=/);
|
|
2040
|
+
if (m) {
|
|
2041
|
+
deps.push({ name: m[1].toLowerCase().replace(/_/g, "-") });
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
return deps;
|
|
2046
|
+
}
|
|
2047
|
+
function parsePipfile(content) {
|
|
2048
|
+
const deps = [];
|
|
2049
|
+
let inDepsSection = false;
|
|
2050
|
+
for (const rawLine of content.split("\n")) {
|
|
2051
|
+
const line = rawLine.trim();
|
|
2052
|
+
if (/^\[(packages|dev-packages)\]/.test(line)) {
|
|
2053
|
+
inDepsSection = true;
|
|
2054
|
+
continue;
|
|
2055
|
+
}
|
|
2056
|
+
if (line.startsWith("[")) {
|
|
2057
|
+
inDepsSection = false;
|
|
2058
|
+
continue;
|
|
2059
|
+
}
|
|
2060
|
+
if (inDepsSection) {
|
|
2061
|
+
const m = line.match(/^([a-zA-Z0-9_.-]+)\s*=/);
|
|
2062
|
+
if (m) {
|
|
2063
|
+
deps.push({ name: m[1].toLowerCase().replace(/_/g, "-") });
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
return deps;
|
|
2068
|
+
}
|
|
2069
|
+
function parseGemfile(content) {
|
|
2070
|
+
const deps = [];
|
|
2071
|
+
const gemRegex = /^\s*gem\s+["']([^"']+)["'](?:\s*,\s*["']([^"']+)["'])?/gm;
|
|
2072
|
+
let match;
|
|
2073
|
+
while ((match = gemRegex.exec(content)) !== null) {
|
|
2074
|
+
deps.push({ name: match[1], version: match[2] });
|
|
2075
|
+
}
|
|
2076
|
+
return deps;
|
|
2077
|
+
}
|
|
2078
|
+
function parseGoMod(content) {
|
|
2079
|
+
const deps = [];
|
|
2080
|
+
const singleRegex = /^require\s+(\S+)\s+(\S+)/gm;
|
|
2081
|
+
let match;
|
|
2082
|
+
while ((match = singleRegex.exec(content)) !== null) {
|
|
2083
|
+
if (!match[1].startsWith("(")) {
|
|
2084
|
+
deps.push({ name: match[1], version: match[2] });
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
const blockRegex = /require\s*\(([\s\S]*?)\)/g;
|
|
2088
|
+
while ((match = blockRegex.exec(content)) !== null) {
|
|
2089
|
+
const lines = match[1].split("\n");
|
|
2090
|
+
for (const line of lines) {
|
|
2091
|
+
const m = line.trim().match(/^(\S+)\s+(\S+)/);
|
|
2092
|
+
if (m && !m[1].startsWith("//")) {
|
|
2093
|
+
deps.push({ name: m[1], version: m[2] });
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
return deps;
|
|
2098
|
+
}
|
|
2099
|
+
function parsePomXml(content) {
|
|
2100
|
+
const deps = [];
|
|
2101
|
+
const depRegex = /<dependency>[\s\S]*?<artifactId>([^<]+)<\/artifactId>[\s\S]*?<\/dependency>/g;
|
|
2102
|
+
let match;
|
|
2103
|
+
while ((match = depRegex.exec(content)) !== null) {
|
|
2104
|
+
deps.push({ name: match[1].trim() });
|
|
2105
|
+
}
|
|
2106
|
+
return deps;
|
|
2107
|
+
}
|
|
2108
|
+
function parseBuildGradle(content) {
|
|
2109
|
+
const deps = [];
|
|
2110
|
+
const depRegex = /(?:implementation|testImplementation|api|compileOnly|runtimeOnly|kapt|annotationProcessor)\s*(?:\(\s*)?["']([^"':]+):([^"':]+)(?::([^"']+))?["']/g;
|
|
2111
|
+
let match;
|
|
2112
|
+
while ((match = depRegex.exec(content)) !== null) {
|
|
2113
|
+
deps.push({ name: match[2].trim(), version: match[3]?.trim() });
|
|
2114
|
+
}
|
|
2115
|
+
return deps;
|
|
2116
|
+
}
|
|
2117
|
+
function parseCargoToml(content) {
|
|
2118
|
+
const deps = [];
|
|
2119
|
+
let inDepsSection = false;
|
|
2120
|
+
for (const rawLine of content.split("\n")) {
|
|
2121
|
+
const line = rawLine.trim();
|
|
2122
|
+
if (/^\[((?:dev-|build-)?dependencies)\]/.test(line)) {
|
|
2123
|
+
inDepsSection = true;
|
|
2124
|
+
continue;
|
|
2125
|
+
}
|
|
2126
|
+
if (line.startsWith("[") && !line.includes("dependencies.")) {
|
|
2127
|
+
inDepsSection = false;
|
|
2128
|
+
continue;
|
|
2129
|
+
}
|
|
2130
|
+
if (inDepsSection) {
|
|
2131
|
+
const m = line.match(/^([a-zA-Z0-9_-]+)\s*=/);
|
|
2132
|
+
if (m) {
|
|
2133
|
+
const versionMatch = line.match(/["']([^"']+)["']/);
|
|
2134
|
+
deps.push({ name: m[1], version: versionMatch?.[1] });
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
return deps;
|
|
2139
|
+
}
|
|
2140
|
+
function parseComposerJson(content) {
|
|
2141
|
+
try {
|
|
2142
|
+
const pkg = JSON.parse(content);
|
|
2143
|
+
const deps = [];
|
|
2144
|
+
for (const section of ["require", "require-dev"]) {
|
|
2145
|
+
const entries2 = pkg[section];
|
|
2146
|
+
if (entries2 && typeof entries2 === "object") {
|
|
2147
|
+
for (const [name, version] of Object.entries(entries2)) {
|
|
2148
|
+
if (name === "php" || name.startsWith("ext-")) continue;
|
|
2149
|
+
deps.push({ name, version: typeof version === "string" ? version : void 0 });
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
return deps;
|
|
2154
|
+
} catch {
|
|
2155
|
+
return [];
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
function parsePodfile(content) {
|
|
2159
|
+
const deps = [];
|
|
2160
|
+
const podRegex = /^\s*pod\s+["']([^"']+)["'](?:\s*,\s*["']([^"']+)["'])?/gm;
|
|
2161
|
+
let match;
|
|
2162
|
+
while ((match = podRegex.exec(content)) !== null) {
|
|
2163
|
+
deps.push({ name: match[1], version: match[2] });
|
|
2164
|
+
}
|
|
2165
|
+
return deps;
|
|
2166
|
+
}
|
|
2167
|
+
function parsePackageSwift(content) {
|
|
2168
|
+
const deps = [];
|
|
2169
|
+
const pkgRegex = /\.package\s*\(\s*url:\s*["'](?:https?:\/\/[^/]+\/[^/]+\/|git@[^:]+:[^/]+\/)([^"'/.]+)/g;
|
|
2170
|
+
let match;
|
|
2171
|
+
while ((match = pkgRegex.exec(content)) !== null) {
|
|
2172
|
+
deps.push({ name: match[1] });
|
|
2173
|
+
}
|
|
2174
|
+
return deps;
|
|
2175
|
+
}
|
|
2176
|
+
var MANIFEST_PARSERS = /* @__PURE__ */ new Map([
|
|
2177
|
+
["package.json", parsePackageJson],
|
|
2178
|
+
["requirements.txt", parseRequirementsTxt],
|
|
2179
|
+
["pyproject.toml", parsePyprojectToml],
|
|
2180
|
+
["Pipfile", parsePipfile],
|
|
2181
|
+
["Gemfile", parseGemfile],
|
|
2182
|
+
["go.mod", parseGoMod],
|
|
2183
|
+
["pom.xml", parsePomXml],
|
|
2184
|
+
["build.gradle", parseBuildGradle],
|
|
2185
|
+
["build.gradle.kts", parseBuildGradle],
|
|
2186
|
+
["Cargo.toml", parseCargoToml],
|
|
2187
|
+
["composer.json", parseComposerJson],
|
|
2188
|
+
["Podfile", parsePodfile],
|
|
2189
|
+
["Package.swift", parsePackageSwift]
|
|
2190
|
+
]);
|
|
2191
|
+
|
|
2192
|
+
// src/discover.ts
|
|
2193
|
+
async function discover(repositoryRoot, options) {
|
|
2194
|
+
const files = listRepoFiles(repositoryRoot);
|
|
2195
|
+
const filePatterns = extractFilePatterns(files);
|
|
2196
|
+
const notableFiles = findNotableFiles(files);
|
|
2197
|
+
const languages = await detectLanguages(repositoryRoot, files);
|
|
2198
|
+
let frameworks = [];
|
|
2199
|
+
let libraries = [];
|
|
2200
|
+
if (!options?.skipDependencies) {
|
|
2201
|
+
const allDeps = await extractDependencies(repositoryRoot, files);
|
|
2202
|
+
frameworks = deduplicateDeps(allDeps.filter((d) => d.kind === "framework"));
|
|
2203
|
+
libraries = deduplicateDeps(
|
|
2204
|
+
allDeps.filter((d) => d.kind === "library" || d.kind === "platform" || d.kind === "tool")
|
|
2205
|
+
);
|
|
2206
|
+
}
|
|
2207
|
+
return {
|
|
2208
|
+
filePatterns,
|
|
2209
|
+
notableFiles,
|
|
2210
|
+
languages,
|
|
2211
|
+
frameworks,
|
|
2212
|
+
libraries,
|
|
2213
|
+
metadata: {
|
|
2214
|
+
repositoryRoot: import_path5.default.resolve(repositoryRoot),
|
|
2215
|
+
totalFiles: files.length,
|
|
2216
|
+
scannedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2217
|
+
}
|
|
2218
|
+
};
|
|
2219
|
+
}
|
|
2220
|
+
function extractFilePatterns(files) {
|
|
2221
|
+
const extensions = /* @__PURE__ */ new Set();
|
|
2222
|
+
for (const file of files) {
|
|
2223
|
+
const ext = import_path5.default.extname(file).toLowerCase();
|
|
2224
|
+
if (ext) {
|
|
2225
|
+
extensions.add(`*${ext}`);
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
return Array.from(extensions).sort();
|
|
2229
|
+
}
|
|
2230
|
+
function findNotableFiles(files) {
|
|
2231
|
+
const notable = /* @__PURE__ */ new Set();
|
|
2232
|
+
for (const file of files) {
|
|
2233
|
+
if (isNotableFile(file)) {
|
|
2234
|
+
notable.add(file);
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
return Array.from(notable).sort();
|
|
2238
|
+
}
|
|
2239
|
+
async function extractDependencies(repositoryRoot, files) {
|
|
2240
|
+
const manifestFiles = /* @__PURE__ */ new Map();
|
|
2241
|
+
for (const file of files) {
|
|
2242
|
+
const basename = file.split("/").pop() ?? file;
|
|
2243
|
+
if (MANIFEST_PARSERS.has(basename)) {
|
|
2244
|
+
const existing = manifestFiles.get(basename) ?? [];
|
|
2245
|
+
existing.push(file);
|
|
2246
|
+
manifestFiles.set(basename, existing);
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
const allDeps = [];
|
|
2250
|
+
for (const [manifestName, paths] of manifestFiles) {
|
|
2251
|
+
const parser = MANIFEST_PARSERS.get(manifestName);
|
|
2252
|
+
for (const filePath of paths) {
|
|
2253
|
+
let content;
|
|
2254
|
+
try {
|
|
2255
|
+
content = await import_promises2.default.readFile(import_path5.default.join(repositoryRoot, filePath), "utf-8");
|
|
2256
|
+
} catch {
|
|
2257
|
+
continue;
|
|
2258
|
+
}
|
|
2259
|
+
const rawDeps = parser(content);
|
|
2260
|
+
for (const raw of rawDeps) {
|
|
2261
|
+
const classification = classifyDependency(raw.name);
|
|
2262
|
+
if (classification) {
|
|
2263
|
+
allDeps.push({
|
|
2264
|
+
name: raw.name,
|
|
2265
|
+
displayName: classification.displayName,
|
|
2266
|
+
kind: classification.kind,
|
|
2267
|
+
source: "manifest",
|
|
2268
|
+
manifestFile: filePath,
|
|
2269
|
+
version: raw.version
|
|
2270
|
+
});
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
return allDeps;
|
|
2276
|
+
}
|
|
2277
|
+
function deduplicateDeps(deps) {
|
|
2278
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2279
|
+
const result = [];
|
|
2280
|
+
for (const dep of deps) {
|
|
2281
|
+
if (!seen.has(dep.displayName)) {
|
|
2282
|
+
seen.add(dep.displayName);
|
|
2283
|
+
result.push(dep);
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
return result.sort((a, b) => a.displayName.localeCompare(b.displayName));
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
// src/codacy-yaml.ts
|
|
2290
|
+
var import_promises3 = __toESM(require("fs/promises"));
|
|
2291
|
+
var import_path6 = __toESM(require("path"));
|
|
1254
2292
|
var import_yaml = require("yaml");
|
|
1255
2293
|
async function readCodacyYaml(repositoryRoot) {
|
|
1256
2294
|
const candidates = [".codacy.yaml", ".codacy.yml"];
|
|
1257
2295
|
let raw = null;
|
|
1258
2296
|
for (const filename of candidates) {
|
|
1259
2297
|
try {
|
|
1260
|
-
raw = await
|
|
2298
|
+
raw = await import_promises3.default.readFile(import_path6.default.join(repositoryRoot, filename), "utf-8");
|
|
1261
2299
|
break;
|
|
1262
2300
|
} catch {
|
|
1263
2301
|
}
|
|
@@ -1515,13 +2553,13 @@ var getQueryString = (params) => {
|
|
|
1515
2553
|
};
|
|
1516
2554
|
var getUrl = (config, options) => {
|
|
1517
2555
|
const encoder = config.ENCODE_PATH || encodeURI;
|
|
1518
|
-
const
|
|
2556
|
+
const path9 = options.url.replace("{api-version}", config.VERSION).replace(/{(.*?)}/g, (substring, group) => {
|
|
1519
2557
|
if (options.path?.hasOwnProperty(group)) {
|
|
1520
2558
|
return encoder(String(options.path[group]));
|
|
1521
2559
|
}
|
|
1522
2560
|
return substring;
|
|
1523
2561
|
});
|
|
1524
|
-
const url = `${config.BASE}${
|
|
2562
|
+
const url = `${config.BASE}${path9}`;
|
|
1525
2563
|
if (options.query) {
|
|
1526
2564
|
return `${url}${getQueryString(options.query)}`;
|
|
1527
2565
|
}
|
|
@@ -4865,22 +5903,26 @@ async function listIgnoredFiles(provider, org, repo) {
|
|
|
4865
5903
|
}
|
|
4866
5904
|
|
|
4867
5905
|
// src/init.ts
|
|
4868
|
-
async function detectLanguages(repositoryRoot) {
|
|
4869
|
-
let files
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
5906
|
+
async function detectLanguages(repositoryRoot, precomputedFiles) {
|
|
5907
|
+
let files;
|
|
5908
|
+
if (precomputedFiles) {
|
|
5909
|
+
files = precomputedFiles;
|
|
5910
|
+
} else {
|
|
5911
|
+
try {
|
|
5912
|
+
const output = (0, import_child_process3.execSync)("git ls-files", {
|
|
5913
|
+
cwd: repositoryRoot,
|
|
5914
|
+
encoding: "utf-8",
|
|
5915
|
+
maxBuffer: 50 * 1024 * 1024
|
|
5916
|
+
}).trim();
|
|
5917
|
+
files = output ? output.split("\n") : [];
|
|
5918
|
+
} catch {
|
|
5919
|
+
return [];
|
|
5920
|
+
}
|
|
4879
5921
|
}
|
|
4880
5922
|
const detected = /* @__PURE__ */ new Set();
|
|
4881
5923
|
for (const file of files) {
|
|
4882
|
-
const basename =
|
|
4883
|
-
const ext =
|
|
5924
|
+
const basename = import_path7.default.basename(file);
|
|
5925
|
+
const ext = import_path7.default.extname(file).toLowerCase();
|
|
4884
5926
|
for (const [lang, config] of Object.entries(import_tooling5.LANGUAGE_CONFIG)) {
|
|
4885
5927
|
if (config.extensions.includes(ext)) {
|
|
4886
5928
|
detected.add(lang);
|
|
@@ -4897,9 +5939,9 @@ async function detectLocalConfigFiles(repositoryRoot, adapters) {
|
|
|
4897
5939
|
adapters.map(async (adapter) => {
|
|
4898
5940
|
const result = await adapter.checkLocalConfigurationFile({
|
|
4899
5941
|
repositoryRoot,
|
|
4900
|
-
generatedConfigDir:
|
|
4901
|
-
globalDir:
|
|
4902
|
-
toolInstallDir:
|
|
5942
|
+
generatedConfigDir: import_path7.default.join(repositoryRoot, ".codacy", "generated"),
|
|
5943
|
+
globalDir: import_path7.default.join(process.env.HOME ?? "~", ".codacy"),
|
|
5944
|
+
toolInstallDir: import_path7.default.join(process.env.HOME ?? "~", ".codacy", "tools", adapter.id),
|
|
4903
5945
|
targetFiles: [],
|
|
4904
5946
|
platform: { os: process.platform, arch: process.arch },
|
|
4905
5947
|
logger: silentLogger,
|
|
@@ -5096,10 +6138,10 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
|
|
|
5096
6138
|
};
|
|
5097
6139
|
}
|
|
5098
6140
|
async function initContainerConfig(repositoryRoot, adapters) {
|
|
5099
|
-
const codacyrcPath =
|
|
6141
|
+
const codacyrcPath = import_path7.default.join(repositoryRoot, ".codacyrc");
|
|
5100
6142
|
let raw;
|
|
5101
6143
|
try {
|
|
5102
|
-
raw = await
|
|
6144
|
+
raw = await import_promises4.default.readFile(codacyrcPath, "utf-8");
|
|
5103
6145
|
} catch {
|
|
5104
6146
|
throw new Error(`.codacyrc not found at ${codacyrcPath}`);
|
|
5105
6147
|
}
|
|
@@ -5150,10 +6192,257 @@ async function initContainerConfig(repositoryRoot, adapters) {
|
|
|
5150
6192
|
}
|
|
5151
6193
|
return createCodacyConfig({ source: "container", tools, includeFiles });
|
|
5152
6194
|
}
|
|
6195
|
+
async function initAutoConfig(repositoryRoot, adapters, descriptors, filters, onDiscoveryComplete, rawFilterString) {
|
|
6196
|
+
const discoveryResult = await discover(repositoryRoot);
|
|
6197
|
+
onDiscoveryComplete?.(discoveryResult);
|
|
6198
|
+
const detectedKeys = /* @__PURE__ */ new Set();
|
|
6199
|
+
for (const lang of discoveryResult.languages) detectedKeys.add(lang);
|
|
6200
|
+
for (const fw of discoveryResult.frameworks) detectedKeys.add(fw.displayName);
|
|
6201
|
+
for (const lib of discoveryResult.libraries) detectedKeys.add(lib.displayName);
|
|
6202
|
+
const repoLanguages = new Set(discoveryResult.languages);
|
|
6203
|
+
const localConfigs = await detectLocalConfigFiles(repositoryRoot, adapters);
|
|
6204
|
+
const descriptorMap = new Map(descriptors.map((d) => [d.id, d]));
|
|
6205
|
+
const candidates = [];
|
|
6206
|
+
for (const adapter of adapters) {
|
|
6207
|
+
if (!adapter.languages.includes("*") && adapter.languages.length > 0 && repoLanguages.size > 0) {
|
|
6208
|
+
const hasOverlap = adapter.languages.some((lang) => repoLanguages.has(lang));
|
|
6209
|
+
if (!hasOverlap) continue;
|
|
6210
|
+
}
|
|
6211
|
+
candidates.push(adapter);
|
|
6212
|
+
}
|
|
6213
|
+
const deduped = deduplicateToolFamilies(candidates, localConfigs);
|
|
6214
|
+
const tools = [];
|
|
6215
|
+
for (const adapter of deduped) {
|
|
6216
|
+
const localConfigPath = localConfigs.get(adapter.id);
|
|
6217
|
+
if (localConfigPath) {
|
|
6218
|
+
tools.push({
|
|
6219
|
+
toolId: adapter.id,
|
|
6220
|
+
localConfigurationFile: localConfigPath,
|
|
6221
|
+
useLocalConfigurationFile: true,
|
|
6222
|
+
patterns: []
|
|
6223
|
+
});
|
|
6224
|
+
continue;
|
|
6225
|
+
}
|
|
6226
|
+
const patternsMetadata = adapter.patternsMetadata;
|
|
6227
|
+
if (!patternsMetadata || patternsMetadata.length === 0) continue;
|
|
6228
|
+
const desc = descriptorMap.get(adapter.id);
|
|
6229
|
+
const mappings = desc?.frameworkMappings;
|
|
6230
|
+
const selectedPatterns = filterAutoPatterns(
|
|
6231
|
+
patternsMetadata,
|
|
6232
|
+
mappings,
|
|
6233
|
+
detectedKeys,
|
|
6234
|
+
repoLanguages,
|
|
6235
|
+
filters
|
|
6236
|
+
);
|
|
6237
|
+
if (selectedPatterns.length > 0) {
|
|
6238
|
+
tools.push({
|
|
6239
|
+
toolId: adapter.id,
|
|
6240
|
+
patterns: selectedPatterns.map((p) => ({ patternId: p.id }))
|
|
6241
|
+
});
|
|
6242
|
+
}
|
|
6243
|
+
}
|
|
6244
|
+
const baseConfig = createCodacyConfig({
|
|
6245
|
+
source: "auto",
|
|
6246
|
+
languages: discoveryResult.languages,
|
|
6247
|
+
tools,
|
|
6248
|
+
autoFilters: rawFilterString || void 0
|
|
6249
|
+
});
|
|
6250
|
+
const { config, codacyYamlApplied } = await enrichWithCodacyYaml(
|
|
6251
|
+
baseConfig,
|
|
6252
|
+
repositoryRoot,
|
|
6253
|
+
adapters
|
|
6254
|
+
);
|
|
6255
|
+
return {
|
|
6256
|
+
config,
|
|
6257
|
+
stats: {
|
|
6258
|
+
detectedLanguages: discoveryResult.languages.length,
|
|
6259
|
+
detectedFrameworks: discoveryResult.frameworks.length,
|
|
6260
|
+
detectedLibraries: discoveryResult.libraries.length,
|
|
6261
|
+
selectedTools: tools.length,
|
|
6262
|
+
codacyYamlApplied
|
|
6263
|
+
}
|
|
6264
|
+
};
|
|
6265
|
+
}
|
|
6266
|
+
function toolFamily(id) {
|
|
6267
|
+
return id.replace(/\d+$/, "");
|
|
6268
|
+
}
|
|
6269
|
+
function deduplicateToolFamilies(candidates, localConfigs) {
|
|
6270
|
+
const families = /* @__PURE__ */ new Map();
|
|
6271
|
+
for (const adapter of candidates) {
|
|
6272
|
+
const family = toolFamily(adapter.id);
|
|
6273
|
+
const group = families.get(family) ?? [];
|
|
6274
|
+
group.push(adapter);
|
|
6275
|
+
families.set(family, group);
|
|
6276
|
+
}
|
|
6277
|
+
const result = [];
|
|
6278
|
+
for (const group of families.values()) {
|
|
6279
|
+
if (group.length === 1) {
|
|
6280
|
+
result.push(group[0]);
|
|
6281
|
+
continue;
|
|
6282
|
+
}
|
|
6283
|
+
const withConfig = group.filter((a) => localConfigs.has(a.id));
|
|
6284
|
+
if (withConfig.length === 1) {
|
|
6285
|
+
result.push(withConfig[0]);
|
|
6286
|
+
continue;
|
|
6287
|
+
}
|
|
6288
|
+
group.sort((a, b) => {
|
|
6289
|
+
const majorA = parseInt(a.preferredVersion.split(".")[0], 10) || 0;
|
|
6290
|
+
const majorB = parseInt(b.preferredVersion.split(".")[0], 10) || 0;
|
|
6291
|
+
return majorB - majorA;
|
|
6292
|
+
});
|
|
6293
|
+
result.push(group[0]);
|
|
6294
|
+
}
|
|
6295
|
+
return result;
|
|
6296
|
+
}
|
|
6297
|
+
function filterAutoPatterns(patterns, mappings, detectedKeys, repoLanguages, filters) {
|
|
6298
|
+
const allMappedPrefixes = [];
|
|
6299
|
+
const prefixToKeys = /* @__PURE__ */ new Map();
|
|
6300
|
+
const allMappedTags = [];
|
|
6301
|
+
const tagToKeys = /* @__PURE__ */ new Map();
|
|
6302
|
+
if (mappings) {
|
|
6303
|
+
for (const [key, entry] of Object.entries(mappings)) {
|
|
6304
|
+
if (entry.prefixes) {
|
|
6305
|
+
for (const prefix of entry.prefixes) {
|
|
6306
|
+
allMappedPrefixes.push(prefix);
|
|
6307
|
+
const existing = prefixToKeys.get(prefix) ?? [];
|
|
6308
|
+
existing.push(key);
|
|
6309
|
+
prefixToKeys.set(prefix, existing);
|
|
6310
|
+
}
|
|
6311
|
+
}
|
|
6312
|
+
if (entry.tags) {
|
|
6313
|
+
for (const tag of entry.tags) {
|
|
6314
|
+
if (!allMappedTags.includes(tag)) allMappedTags.push(tag);
|
|
6315
|
+
const existing = tagToKeys.get(tag) ?? [];
|
|
6316
|
+
existing.push(key);
|
|
6317
|
+
tagToKeys.set(tag, existing);
|
|
6318
|
+
}
|
|
6319
|
+
}
|
|
6320
|
+
}
|
|
6321
|
+
}
|
|
6322
|
+
const severityFilter = filters?.severities.length ? new Map(filters.severities.map((s) => [s.level, s.all])) : void 0;
|
|
6323
|
+
const categoryFilter = filters?.categories.length ? new Map(filters.categories.map((c) => [c.category, c.all])) : void 0;
|
|
6324
|
+
const result = [];
|
|
6325
|
+
for (const pattern of patterns) {
|
|
6326
|
+
const matchedKeys = findMappedKeys(
|
|
6327
|
+
pattern.id,
|
|
6328
|
+
pattern.tags,
|
|
6329
|
+
allMappedPrefixes,
|
|
6330
|
+
prefixToKeys,
|
|
6331
|
+
allMappedTags,
|
|
6332
|
+
tagToKeys
|
|
6333
|
+
);
|
|
6334
|
+
if (matchedKeys.length > 0) {
|
|
6335
|
+
if (!matchedKeys.some((k) => detectedKeys.has(k))) continue;
|
|
6336
|
+
} else {
|
|
6337
|
+
if (!pattern.enabled) {
|
|
6338
|
+
if (!isOverriddenByAllPrefix(pattern, severityFilter, categoryFilter)) {
|
|
6339
|
+
continue;
|
|
6340
|
+
}
|
|
6341
|
+
}
|
|
6342
|
+
}
|
|
6343
|
+
if (pattern.languages.length > 0 && repoLanguages.size > 0) {
|
|
6344
|
+
if (!pattern.languages.some((lang) => repoLanguages.has(lang))) continue;
|
|
6345
|
+
}
|
|
6346
|
+
if (severityFilter && !severityFilter.has(pattern.severityLevel)) continue;
|
|
6347
|
+
if (categoryFilter && !categoryFilter.has(pattern.category)) continue;
|
|
6348
|
+
result.push(pattern);
|
|
6349
|
+
}
|
|
6350
|
+
return result;
|
|
6351
|
+
}
|
|
6352
|
+
function findMappedKeys(patternId, patternTags, allPrefixes, prefixToKeys, allTags, tagToKeys) {
|
|
6353
|
+
const matched = /* @__PURE__ */ new Set();
|
|
6354
|
+
let longestPrefix = "";
|
|
6355
|
+
for (const prefix of allPrefixes) {
|
|
6356
|
+
if (patternId.startsWith(prefix) && prefix.length > longestPrefix.length) {
|
|
6357
|
+
longestPrefix = prefix;
|
|
6358
|
+
}
|
|
6359
|
+
}
|
|
6360
|
+
if (longestPrefix) {
|
|
6361
|
+
const keys = prefixToKeys.get(longestPrefix);
|
|
6362
|
+
if (keys) for (const k of keys) matched.add(k);
|
|
6363
|
+
}
|
|
6364
|
+
if (patternTags && patternTags.length > 0 && allTags.length > 0) {
|
|
6365
|
+
for (const tag of patternTags) {
|
|
6366
|
+
const keys = tagToKeys.get(tag);
|
|
6367
|
+
if (keys) for (const k of keys) matched.add(k);
|
|
6368
|
+
}
|
|
6369
|
+
}
|
|
6370
|
+
return Array.from(matched);
|
|
6371
|
+
}
|
|
6372
|
+
function isOverriddenByAllPrefix(pattern, severityFilter, categoryFilter) {
|
|
6373
|
+
if (severityFilter) {
|
|
6374
|
+
const allFlag = severityFilter.get(pattern.severityLevel);
|
|
6375
|
+
if (allFlag === true) return true;
|
|
6376
|
+
}
|
|
6377
|
+
if (categoryFilter) {
|
|
6378
|
+
const allFlag = categoryFilter.get(pattern.category);
|
|
6379
|
+
if (allFlag === true) return true;
|
|
6380
|
+
}
|
|
6381
|
+
return false;
|
|
6382
|
+
}
|
|
6383
|
+
|
|
6384
|
+
// src/auto-filters.ts
|
|
6385
|
+
var SEVERITY_ALIASES = {
|
|
6386
|
+
info: "Info",
|
|
6387
|
+
minor: "Info",
|
|
6388
|
+
warning: "Warning",
|
|
6389
|
+
medium: "Warning",
|
|
6390
|
+
high: "High",
|
|
6391
|
+
error: "Error",
|
|
6392
|
+
critical: "Error"
|
|
6393
|
+
};
|
|
6394
|
+
var CATEGORY_NAMES = {
|
|
6395
|
+
security: "Security",
|
|
6396
|
+
errorprone: "ErrorProne",
|
|
6397
|
+
codestyle: "CodeStyle",
|
|
6398
|
+
compatibility: "Compatibility",
|
|
6399
|
+
performance: "Performance",
|
|
6400
|
+
unusedcode: "UnusedCode",
|
|
6401
|
+
complexity: "Complexity",
|
|
6402
|
+
comprehensibility: "Comprehensibility",
|
|
6403
|
+
bestpractice: "BestPractice",
|
|
6404
|
+
documentation: "Documentation"
|
|
6405
|
+
};
|
|
6406
|
+
function parseAutoFilters(raw) {
|
|
6407
|
+
const severities = [];
|
|
6408
|
+
const categories = [];
|
|
6409
|
+
const unknown = [];
|
|
6410
|
+
const tokens = raw.split(",").map((t) => t.trim()).filter(Boolean);
|
|
6411
|
+
for (const token of tokens) {
|
|
6412
|
+
let name = token;
|
|
6413
|
+
let all = false;
|
|
6414
|
+
if (name.toLowerCase().startsWith("all")) {
|
|
6415
|
+
all = true;
|
|
6416
|
+
name = name.slice(3);
|
|
6417
|
+
}
|
|
6418
|
+
const severityMatch = SEVERITY_ALIASES[name.toLowerCase()];
|
|
6419
|
+
if (severityMatch) {
|
|
6420
|
+
if (!severities.some((s) => s.level === severityMatch)) {
|
|
6421
|
+
severities.push({ level: severityMatch, all });
|
|
6422
|
+
} else {
|
|
6423
|
+
const existing = severities.find((s) => s.level === severityMatch);
|
|
6424
|
+
if (all) existing.all = true;
|
|
6425
|
+
}
|
|
6426
|
+
continue;
|
|
6427
|
+
}
|
|
6428
|
+
const categoryMatch = CATEGORY_NAMES[name.toLowerCase()];
|
|
6429
|
+
if (categoryMatch) {
|
|
6430
|
+
if (!categories.some((c) => c.category === categoryMatch)) {
|
|
6431
|
+
categories.push({ category: categoryMatch, all });
|
|
6432
|
+
} else {
|
|
6433
|
+
const existing = categories.find((c) => c.category === categoryMatch);
|
|
6434
|
+
if (all) existing.all = true;
|
|
6435
|
+
}
|
|
6436
|
+
continue;
|
|
6437
|
+
}
|
|
6438
|
+
unknown.push(token);
|
|
6439
|
+
}
|
|
6440
|
+
return { filters: { severities, categories }, unknown };
|
|
6441
|
+
}
|
|
5153
6442
|
|
|
5154
6443
|
// src/logger.ts
|
|
5155
|
-
var
|
|
5156
|
-
var
|
|
6444
|
+
var import_fs2 = __toESM(require("fs"));
|
|
6445
|
+
var import_path8 = __toESM(require("path"));
|
|
5157
6446
|
var import_os2 = __toESM(require("os"));
|
|
5158
6447
|
var LEVEL_ORDER = {
|
|
5159
6448
|
debug: 0,
|
|
@@ -5164,7 +6453,7 @@ var LEVEL_ORDER = {
|
|
|
5164
6453
|
function rotateLogFiles(logsDir, maxFiles) {
|
|
5165
6454
|
let entries2;
|
|
5166
6455
|
try {
|
|
5167
|
-
entries2 =
|
|
6456
|
+
entries2 = import_fs2.default.readdirSync(logsDir).filter((f) => f.startsWith("analysis-") && f.endsWith(".log"));
|
|
5168
6457
|
} catch {
|
|
5169
6458
|
return;
|
|
5170
6459
|
}
|
|
@@ -5173,7 +6462,7 @@ function rotateLogFiles(logsDir, maxFiles) {
|
|
|
5173
6462
|
const toRemove = entries2.slice(0, entries2.length - maxFiles);
|
|
5174
6463
|
for (const f of toRemove) {
|
|
5175
6464
|
try {
|
|
5176
|
-
|
|
6465
|
+
import_fs2.default.unlinkSync(import_path8.default.join(logsDir, f));
|
|
5177
6466
|
} catch {
|
|
5178
6467
|
}
|
|
5179
6468
|
}
|
|
@@ -5208,17 +6497,17 @@ function enrichMessage(message, fields) {
|
|
|
5208
6497
|
function createLogger(options = {}) {
|
|
5209
6498
|
const minLevel = LEVEL_ORDER[options.level ?? "info"];
|
|
5210
6499
|
const fileEnabled = options.fileEnabled ?? true;
|
|
5211
|
-
const logsDir = options.logsDir ??
|
|
6500
|
+
const logsDir = options.logsDir ?? import_path8.default.join(import_os2.default.homedir(), ".codacy", "logs");
|
|
5212
6501
|
const maxFileSize = options.maxFileSize ?? 10 * 1024 * 1024;
|
|
5213
6502
|
const maxFiles = options.maxFiles ?? 5;
|
|
5214
6503
|
const onStderrMessage = options.onStderrMessage;
|
|
5215
6504
|
let logFd = null;
|
|
5216
6505
|
let bytesWritten = 0;
|
|
5217
6506
|
if (fileEnabled) {
|
|
5218
|
-
|
|
6507
|
+
import_fs2.default.mkdirSync(logsDir, { recursive: true });
|
|
5219
6508
|
rotateLogFiles(logsDir, maxFiles);
|
|
5220
|
-
const logFilePath =
|
|
5221
|
-
logFd =
|
|
6509
|
+
const logFilePath = import_path8.default.join(logsDir, `analysis-${fileTimestamp()}.log`);
|
|
6510
|
+
logFd = import_fs2.default.openSync(logFilePath, "a");
|
|
5222
6511
|
}
|
|
5223
6512
|
function log(level, message, fields) {
|
|
5224
6513
|
const numLevel = LEVEL_ORDER[level];
|
|
@@ -5231,13 +6520,13 @@ function createLogger(options = {}) {
|
|
|
5231
6520
|
...fields
|
|
5232
6521
|
});
|
|
5233
6522
|
const buf = Buffer.from(entry + "\n", "utf-8");
|
|
5234
|
-
|
|
6523
|
+
import_fs2.default.writeSync(logFd, buf);
|
|
5235
6524
|
bytesWritten += buf.length;
|
|
5236
6525
|
if (bytesWritten >= maxFileSize) {
|
|
5237
|
-
|
|
6526
|
+
import_fs2.default.closeSync(logFd);
|
|
5238
6527
|
rotateLogFiles(logsDir, maxFiles);
|
|
5239
|
-
const newPath =
|
|
5240
|
-
logFd =
|
|
6528
|
+
const newPath = import_path8.default.join(logsDir, `analysis-${fileTimestamp()}.log`);
|
|
6529
|
+
logFd = import_fs2.default.openSync(newPath, "a");
|
|
5241
6530
|
bytesWritten = 0;
|
|
5242
6531
|
}
|
|
5243
6532
|
}
|
|
@@ -5262,15 +6551,18 @@ function createLogger(options = {}) {
|
|
|
5262
6551
|
applyCodacyYamlExcludes,
|
|
5263
6552
|
buildToolConfig,
|
|
5264
6553
|
clearAdapters,
|
|
6554
|
+
computePatternSummary,
|
|
5265
6555
|
configureApiToken,
|
|
5266
6556
|
createCodacyConfig,
|
|
5267
6557
|
createLogger,
|
|
5268
6558
|
detectLanguages,
|
|
6559
|
+
discover,
|
|
5269
6560
|
formatOutput,
|
|
5270
6561
|
formatTextSummary,
|
|
5271
6562
|
getAdapter,
|
|
5272
6563
|
getRegisteredAdapters,
|
|
5273
6564
|
getRegisteredDescriptors,
|
|
6565
|
+
initAutoConfig,
|
|
5274
6566
|
initContainerConfig,
|
|
5275
6567
|
initDefaultConfig,
|
|
5276
6568
|
initLocalConfig,
|
|
@@ -5279,6 +6571,7 @@ function createLogger(options = {}) {
|
|
|
5279
6571
|
listIgnoredFiles,
|
|
5280
6572
|
listPrFiles,
|
|
5281
6573
|
listStagedFiles,
|
|
6574
|
+
parseAutoFilters,
|
|
5282
6575
|
readCodacyConfig,
|
|
5283
6576
|
readCodacyYaml,
|
|
5284
6577
|
registerAdapter,
|