@codacy/analysis-runner 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -37,14 +37,18 @@ __export(index_exports, {
37
37
  clearAdapters: () => clearAdapters,
38
38
  computePatternSummary: () => computePatternSummary,
39
39
  configureApiToken: () => configureApiToken,
40
+ configureProxy: () => import_tooling6.configureProxy,
40
41
  createCodacyConfig: () => createCodacyConfig,
41
42
  createLogger: () => createLogger,
43
+ createNullLogger: () => createNullLogger,
44
+ deleteCredentials: () => deleteCredentials,
42
45
  detectLanguages: () => detectLanguages,
43
46
  diffConfigs: () => diffConfigs,
44
47
  discover: () => discover,
45
48
  formatOutput: () => formatOutput,
46
49
  formatTextSummary: () => formatTextSummary,
47
50
  getAdapter: () => getAdapter,
51
+ getKnownDisplayNames: () => getKnownDisplayNames,
48
52
  getRegisteredAdapters: () => getRegisteredAdapters,
49
53
  getRegisteredDescriptors: () => getRegisteredDescriptors,
50
54
  initAutoConfig: () => initAutoConfig,
@@ -57,25 +61,34 @@ __export(index_exports, {
57
61
  listIgnoredFiles: () => listIgnoredFiles,
58
62
  listPrFiles: () => listPrFiles,
59
63
  listStagedFiles: () => listStagedFiles,
64
+ loadCredentials: () => loadCredentials,
60
65
  mergeConfigs: () => mergeConfigs,
61
66
  parseAutoFilters: () => parseAutoFilters,
67
+ pruneEmptyTools: () => pruneEmptyTools,
68
+ readBaselineConfig: () => readBaselineConfig,
62
69
  readCodacyConfig: () => readCodacyConfig,
63
70
  readCodacyYaml: () => readCodacyYaml,
64
71
  registerAdapter: () => registerAdapter,
65
72
  registerLazyAdapter: () => registerLazyAdapter,
66
73
  registerUnsupportedTool: () => registerUnsupportedTool,
74
+ resolveApiToken: () => resolveApiToken,
75
+ resolveBaselinePath: () => resolveBaselinePath,
67
76
  resolveConfigPath: () => resolveConfigPath,
68
77
  resolveEngineToAdapter: () => resolveEngineToAdapter,
78
+ resolveProxyConfig: () => import_tooling6.resolveProxyConfig,
69
79
  rotateLogFiles: () => rotateLogFiles,
80
+ saveCredentials: () => saveCredentials,
81
+ updateConfigIncremental: () => updateConfigIncremental,
70
82
  validateApiToken: () => validateApiToken,
83
+ writeBaselineConfig: () => writeBaselineConfig,
71
84
  writeCodacyConfig: () => writeCodacyConfig,
72
85
  writeCodacyConfigFile: () => writeCodacyConfigFile
73
86
  });
74
87
  module.exports = __toCommonJS(index_exports);
75
88
 
76
89
  // src/analyze.ts
77
- var import_os = __toESM(require("os"));
78
- var import_path3 = __toESM(require("path"));
90
+ var import_os2 = __toESM(require("os"));
91
+ var import_path4 = __toESM(require("path"));
79
92
  var import_child_process3 = require("child_process");
80
93
  var import_util = require("util");
81
94
  var import_tooling3 = require("@codacy/tooling");
@@ -148,8 +161,14 @@ var CONFIG_FILE = "codacy.config.json";
148
161
  function resolveConfigPath(repositoryRoot, configFile) {
149
162
  return configFile ? import_path.default.resolve(repositoryRoot, configFile) : import_path.default.join(repositoryRoot, CODACY_DIR, CONFIG_FILE);
150
163
  }
151
- async function readCodacyConfig(repositoryRoot, configFile) {
164
+ function resolveBaselinePath(repositoryRoot, configFile) {
152
165
  const configPath = resolveConfigPath(repositoryRoot, configFile);
166
+ const dir = import_path.default.dirname(configPath);
167
+ const ext = import_path.default.extname(configPath);
168
+ const stem = import_path.default.basename(configPath, ext);
169
+ return import_path.default.join(dir, `${stem}.baseline${ext}`);
170
+ }
171
+ async function readConfigAtPath(configPath) {
153
172
  let raw;
154
173
  try {
155
174
  raw = await import_promises.default.readFile(configPath, "utf-8");
@@ -159,10 +178,26 @@ async function readCodacyConfig(repositoryRoot, configFile) {
159
178
  }
160
179
  throw err;
161
180
  }
162
- const config = JSON.parse(raw);
181
+ const parsed = JSON.parse(raw);
182
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
183
+ throw new Error(`Invalid config at ${configPath}: expected a JSON object`);
184
+ }
185
+ const config = parsed;
163
186
  coerceParameterValues(config);
164
187
  return config;
165
188
  }
189
+ async function readCodacyConfig(repositoryRoot, configFile) {
190
+ return readConfigAtPath(resolveConfigPath(repositoryRoot, configFile));
191
+ }
192
+ async function readBaselineConfig(repositoryRoot, configFile) {
193
+ return readConfigAtPath(resolveBaselinePath(repositoryRoot, configFile));
194
+ }
195
+ async function writeBaselineConfig(repositoryRoot, config, configFile) {
196
+ const baselinePath = resolveBaselinePath(repositoryRoot, configFile);
197
+ await import_promises.default.mkdir(import_path.default.dirname(baselinePath), { recursive: true });
198
+ await import_promises.default.writeFile(baselinePath, JSON.stringify(config, null, 2) + "\n");
199
+ return baselinePath;
200
+ }
166
201
  function coerceParameterValues(config) {
167
202
  for (const tool of config.tools) {
168
203
  for (const pattern of tool.patterns) {
@@ -418,6 +453,61 @@ function detectDefaultBranch(repositoryRoot) {
418
453
 
419
454
  // src/files.ts
420
455
  var MAX_FILE_SIZE_BYTES = 150 * 1024;
456
+ var DEPENDENCY_MANIFEST_FILES = /* @__PURE__ */ new Set([
457
+ // JavaScript / TypeScript
458
+ "package.json",
459
+ "package-lock.json",
460
+ "npm-shrinkwrap.json",
461
+ "yarn.lock",
462
+ "pnpm-lock.yaml",
463
+ "bun.lock",
464
+ // Python
465
+ "requirements.txt",
466
+ "Pipfile",
467
+ "Pipfile.lock",
468
+ "poetry.lock",
469
+ "uv.lock",
470
+ // PHP
471
+ "composer.json",
472
+ "composer.lock",
473
+ // Ruby
474
+ "Gemfile.lock",
475
+ // Rust
476
+ "Cargo.toml",
477
+ "Cargo.lock",
478
+ // Go
479
+ "go.mod",
480
+ "go.sum",
481
+ // .NET
482
+ "packages.config",
483
+ "packages.lock.json",
484
+ "Directory.Packages.props",
485
+ "Packages.props",
486
+ // Java / Scala
487
+ "pom.xml",
488
+ "gradle.lockfile",
489
+ "build.gradle",
490
+ "build.gradle.kts",
491
+ "build.sbt.lock",
492
+ // Dart / Flutter
493
+ "pubspec.yaml",
494
+ "pubspec.lock",
495
+ // Elixir
496
+ "mix.lock",
497
+ // Swift / CocoaPods
498
+ "Package.resolved",
499
+ "Podfile.lock",
500
+ // C / C++ (Conan)
501
+ "conan.lock",
502
+ // Erlang
503
+ "rebar.lock"
504
+ ]);
505
+ var DEPENDENCY_MANIFEST_SUFFIXES = [".deps.json"];
506
+ function isDependencyManifest(relativePath) {
507
+ const base = relativePath.split("/").pop() ?? "";
508
+ if (DEPENDENCY_MANIFEST_FILES.has(base)) return true;
509
+ return DEPENDENCY_MANIFEST_SUFFIXES.some((suffix) => base.endsWith(suffix));
510
+ }
421
511
  function listRepoFiles(repositoryRoot) {
422
512
  try {
423
513
  const tracked = (0, import_child_process2.execSync)("git ls-files", {
@@ -449,6 +539,15 @@ function filterOversizedFiles(files, repositoryRoot, logger) {
449
539
  try {
450
540
  const stat = import_fs.default.statSync(import_path2.default.join(repositoryRoot, f));
451
541
  if (stat.size > MAX_FILE_SIZE_BYTES) {
542
+ if (isDependencyManifest(f)) {
543
+ logger?.debug("Keeping oversized dependency manifest", {
544
+ file: f,
545
+ sizeKB: Math.round(stat.size / 1024),
546
+ limitKB: MAX_FILE_SIZE_BYTES / 1024
547
+ });
548
+ result.push(f);
549
+ continue;
550
+ }
452
551
  skipped.push({ filePath: f, sizeBytes: stat.size });
453
552
  logger?.debug("Skipping oversized file", {
454
553
  file: f,
@@ -504,7 +603,43 @@ function filterByTargetFiles(allFiles, targetPatterns) {
504
603
  // src/executor.ts
505
604
  var import_tooling2 = require("@codacy/tooling");
506
605
  var HARD_TIMEOUT_GRACE_MS = 2e3;
507
- async function runTool(adapter, ctx, codacyToolConfig, timeoutMs) {
606
+ function anySignal(a, b) {
607
+ if (typeof AbortSignal.any === "function") return AbortSignal.any([a, b]);
608
+ const composite = new AbortController();
609
+ if (a.aborted || b.aborted) {
610
+ composite.abort();
611
+ } else {
612
+ const onAbort = () => composite.abort();
613
+ a.addEventListener("abort", onAbort, { once: true });
614
+ b.addEventListener("abort", onAbort, { once: true });
615
+ }
616
+ return composite.signal;
617
+ }
618
+ function cancelledResult(adapter) {
619
+ const now = (/* @__PURE__ */ new Date()).toISOString();
620
+ return {
621
+ metadata: { startedAt: now, completedAt: now, durationMs: 0 },
622
+ issues: [],
623
+ errors: [
624
+ {
625
+ toolId: adapter.id,
626
+ phase: "toolInvoke",
627
+ kind: "Cancelled",
628
+ message: `Analysis cancelled by caller; ${adapter.id} was not run`,
629
+ level: "error"
630
+ }
631
+ ],
632
+ summary: {
633
+ toolId: adapter.id,
634
+ status: "skipped",
635
+ issueCount: 0,
636
+ errorCount: 1,
637
+ durationMs: 0,
638
+ filesAnalyzed: 0
639
+ }
640
+ };
641
+ }
642
+ async function runTool(adapter, ctx, codacyToolConfig, timeoutMs, callerSignal) {
508
643
  const startTime = Date.now();
509
644
  ctx.logger.debug("runTool started", {
510
645
  toolId: adapter.id,
@@ -512,7 +647,7 @@ async function runTool(adapter, ctx, codacyToolConfig, timeoutMs) {
512
647
  timeoutMs
513
648
  });
514
649
  const abortController = new AbortController();
515
- ctx.signal = abortController.signal;
650
+ ctx.signal = callerSignal ? anySignal(callerSignal, abortController.signal) : abortController.signal;
516
651
  const abortTimer = setTimeout(() => abortController.abort(), timeoutMs);
517
652
  abortTimer.unref?.();
518
653
  let hardTimer;
@@ -533,8 +668,14 @@ async function runTool(adapter, ctx, codacyToolConfig, timeoutMs) {
533
668
  return result;
534
669
  } catch (err) {
535
670
  const durationMs = Date.now() - startTime;
536
- const isTimeout = err instanceof Error && err.message.includes("TIMEOUT");
537
- if (isTimeout) {
671
+ const isCancelled = callerSignal?.aborted === true;
672
+ const isTimeout = !isCancelled && err instanceof Error && err.message.includes("TIMEOUT");
673
+ if (isCancelled) {
674
+ ctx.logger.error("Tool cancelled by caller", {
675
+ toolId: adapter.id,
676
+ elapsedMs: durationMs
677
+ });
678
+ } else if (isTimeout) {
538
679
  ctx.logger.error("Tool timed out", { toolId: adapter.id, timeoutMs, elapsedMs: durationMs });
539
680
  } else {
540
681
  ctx.logger.error("Tool execution failed", {
@@ -544,6 +685,8 @@ async function runTool(adapter, ctx, codacyToolConfig, timeoutMs) {
544
685
  });
545
686
  }
546
687
  const now = /* @__PURE__ */ new Date();
688
+ const kind = isCancelled ? "Cancelled" : isTimeout ? "Timeout" : "InvocationError";
689
+ const message = isCancelled ? `Tool ${adapter.id} cancelled by caller` : isTimeout ? `Tool ${adapter.id} exceeded timeout of ${(0, import_tooling2.formatDuration)(timeoutMs)}` : `Tool ${adapter.id} failed: ${err instanceof Error ? err.message : String(err)}`;
547
690
  return {
548
691
  metadata: {
549
692
  startedAt: new Date(now.getTime() - durationMs).toISOString(),
@@ -555,8 +698,8 @@ async function runTool(adapter, ctx, codacyToolConfig, timeoutMs) {
555
698
  {
556
699
  toolId: adapter.id,
557
700
  phase: "toolInvoke",
558
- kind: isTimeout ? "Timeout" : "InvocationError",
559
- message: isTimeout ? `Tool ${adapter.id} exceeded timeout of ${(0, import_tooling2.formatDuration)(timeoutMs)}` : `Tool ${adapter.id} failed: ${err instanceof Error ? err.message : String(err)}`,
701
+ kind,
702
+ message,
560
703
  level: "error"
561
704
  }
562
705
  ],
@@ -574,15 +717,19 @@ async function runTool(adapter, ctx, codacyToolConfig, timeoutMs) {
574
717
  if (hardTimer) clearTimeout(hardTimer);
575
718
  }
576
719
  }
577
- async function runToolsSequential(tools, timeoutMs, callbacks) {
720
+ async function runToolsSequential(tools, timeoutMs, callbacks, callerSignal) {
578
721
  const results = [];
579
722
  for (const { adapter, ctx, codacyToolConfig } of tools) {
723
+ if (callerSignal?.aborted) {
724
+ results.push(cancelledResult(adapter));
725
+ continue;
726
+ }
580
727
  if (callbacks?.onToolProgress) {
581
728
  const cb = callbacks.onToolProgress;
582
729
  ctx.onProgress = (message) => cb(adapter.id, adapter.displayName, message);
583
730
  }
584
731
  callbacks?.onToolStart?.(adapter.id, adapter.displayName);
585
- const result = await runTool(adapter, ctx, codacyToolConfig, timeoutMs);
732
+ const result = await runTool(adapter, ctx, codacyToolConfig, timeoutMs, callerSignal);
586
733
  callbacks?.onToolComplete?.(
587
734
  adapter.id,
588
735
  adapter.displayName,
@@ -593,19 +740,23 @@ async function runToolsSequential(tools, timeoutMs, callbacks) {
593
740
  }
594
741
  return results;
595
742
  }
596
- async function runToolsParallel(tools, timeoutMs, concurrency, callbacks) {
743
+ async function runToolsParallel(tools, timeoutMs, concurrency, callbacks, callerSignal) {
597
744
  const results = new Array(tools.length);
598
745
  let nextIndex = 0;
599
746
  async function worker() {
600
747
  while (nextIndex < tools.length) {
601
748
  const index = nextIndex++;
602
749
  const { adapter, ctx, codacyToolConfig } = tools[index];
750
+ if (callerSignal?.aborted) {
751
+ results[index] = cancelledResult(adapter);
752
+ continue;
753
+ }
603
754
  if (callbacks?.onToolProgress) {
604
755
  const cb = callbacks.onToolProgress;
605
756
  ctx.onProgress = (message) => cb(adapter.id, adapter.displayName, message);
606
757
  }
607
758
  callbacks?.onToolStart?.(adapter.id, adapter.displayName);
608
- results[index] = await runTool(adapter, ctx, codacyToolConfig, timeoutMs);
759
+ results[index] = await runTool(adapter, ctx, codacyToolConfig, timeoutMs, callerSignal);
609
760
  callbacks?.onToolComplete?.(
610
761
  adapter.id,
611
762
  adapter.displayName,
@@ -619,6 +770,124 @@ async function runToolsParallel(tools, timeoutMs, concurrency, callbacks) {
619
770
  return results;
620
771
  }
621
772
 
773
+ // src/logger.ts
774
+ var import_fs2 = __toESM(require("fs"));
775
+ var import_path3 = __toESM(require("path"));
776
+ var import_os = __toESM(require("os"));
777
+ var LEVEL_ORDER = {
778
+ debug: 0,
779
+ info: 1,
780
+ warning: 2,
781
+ error: 3
782
+ };
783
+ function createNullLogger() {
784
+ return {
785
+ debug: () => {
786
+ },
787
+ info: () => {
788
+ },
789
+ warn: () => {
790
+ },
791
+ error: () => {
792
+ }
793
+ };
794
+ }
795
+ function rotateLogFiles(logsDir, maxFiles) {
796
+ let entries2;
797
+ try {
798
+ entries2 = import_fs2.default.readdirSync(logsDir).filter((f) => f.startsWith("analysis-") && f.endsWith(".log"));
799
+ } catch {
800
+ return;
801
+ }
802
+ if (entries2.length <= maxFiles) return;
803
+ entries2.sort();
804
+ const toRemove = entries2.slice(0, entries2.length - maxFiles);
805
+ for (const f of toRemove) {
806
+ try {
807
+ import_fs2.default.unlinkSync(import_path3.default.join(logsDir, f));
808
+ } catch {
809
+ }
810
+ }
811
+ }
812
+ var fileSeq = 0;
813
+ function fileTimestamp() {
814
+ return `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${fileSeq++}`;
815
+ }
816
+ function enrichMessage(message, fields) {
817
+ if (!fields) return message;
818
+ const parts = [];
819
+ if (fields.toolId) parts.push(String(fields.toolId));
820
+ if (fields.version) parts.push(`v${fields.version}`);
821
+ if (fields.installation) parts.push(String(fields.installation));
822
+ if (fields.configSource) parts.push(String(fields.configSource));
823
+ if (typeof fields.fileCount === "number") parts.push(`${fields.fileCount} files`);
824
+ if (typeof fields.issueCount === "number") parts.push(`${fields.issueCount} issues`);
825
+ if (typeof fields.errorCount === "number" && fields.errorCount > 0)
826
+ parts.push(`${fields.errorCount} errors`);
827
+ if (typeof fields.totalIssues === "number") parts.push(`${fields.totalIssues} issues`);
828
+ if (typeof fields.totalErrors === "number" && fields.totalErrors > 0)
829
+ parts.push(`${fields.totalErrors} errors`);
830
+ if (typeof fields.durationMs === "number")
831
+ parts.push(`${(fields.durationMs / 1e3).toFixed(1)}s`);
832
+ if (fields.reason) parts.push(String(fields.reason));
833
+ if (fields.error) parts.push(String(fields.error));
834
+ if (typeof fields.found === "boolean") parts.push(fields.found ? "found" : "not found");
835
+ if (fields.path && !fields.version) parts.push(String(fields.path));
836
+ if (parts.length === 0) return message;
837
+ return `${message} (${parts.join(", ")})`;
838
+ }
839
+ function createLogger(options = {}) {
840
+ const minLevel = LEVEL_ORDER[options.level ?? "info"];
841
+ const fileEnabled = options.fileEnabled ?? true;
842
+ const logsDir = options.logsDir ?? import_path3.default.join(import_os.default.homedir(), ".codacy", "logs");
843
+ const maxFileSize = options.maxFileSize ?? 10 * 1024 * 1024;
844
+ const maxFiles = options.maxFiles ?? 5;
845
+ const onStderrMessage = options.onStderrMessage;
846
+ let logFd = null;
847
+ let bytesWritten = 0;
848
+ if (fileEnabled) {
849
+ import_fs2.default.mkdirSync(logsDir, { recursive: true });
850
+ rotateLogFiles(logsDir, maxFiles);
851
+ const logFilePath = import_path3.default.join(logsDir, `analysis-${fileTimestamp()}.log`);
852
+ logFd = import_fs2.default.openSync(logFilePath, "a");
853
+ }
854
+ function log(level, message, fields) {
855
+ const numLevel = LEVEL_ORDER[level];
856
+ if (numLevel < minLevel) return;
857
+ if (fileEnabled && logFd !== null) {
858
+ const entry = JSON.stringify({
859
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
860
+ level,
861
+ message,
862
+ ...fields
863
+ });
864
+ const buf = Buffer.from(entry + "\n", "utf-8");
865
+ import_fs2.default.writeSync(logFd, buf);
866
+ bytesWritten += buf.length;
867
+ if (bytesWritten >= maxFileSize) {
868
+ import_fs2.default.closeSync(logFd);
869
+ rotateLogFiles(logsDir, maxFiles);
870
+ const newPath = import_path3.default.join(logsDir, `analysis-${fileTimestamp()}.log`);
871
+ logFd = import_fs2.default.openSync(newPath, "a");
872
+ bytesWritten = 0;
873
+ }
874
+ }
875
+ const enriched = enrichMessage(message, fields);
876
+ if (onStderrMessage) {
877
+ onStderrMessage(enriched, level);
878
+ } else {
879
+ process.stderr.write(` ${enriched}
880
+ `);
881
+ }
882
+ }
883
+ return {
884
+ debug: (msg, fields) => log("debug", msg, fields),
885
+ info: (msg, fields) => log("info", msg, fields),
886
+ warn: (msg, fields) => log("warning", msg, fields),
887
+ error: (msg, fields) => log("error", msg, fields)
888
+ };
889
+ }
890
+
622
891
  // src/summary.ts
623
892
  var NOISY_THRESHOLD = 500;
624
893
  var SEVERITY_ORDER = ["Error", "High", "Warning", "Info"];
@@ -731,16 +1000,6 @@ function computePatternSummary(issues, lookup) {
731
1000
 
732
1001
  // src/analyze.ts
733
1002
  var execFileAsync = (0, import_util.promisify)(import_child_process3.execFile);
734
- var nullLogger = {
735
- debug: () => {
736
- },
737
- info: () => {
738
- },
739
- warn: () => {
740
- },
741
- error: () => {
742
- }
743
- };
744
1003
  var CHECK_CONCURRENCY = 12;
745
1004
  async function mapWithConcurrency(items, concurrency, task) {
746
1005
  const results = new Array(items.length);
@@ -757,10 +1016,11 @@ async function mapWithConcurrency(items, concurrency, task) {
757
1016
  }
758
1017
  async function analyze(options) {
759
1018
  const startedAt = /* @__PURE__ */ new Date();
1019
+ if (options.proxy) (0, import_tooling3.configureProxy)(options.proxy);
760
1020
  const mode = options.mode || "standard";
761
1021
  const toolTimeout = options.toolTimeout || 9e5;
762
1022
  const parallelTools = options.parallelTools || 1;
763
- const logger = options.logger ?? nullLogger;
1023
+ const logger = options.logger ?? createNullLogger();
764
1024
  const phaseTimings = {
765
1025
  availabilityMs: 0,
766
1026
  fileDiscoveryMs: 0,
@@ -773,6 +1033,26 @@ async function analyze(options) {
773
1033
  unavailable: [],
774
1034
  checkedAt: (/* @__PURE__ */ new Date()).toISOString()
775
1035
  };
1036
+ if (options.signal?.aborted) {
1037
+ logger.info("Analysis cancelled by caller before it started");
1038
+ return buildResult(
1039
+ startedAt,
1040
+ options.repositoryRoot,
1041
+ mode,
1042
+ emptyCapability,
1043
+ [],
1044
+ [
1045
+ {
1046
+ toolId: "",
1047
+ phase: "toolInvoke",
1048
+ kind: "Cancelled",
1049
+ message: "Analysis cancelled by caller before it started",
1050
+ level: "error"
1051
+ }
1052
+ ],
1053
+ []
1054
+ );
1055
+ }
776
1056
  try {
777
1057
  await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], {
778
1058
  cwd: options.repositoryRoot
@@ -836,11 +1116,11 @@ async function analyze(options) {
836
1116
  const requestedTools = options.tools ?? codacyConfig.tools.map((t) => t.toolId);
837
1117
  const adapters = await getRegisteredAdapters(requestedTools);
838
1118
  logger.info("Pipeline started", { mode, toolCount: adapters.length, parallelTools, toolTimeout });
839
- const globalDir = import_path3.default.join(import_os.default.homedir(), ".codacy");
1119
+ const globalDir = import_path4.default.join(import_os2.default.homedir(), ".codacy");
840
1120
  const baseCtx = {
841
1121
  repositoryRoot: options.repositoryRoot,
842
1122
  globalDir,
843
- platform: { os: import_os.default.platform(), arch: import_os.default.arch() },
1123
+ platform: { os: import_os2.default.platform(), arch: import_os2.default.arch() },
844
1124
  logger,
845
1125
  runner: "local"
846
1126
  };
@@ -1202,9 +1482,15 @@ async function analyze(options) {
1202
1482
  };
1203
1483
  let results;
1204
1484
  if (parallelTools > 1 && toolRuns.length > 1) {
1205
- results = await runToolsParallel(toolRuns, toolTimeout, parallelTools, callbacks);
1485
+ results = await runToolsParallel(
1486
+ toolRuns,
1487
+ toolTimeout,
1488
+ parallelTools,
1489
+ callbacks,
1490
+ options.signal
1491
+ );
1206
1492
  } else {
1207
- results = await runToolsSequential(toolRuns, toolTimeout, callbacks);
1493
+ results = await runToolsSequential(toolRuns, toolTimeout, callbacks, options.signal);
1208
1494
  }
1209
1495
  phaseTimings.toolExecutionMs = Date.now() - toolExecStartMs;
1210
1496
  const allIssues = normalizeIssues(results.flatMap((r) => r.issues));
@@ -1245,8 +1531,8 @@ function makeToolContext(base, adapter, targetFiles) {
1245
1531
  return {
1246
1532
  ...base,
1247
1533
  targetFiles,
1248
- toolInstallDir: import_path3.default.join(base.globalDir, "tools", adapter.id),
1249
- generatedConfigDir: import_path3.default.join(base.repositoryRoot, ".codacy", "generated", adapter.id)
1534
+ toolInstallDir: import_path4.default.join(base.globalDir, "tools", adapter.id),
1535
+ generatedConfigDir: import_path4.default.join(base.repositoryRoot, ".codacy", "generated", adapter.id)
1250
1536
  };
1251
1537
  }
1252
1538
  function normalizeIssues(issues) {
@@ -1285,7 +1571,7 @@ function buildResult(startedAt, repositoryRoot, mode, capability, issues, errors
1285
1571
  }
1286
1572
 
1287
1573
  // src/format.ts
1288
- var import_path4 = __toESM(require("path"));
1574
+ var import_path5 = __toESM(require("path"));
1289
1575
  var import_tooling4 = require("@codacy/tooling");
1290
1576
  var import_ansis = __toESM(require("ansis"));
1291
1577
  var import_cli_table3 = __toESM(require("cli-table3"));
@@ -1711,7 +1997,7 @@ function formatFilesCell(filesRouted) {
1711
1997
  }
1712
1998
  function formatConfigCell(tool, repositoryRoot) {
1713
1999
  if (tool.configSource === "local" && tool.configPath) {
1714
- const rawPath = import_path4.default.isAbsolute(tool.configPath) ? import_path4.default.relative(repositoryRoot, tool.configPath) : tool.configPath;
2000
+ const rawPath = import_path5.default.isAbsolute(tool.configPath) ? import_path5.default.relative(repositoryRoot, tool.configPath) : tool.configPath;
1715
2001
  return rawPath.startsWith("./") ? rawPath : `./${rawPath}`;
1716
2002
  }
1717
2003
  if (tool.configSource === "codacy") return "Codacy";
@@ -1854,13 +2140,13 @@ function buildConfigFileTool(toolId, source, dest, listFn) {
1854
2140
  applyToolLevelLists(tool, source, dest, listFn);
1855
2141
  return tool;
1856
2142
  }
1857
- function mergePatterns(source, dest) {
2143
+ function mergePatterns(source, dest, preferDest = false) {
1858
2144
  const sourceMap = indexByPatternId(source);
1859
2145
  const seen = /* @__PURE__ */ new Set();
1860
2146
  const result = [];
1861
2147
  for (const dp of dest) {
1862
2148
  const sp = sourceMap.get(dp.patternId);
1863
- result.push(clonePattern(sp ?? dp));
2149
+ result.push(clonePattern(preferDest ? dp : sp ?? dp));
1864
2150
  seen.add(dp.patternId);
1865
2151
  }
1866
2152
  for (const sp of source) {
@@ -1881,12 +2167,12 @@ function diffPatterns(source, dest) {
1881
2167
  const sourceIds = new Set(source.map((p) => p.patternId));
1882
2168
  return dest.filter((dp) => !sourceIds.has(dp.patternId)).map(clonePattern);
1883
2169
  }
1884
- function combineTool(op, toolId, source, dest) {
2170
+ function combineTool(op, toolId, source, dest, preferDest = false) {
1885
2171
  const listFn = op === "merge" ? unionList : intersectList;
1886
2172
  if (usesConfigFile(source) || usesConfigFile(dest)) {
1887
2173
  return buildConfigFileTool(toolId, source, dest, listFn);
1888
2174
  }
1889
- const patterns = op === "merge" ? mergePatterns(source.patterns, dest.patterns) : intersectPatterns(source.patterns, dest.patterns);
2175
+ const patterns = op === "merge" ? mergePatterns(source.patterns, dest.patterns, preferDest) : intersectPatterns(source.patterns, dest.patterns);
1890
2176
  const tool = { toolId, patterns };
1891
2177
  if (dest.useLocalConfigurationFile !== void 0) {
1892
2178
  tool.useLocalConfigurationFile = dest.useLocalConfigurationFile;
@@ -1906,13 +2192,14 @@ function buildResultConfig(dest, tools) {
1906
2192
  if (dest.includeFiles !== void 0) result.includeFiles = structuredClone(dest.includeFiles);
1907
2193
  return result;
1908
2194
  }
1909
- function mergeConfigs(source, dest) {
2195
+ function mergeConfigs(source, dest, options = {}) {
2196
+ const preferDest = options.preferDestParameters === true;
1910
2197
  const sourceMap = indexByToolId(source.tools);
1911
2198
  const seen = /* @__PURE__ */ new Set();
1912
2199
  const tools = [];
1913
2200
  for (const d of dest.tools) {
1914
2201
  const s = sourceMap.get(d.toolId);
1915
- tools.push(s ? combineTool("merge", d.toolId, s, d) : cloneTool(d));
2202
+ tools.push(s ? combineTool("merge", d.toolId, s, d, preferDest) : cloneTool(d));
1916
2203
  seen.add(d.toolId);
1917
2204
  }
1918
2205
  for (const s of source.tools) {
@@ -1970,16 +2257,49 @@ function applyConfigOperation(op, source, dest) {
1970
2257
  }
1971
2258
  }
1972
2259
  }
2260
+ function pruneEmptyTools(config) {
2261
+ const tools = config.tools.filter((t) => !isEmptyTool(t));
2262
+ if (tools.length === config.tools.length) return config;
2263
+ return { ...config, tools };
2264
+ }
2265
+ function threeWayList(base = [], current = [], next = []) {
2266
+ const baseSet = new Set(base);
2267
+ const nextSet = new Set(next);
2268
+ const removed = new Set(base.filter((x) => !nextSet.has(x)));
2269
+ const added = next.filter((x) => !baseSet.has(x));
2270
+ const kept = current.filter((x) => !removed.has(x));
2271
+ const seen = /* @__PURE__ */ new Set();
2272
+ const result = [];
2273
+ for (const x of [...kept, ...added]) {
2274
+ if (!seen.has(x)) {
2275
+ seen.add(x);
2276
+ result.push(x);
2277
+ }
2278
+ }
2279
+ return result;
2280
+ }
2281
+ function updateConfigIncremental(base, current, next) {
2282
+ const added = diffConfigs(base, next);
2283
+ const removed = diffConfigs(next, base);
2284
+ const afterRemoval = diffConfigs(removed, current);
2285
+ const merged = mergeConfigs(added, afterRemoval, { preferDestParameters: true });
2286
+ const exclude = threeWayList(base.exclude, current.exclude, next.exclude);
2287
+ const result = { ...merged };
2288
+ if (exclude.length > 0) result.exclude = exclude;
2289
+ else delete result.exclude;
2290
+ result.metadata = { ...merged.metadata, languages: [...next.metadata.languages] };
2291
+ return pruneEmptyTools(result);
2292
+ }
1973
2293
 
1974
2294
  // src/init.ts
1975
2295
  var import_promises4 = __toESM(require("fs/promises"));
1976
- var import_path7 = __toESM(require("path"));
2296
+ var import_path8 = __toESM(require("path"));
1977
2297
  var import_child_process4 = require("child_process");
1978
2298
  var import_tooling5 = require("@codacy/tooling");
1979
2299
 
1980
2300
  // src/discover.ts
1981
2301
  var import_promises2 = __toESM(require("fs/promises"));
1982
- var import_path5 = __toESM(require("path"));
2302
+ var import_path6 = __toESM(require("path"));
1983
2303
 
1984
2304
  // src/discover-catalog.ts
1985
2305
  var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
@@ -2009,6 +2329,10 @@ var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
2009
2329
  ["@hapi/hapi", { displayName: "Hapi", kind: "framework" }],
2010
2330
  ["hono", { displayName: "Hono", kind: "framework" }],
2011
2331
  ["electron", { displayName: "Electron", kind: "framework" }],
2332
+ // AWS CDK — the v2 umbrella package `aws-cdk-lib`, the CLI package `aws-cdk`,
2333
+ // and the v1 `@aws-cdk/*` construct packages (prefix rule below).
2334
+ ["aws-cdk-lib", { displayName: "AWS CDK", kind: "framework" }],
2335
+ ["aws-cdk", { displayName: "AWS CDK", kind: "framework" }],
2012
2336
  // --- JavaScript / TypeScript — Libraries ---
2013
2337
  ["lodash", { displayName: "Lodash", kind: "library" }],
2014
2338
  ["underscore", { displayName: "Underscore.js", kind: "library" }],
@@ -2061,9 +2385,20 @@ var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
2061
2385
  ["pino", { displayName: "Pino", kind: "library" }],
2062
2386
  ["winston", { displayName: "Winston", kind: "library" }],
2063
2387
  ["i18next", { displayName: "i18next", kind: "library" }],
2064
- ["react-intl", { displayName: "react-intl", kind: "library" }],
2388
+ // react-intl is the React binding of the FormatJS ecosystem; eslint-plugin-formatjs
2389
+ // lints both, so we report the umbrella name "formatjs" (matches the mapping key).
2390
+ ["react-intl", { displayName: "formatjs", kind: "library" }],
2065
2391
  ["vue-i18n", { displayName: "vue-i18n", kind: "library" }],
2066
2392
  ["next-intl", { displayName: "next-intl", kind: "library" }],
2393
+ // Type checker (eslint-plugin-flowtype). Also detected via a `.flowconfig` file.
2394
+ ["flow-bin", { displayName: "Flow", kind: "library" }],
2395
+ // Legacy JS libraries with dedicated ESLint plugins.
2396
+ ["backbone", { displayName: "Backbone.js", kind: "library" }],
2397
+ ["chai", { displayName: "Chai", kind: "library" }],
2398
+ ["codeceptjs", { displayName: "CodeceptJS", kind: "library" }],
2399
+ ["webdriverio", { displayName: "WebdriverIO", kind: "library" }],
2400
+ ["react-relay", { displayName: "Relay", kind: "library" }],
2401
+ ["relay-runtime", { displayName: "Relay", kind: "library" }],
2067
2402
  // --- JavaScript / TypeScript — Tools ---
2068
2403
  ["typescript", { displayName: "TypeScript", kind: "tool" }],
2069
2404
  ["eslint", { displayName: "ESLint", kind: "tool" }],
@@ -2275,7 +2610,9 @@ var DEPENDENCY_CATALOG = /* @__PURE__ */ new Map([
2275
2610
  ["cats-core", { displayName: "Cats", kind: "library" }],
2276
2611
  ["circe-core", { displayName: "Circe", kind: "library" }],
2277
2612
  // --- Dart — Frameworks ---
2278
- // `flutter` and `angular` appear as plain keys in pubspec.yaml.
2613
+ // `flutter` and `angular` appear as plain keys in pubspec.yaml. NOTE: `angular`
2614
+ // here is Dart's AngularDart. The npm package `angular` is AngularJS (1.x) — a
2615
+ // different framework — so package.json overrides this via MANIFEST_SCOPED_CATALOG.
2279
2616
  ["flutter", { displayName: "Flutter", kind: "framework" }],
2280
2617
  ["flutter_test", { displayName: "Flutter", kind: "framework" }],
2281
2618
  ["angular", { displayName: "Angular", kind: "framework" }],
@@ -2312,10 +2649,13 @@ var DEPENDENCY_PREFIX_RULES = [
2312
2649
  classification: { displayName: "Testing Library", kind: "library" }
2313
2650
  },
2314
2651
  { prefix: "@tanstack/", classification: { displayName: "TanStack", kind: "library" } },
2652
+ { prefix: "@wdio/", classification: { displayName: "WebdriverIO", kind: "library" } },
2653
+ // AWS CDK v1 construct libraries are published under the @aws-cdk/ scope.
2654
+ { prefix: "@aws-cdk/", classification: { displayName: "AWS CDK", kind: "framework" } },
2315
2655
  { prefix: "@reduxjs/", classification: { displayName: "Redux", kind: "library" } },
2316
2656
  { prefix: "@prisma/", classification: { displayName: "Prisma", kind: "library" } },
2317
2657
  { prefix: "@apollo/", classification: { displayName: "Apollo", kind: "library" } },
2318
- { prefix: "@formatjs/", classification: { displayName: "react-intl", kind: "library" } },
2658
+ { prefix: "@formatjs/", classification: { displayName: "formatjs", kind: "library" } },
2319
2659
  { prefix: "@lingui/", classification: { displayName: "Lingui", kind: "library" } },
2320
2660
  { prefix: "@emotion/", classification: { displayName: "Emotion", kind: "library" } },
2321
2661
  { prefix: "@mui/", classification: { displayName: "Material UI", kind: "library" } },
@@ -2358,7 +2698,14 @@ var DEPENDENCY_PREFIX_RULES = [
2358
2698
  { prefix: "MSTest.", classification: { displayName: "MSTest", kind: "library" } },
2359
2699
  { prefix: "Serilog", classification: { displayName: "Serilog", kind: "library" } }
2360
2700
  ];
2361
- function classifyDependency(name) {
2701
+ var MANIFEST_SCOPED_CATALOG = /* @__PURE__ */ new Map([
2702
+ ["package.json", /* @__PURE__ */ new Map([["angular", { displayName: "AngularJS", kind: "framework" }]])]
2703
+ ]);
2704
+ function classifyDependency(name, manifestBasename) {
2705
+ if (manifestBasename) {
2706
+ const scoped = MANIFEST_SCOPED_CATALOG.get(manifestBasename)?.get(name);
2707
+ if (scoped) return scoped;
2708
+ }
2362
2709
  const exact = DEPENDENCY_CATALOG.get(name);
2363
2710
  if (exact) return exact;
2364
2711
  for (const rule of DEPENDENCY_PREFIX_RULES) {
@@ -2366,6 +2713,91 @@ function classifyDependency(name) {
2366
2713
  }
2367
2714
  return void 0;
2368
2715
  }
2716
+ function pathBasename(filePath) {
2717
+ return filePath.split("/").pop() ?? filePath;
2718
+ }
2719
+ var FILE_SIGNAL_RULES = [
2720
+ {
2721
+ name: ".flowconfig",
2722
+ matches: (f) => pathBasename(f) === ".flowconfig",
2723
+ classification: { displayName: "Flow", kind: "library" }
2724
+ },
2725
+ {
2726
+ name: "cdk.json",
2727
+ matches: (f) => pathBasename(f) === "cdk.json",
2728
+ classification: { displayName: "AWS CDK", kind: "framework" }
2729
+ },
2730
+ {
2731
+ name: ".meteor/release",
2732
+ matches: (f) => f === ".meteor/release" || f.endsWith("/.meteor/release"),
2733
+ classification: { displayName: "Meteor", kind: "framework" }
2734
+ },
2735
+ {
2736
+ name: "sfdx-project.json",
2737
+ matches: (f) => pathBasename(f) === "sfdx-project.json",
2738
+ classification: { displayName: "Salesforce", kind: "platform" }
2739
+ },
2740
+ {
2741
+ name: "lwc.config.json",
2742
+ matches: (f) => pathBasename(f) === "lwc.config.json",
2743
+ classification: { displayName: "Lightning Web Components", kind: "platform" }
2744
+ },
2745
+ {
2746
+ name: "suitecloud.config.js",
2747
+ matches: (f) => pathBasename(f) === "suitecloud.config.js",
2748
+ classification: { displayName: "SuiteScript", kind: "platform" }
2749
+ }
2750
+ ];
2751
+ function detectFileSignals(files) {
2752
+ const result = [];
2753
+ for (const rule of FILE_SIGNAL_RULES) {
2754
+ const hit = files.find((f) => rule.matches(f));
2755
+ if (!hit) continue;
2756
+ result.push({
2757
+ name: rule.name,
2758
+ displayName: rule.classification.displayName,
2759
+ kind: rule.classification.kind,
2760
+ source: "file",
2761
+ manifestFile: hit
2762
+ });
2763
+ }
2764
+ return result;
2765
+ }
2766
+ var PACKAGE_JSON_SIGNAL_DISPLAY_NAMES = ["ESLint Plugin"];
2767
+ function isEslintPluginName(name) {
2768
+ return /^eslint-plugin-/.test(name) || /^@[^/]+\/eslint-plugin(-.*)?$/.test(name);
2769
+ }
2770
+ function detectPackageJsonSignals(content) {
2771
+ let pkg;
2772
+ try {
2773
+ pkg = JSON.parse(content);
2774
+ } catch {
2775
+ return [];
2776
+ }
2777
+ const name = pkg && typeof pkg.name === "string" ? pkg.name : "";
2778
+ if (name && isEslintPluginName(name)) {
2779
+ return [
2780
+ {
2781
+ name,
2782
+ displayName: "ESLint Plugin",
2783
+ kind: "tool",
2784
+ source: "manifest",
2785
+ manifestFile: "package.json"
2786
+ }
2787
+ ];
2788
+ }
2789
+ return [];
2790
+ }
2791
+ function getKnownDisplayNames() {
2792
+ const names = /* @__PURE__ */ new Set();
2793
+ for (const { displayName } of DEPENDENCY_CATALOG.values()) names.add(displayName);
2794
+ for (const scoped of MANIFEST_SCOPED_CATALOG.values())
2795
+ for (const { displayName } of scoped.values()) names.add(displayName);
2796
+ for (const rule of DEPENDENCY_PREFIX_RULES) names.add(rule.classification.displayName);
2797
+ for (const rule of FILE_SIGNAL_RULES) names.add(rule.classification.displayName);
2798
+ for (const name of PACKAGE_JSON_SIGNAL_DISPLAY_NAMES) names.add(name);
2799
+ return names;
2800
+ }
2369
2801
  var NOTABLE_ANY_DEPTH = /* @__PURE__ */ new Set([
2370
2802
  "Dockerfile",
2371
2803
  "docker-compose.yml",
@@ -2735,7 +3167,8 @@ async function discover(repositoryRoot, options) {
2735
3167
  let frameworks = [];
2736
3168
  let libraries = [];
2737
3169
  if (!options?.skipDependencies) {
2738
- const allDeps = await extractDependencies(repositoryRoot, files);
3170
+ const manifestDeps = await extractDependencies(repositoryRoot, files);
3171
+ const allDeps = [...manifestDeps, ...detectFileSignals(files)];
2739
3172
  frameworks = deduplicateDeps(allDeps.filter((d) => d.kind === "framework"));
2740
3173
  libraries = deduplicateDeps(
2741
3174
  allDeps.filter((d) => d.kind === "library" || d.kind === "platform" || d.kind === "tool")
@@ -2748,7 +3181,7 @@ async function discover(repositoryRoot, options) {
2748
3181
  frameworks,
2749
3182
  libraries,
2750
3183
  metadata: {
2751
- repositoryRoot: import_path5.default.resolve(repositoryRoot),
3184
+ repositoryRoot: import_path6.default.resolve(repositoryRoot),
2752
3185
  totalFiles: files.length,
2753
3186
  scannedAt: (/* @__PURE__ */ new Date()).toISOString()
2754
3187
  }
@@ -2757,7 +3190,7 @@ async function discover(repositoryRoot, options) {
2757
3190
  function extractFilePatterns(files) {
2758
3191
  const extensions = /* @__PURE__ */ new Set();
2759
3192
  for (const file of files) {
2760
- const ext = import_path5.default.extname(file).toLowerCase();
3193
+ const ext = import_path6.default.extname(file).toLowerCase();
2761
3194
  if (ext) {
2762
3195
  extensions.add(`*${ext}`);
2763
3196
  }
@@ -2790,13 +3223,14 @@ async function extractDependencies(repositoryRoot, files) {
2790
3223
  if (!parser) continue;
2791
3224
  let content;
2792
3225
  try {
2793
- content = await import_promises2.default.readFile(import_path5.default.join(repositoryRoot, filePath), "utf-8");
3226
+ content = await import_promises2.default.readFile(import_path6.default.join(repositoryRoot, filePath), "utf-8");
2794
3227
  } catch {
2795
3228
  continue;
2796
3229
  }
3230
+ const basename = filePath.split("/").pop() ?? filePath;
2797
3231
  const rawDeps = parser(content);
2798
3232
  for (const raw of rawDeps) {
2799
- const classification = classifyDependency(raw.name);
3233
+ const classification = classifyDependency(raw.name, basename);
2800
3234
  if (classification) {
2801
3235
  allDeps.push({
2802
3236
  name: raw.name,
@@ -2808,6 +3242,11 @@ async function extractDependencies(repositoryRoot, files) {
2808
3242
  });
2809
3243
  }
2810
3244
  }
3245
+ if (basename === "package.json") {
3246
+ for (const signal of detectPackageJsonSignals(content)) {
3247
+ allDeps.push({ ...signal, manifestFile: filePath });
3248
+ }
3249
+ }
2811
3250
  }
2812
3251
  return allDeps;
2813
3252
  }
@@ -2825,14 +3264,14 @@ function deduplicateDeps(deps) {
2825
3264
 
2826
3265
  // src/codacy-yaml.ts
2827
3266
  var import_promises3 = __toESM(require("fs/promises"));
2828
- var import_path6 = __toESM(require("path"));
3267
+ var import_path7 = __toESM(require("path"));
2829
3268
  var import_yaml = require("yaml");
2830
3269
  async function readCodacyYaml(repositoryRoot) {
2831
3270
  const candidates = [".codacy.yaml", ".codacy.yml"];
2832
3271
  let raw = null;
2833
3272
  for (const filename of candidates) {
2834
3273
  try {
2835
- raw = await import_promises3.default.readFile(import_path6.default.join(repositoryRoot, filename), "utf-8");
3274
+ raw = await import_promises3.default.readFile(import_path7.default.join(repositoryRoot, filename), "utf-8");
2836
3275
  break;
2837
3276
  } catch {
2838
3277
  }
@@ -3090,13 +3529,13 @@ var getQueryString = (params) => {
3090
3529
  };
3091
3530
  var getUrl = (config, options) => {
3092
3531
  const encoder = config.ENCODE_PATH || encodeURI;
3093
- const path9 = options.url.replace("{api-version}", config.VERSION).replace(/{(.*?)}/g, (substring, group) => {
3532
+ const path10 = options.url.replace("{api-version}", config.VERSION).replace(/{(.*?)}/g, (substring, group) => {
3094
3533
  if (options.path?.hasOwnProperty(group)) {
3095
3534
  return encoder(String(options.path[group]));
3096
3535
  }
3097
3536
  return substring;
3098
3537
  });
3099
- const url = `${config.BASE}${path9}`;
3538
+ const url = `${config.BASE}${path10}`;
3100
3539
  if (options.query) {
3101
3540
  return `${url}${getQueryString(options.query)}`;
3102
3541
  }
@@ -6490,8 +6929,8 @@ async function detectLanguages(repositoryRoot, precomputedFiles) {
6490
6929
  }
6491
6930
  const detected = /* @__PURE__ */ new Set();
6492
6931
  for (const file of files) {
6493
- const basename = import_path7.default.basename(file);
6494
- const ext = import_path7.default.extname(file).toLowerCase();
6932
+ const basename = import_path8.default.basename(file);
6933
+ const ext = import_path8.default.extname(file).toLowerCase();
6495
6934
  for (const [lang, config] of Object.entries(import_tooling5.LANGUAGE_CONFIG)) {
6496
6935
  if (config.extensions.includes(ext)) {
6497
6936
  detected.add(lang);
@@ -6508,12 +6947,12 @@ async function detectLocalConfigFiles(repositoryRoot, adapters) {
6508
6947
  adapters.map(async (adapter) => {
6509
6948
  const result = await adapter.checkLocalConfigurationFile({
6510
6949
  repositoryRoot,
6511
- generatedConfigDir: import_path7.default.join(repositoryRoot, ".codacy", "generated"),
6512
- globalDir: import_path7.default.join(process.env.HOME ?? "~", ".codacy"),
6513
- toolInstallDir: import_path7.default.join(process.env.HOME ?? "~", ".codacy", "tools", adapter.id),
6950
+ generatedConfigDir: import_path8.default.join(repositoryRoot, ".codacy", "generated"),
6951
+ globalDir: import_path8.default.join(process.env.HOME ?? "~", ".codacy"),
6952
+ toolInstallDir: import_path8.default.join(process.env.HOME ?? "~", ".codacy", "tools", adapter.id),
6514
6953
  targetFiles: [],
6515
6954
  platform: { os: process.platform, arch: process.arch },
6516
- logger: silentLogger,
6955
+ logger: createNullLogger(),
6517
6956
  runner: "local"
6518
6957
  });
6519
6958
  return { toolId: adapter.id, result };
@@ -6526,16 +6965,6 @@ async function detectLocalConfigFiles(repositoryRoot, adapters) {
6526
6965
  }
6527
6966
  return found;
6528
6967
  }
6529
- var silentLogger = {
6530
- debug: () => {
6531
- },
6532
- info: () => {
6533
- },
6534
- warn: () => {
6535
- },
6536
- error: () => {
6537
- }
6538
- };
6539
6968
  async function enrichWithCodacyYaml(config, repositoryRoot, adapters) {
6540
6969
  const yamlConfig = await readCodacyYaml(repositoryRoot);
6541
6970
  if (!yamlConfig) return { config, codacyYamlApplied: false };
@@ -6714,7 +7143,7 @@ async function initRemoteConfig(repositoryRoot, provider, organization, reposito
6714
7143
  };
6715
7144
  }
6716
7145
  async function initContainerConfig(repositoryRoot, adapters) {
6717
- const codacyrcPath = import_path7.default.join(repositoryRoot, ".codacyrc");
7146
+ const codacyrcPath = import_path8.default.join(repositoryRoot, ".codacyrc");
6718
7147
  let raw;
6719
7148
  try {
6720
7149
  raw = await import_promises4.default.readFile(codacyrcPath, "utf-8");
@@ -7045,110 +7474,71 @@ function parseAutoFilters(raw) {
7045
7474
  return { filters: { severities, categories }, unknown };
7046
7475
  }
7047
7476
 
7048
- // src/logger.ts
7049
- var import_fs2 = __toESM(require("fs"));
7050
- var import_path8 = __toESM(require("path"));
7051
- var import_os2 = __toESM(require("os"));
7052
- var LEVEL_ORDER = {
7053
- debug: 0,
7054
- info: 1,
7055
- warning: 2,
7056
- error: 3
7057
- };
7058
- function rotateLogFiles(logsDir, maxFiles) {
7059
- let entries2;
7477
+ // src/index.ts
7478
+ var import_tooling6 = require("@codacy/tooling");
7479
+
7480
+ // src/credentials.ts
7481
+ var import_crypto = __toESM(require("crypto"));
7482
+ var import_promises5 = __toESM(require("fs/promises"));
7483
+ var import_os3 = __toESM(require("os"));
7484
+ var import_path9 = __toESM(require("path"));
7485
+ function credentialsPath() {
7486
+ return import_path9.default.join(import_os3.default.homedir(), ".codacy", "credentials");
7487
+ }
7488
+ function deriveKey(salt) {
7489
+ const info = import_os3.default.userInfo();
7490
+ const machineKey = [import_os3.default.hostname(), info.username, import_os3.default.homedir(), import_os3.default.platform()].join("|");
7491
+ return import_crypto.default.pbkdf2Sync(machineKey, salt, 1e5, 32, "sha512");
7492
+ }
7493
+ async function saveCredentials(token) {
7494
+ const credentialsFile = credentialsPath();
7495
+ const salt = import_crypto.default.randomBytes(32);
7496
+ const iv = import_crypto.default.randomBytes(16);
7497
+ const key = deriveKey(salt);
7498
+ const cipher = import_crypto.default.createCipheriv("aes-256-gcm", key, iv);
7499
+ const encrypted = Buffer.concat([cipher.update(token, "utf-8"), cipher.final()]);
7500
+ const authTag = cipher.getAuthTag();
7501
+ const payload = JSON.stringify({
7502
+ salt: salt.toString("hex"),
7503
+ iv: iv.toString("hex"),
7504
+ authTag: authTag.toString("hex"),
7505
+ encrypted: encrypted.toString("hex")
7506
+ });
7507
+ await import_promises5.default.mkdir(import_path9.default.dirname(credentialsFile), { recursive: true });
7508
+ await import_promises5.default.writeFile(credentialsFile, payload, { encoding: "utf-8", mode: 384 });
7509
+ }
7510
+ async function loadCredentials() {
7060
7511
  try {
7061
- entries2 = import_fs2.default.readdirSync(logsDir).filter((f) => f.startsWith("analysis-") && f.endsWith(".log"));
7512
+ const raw = await import_promises5.default.readFile(credentialsPath(), "utf-8");
7513
+ const data = JSON.parse(raw);
7514
+ if (!data || typeof data !== "object" || Array.isArray(data)) return null;
7515
+ const salt = Buffer.from(data.salt, "hex");
7516
+ const iv = Buffer.from(data.iv, "hex");
7517
+ const authTag = Buffer.from(data.authTag, "hex");
7518
+ const encrypted = Buffer.from(data.encrypted, "hex");
7519
+ const key = deriveKey(salt);
7520
+ const decipher = import_crypto.default.createDecipheriv("aes-256-gcm", key, iv);
7521
+ decipher.setAuthTag(authTag);
7522
+ const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
7523
+ return decrypted.toString("utf-8");
7062
7524
  } catch {
7063
- return;
7525
+ return null;
7064
7526
  }
7065
- if (entries2.length <= maxFiles) return;
7066
- entries2.sort();
7067
- const toRemove = entries2.slice(0, entries2.length - maxFiles);
7068
- for (const f of toRemove) {
7069
- try {
7070
- import_fs2.default.unlinkSync(import_path8.default.join(logsDir, f));
7071
- } catch {
7072
- }
7073
- }
7074
- }
7075
- var fileSeq = 0;
7076
- function fileTimestamp() {
7077
- return `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${fileSeq++}`;
7078
- }
7079
- function enrichMessage(message, fields) {
7080
- if (!fields) return message;
7081
- const parts = [];
7082
- if (fields.toolId) parts.push(String(fields.toolId));
7083
- if (fields.version) parts.push(`v${fields.version}`);
7084
- if (fields.installation) parts.push(String(fields.installation));
7085
- if (fields.configSource) parts.push(String(fields.configSource));
7086
- if (typeof fields.fileCount === "number") parts.push(`${fields.fileCount} files`);
7087
- if (typeof fields.issueCount === "number") parts.push(`${fields.issueCount} issues`);
7088
- if (typeof fields.errorCount === "number" && fields.errorCount > 0)
7089
- parts.push(`${fields.errorCount} errors`);
7090
- if (typeof fields.totalIssues === "number") parts.push(`${fields.totalIssues} issues`);
7091
- if (typeof fields.totalErrors === "number" && fields.totalErrors > 0)
7092
- parts.push(`${fields.totalErrors} errors`);
7093
- if (typeof fields.durationMs === "number")
7094
- parts.push(`${(fields.durationMs / 1e3).toFixed(1)}s`);
7095
- if (fields.reason) parts.push(String(fields.reason));
7096
- if (fields.error) parts.push(String(fields.error));
7097
- if (typeof fields.found === "boolean") parts.push(fields.found ? "found" : "not found");
7098
- if (fields.path && !fields.version) parts.push(String(fields.path));
7099
- if (parts.length === 0) return message;
7100
- return `${message} (${parts.join(", ")})`;
7101
7527
  }
7102
- function createLogger(options = {}) {
7103
- const minLevel = LEVEL_ORDER[options.level ?? "info"];
7104
- const fileEnabled = options.fileEnabled ?? true;
7105
- const logsDir = options.logsDir ?? import_path8.default.join(import_os2.default.homedir(), ".codacy", "logs");
7106
- const maxFileSize = options.maxFileSize ?? 10 * 1024 * 1024;
7107
- const maxFiles = options.maxFiles ?? 5;
7108
- const onStderrMessage = options.onStderrMessage;
7109
- let logFd = null;
7110
- let bytesWritten = 0;
7111
- if (fileEnabled) {
7112
- import_fs2.default.mkdirSync(logsDir, { recursive: true });
7113
- rotateLogFiles(logsDir, maxFiles);
7114
- const logFilePath = import_path8.default.join(logsDir, `analysis-${fileTimestamp()}.log`);
7115
- logFd = import_fs2.default.openSync(logFilePath, "a");
7116
- }
7117
- function log(level, message, fields) {
7118
- const numLevel = LEVEL_ORDER[level];
7119
- if (numLevel < minLevel) return;
7120
- if (fileEnabled && logFd !== null) {
7121
- const entry = JSON.stringify({
7122
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
7123
- level,
7124
- message,
7125
- ...fields
7126
- });
7127
- const buf = Buffer.from(entry + "\n", "utf-8");
7128
- import_fs2.default.writeSync(logFd, buf);
7129
- bytesWritten += buf.length;
7130
- if (bytesWritten >= maxFileSize) {
7131
- import_fs2.default.closeSync(logFd);
7132
- rotateLogFiles(logsDir, maxFiles);
7133
- const newPath = import_path8.default.join(logsDir, `analysis-${fileTimestamp()}.log`);
7134
- logFd = import_fs2.default.openSync(newPath, "a");
7135
- bytesWritten = 0;
7136
- }
7137
- }
7138
- const enriched = enrichMessage(message, fields);
7139
- if (onStderrMessage) {
7140
- onStderrMessage(enriched, level);
7141
- } else {
7142
- process.stderr.write(` ${enriched}
7143
- `);
7144
- }
7528
+ async function deleteCredentials() {
7529
+ try {
7530
+ await import_promises5.default.unlink(credentialsPath());
7531
+ return true;
7532
+ } catch {
7533
+ return false;
7145
7534
  }
7146
- return {
7147
- debug: (msg, fields) => log("debug", msg, fields),
7148
- info: (msg, fields) => log("info", msg, fields),
7149
- warn: (msg, fields) => log("warning", msg, fields),
7150
- error: (msg, fields) => log("error", msg, fields)
7151
- };
7535
+ }
7536
+ async function resolveApiToken() {
7537
+ const envToken = process.env.CODACY_API_TOKEN;
7538
+ if (envToken) return envToken;
7539
+ const stored = await loadCredentials();
7540
+ if (stored) return stored;
7541
+ throw new Error("No API token found. Set CODACY_API_TOKEN or run 'codacy-analysis login'.");
7152
7542
  }
7153
7543
  // Annotate the CommonJS export names for ESM import in node:
7154
7544
  0 && (module.exports = {
@@ -7159,14 +7549,18 @@ function createLogger(options = {}) {
7159
7549
  clearAdapters,
7160
7550
  computePatternSummary,
7161
7551
  configureApiToken,
7552
+ configureProxy,
7162
7553
  createCodacyConfig,
7163
7554
  createLogger,
7555
+ createNullLogger,
7556
+ deleteCredentials,
7164
7557
  detectLanguages,
7165
7558
  diffConfigs,
7166
7559
  discover,
7167
7560
  formatOutput,
7168
7561
  formatTextSummary,
7169
7562
  getAdapter,
7563
+ getKnownDisplayNames,
7170
7564
  getRegisteredAdapters,
7171
7565
  getRegisteredDescriptors,
7172
7566
  initAutoConfig,
@@ -7179,17 +7573,26 @@ function createLogger(options = {}) {
7179
7573
  listIgnoredFiles,
7180
7574
  listPrFiles,
7181
7575
  listStagedFiles,
7576
+ loadCredentials,
7182
7577
  mergeConfigs,
7183
7578
  parseAutoFilters,
7579
+ pruneEmptyTools,
7580
+ readBaselineConfig,
7184
7581
  readCodacyConfig,
7185
7582
  readCodacyYaml,
7186
7583
  registerAdapter,
7187
7584
  registerLazyAdapter,
7188
7585
  registerUnsupportedTool,
7586
+ resolveApiToken,
7587
+ resolveBaselinePath,
7189
7588
  resolveConfigPath,
7190
7589
  resolveEngineToAdapter,
7590
+ resolveProxyConfig,
7191
7591
  rotateLogFiles,
7592
+ saveCredentials,
7593
+ updateConfigIncremental,
7192
7594
  validateApiToken,
7595
+ writeBaselineConfig,
7193
7596
  writeCodacyConfig,
7194
7597
  writeCodacyConfigFile
7195
7598
  });