@konvert7/klint 0.35.2 → 0.37.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/core/stats.ts CHANGED
@@ -6,6 +6,7 @@ export const STATS_DIR = ".klint";
6
6
  export const STATS_FILE = "stats.json";
7
7
  const SELF_IGNORE = "# Created by klint automatically.\n*\n";
8
8
  const STATS_VERSION = 1;
9
+ const KLINT_ADVISORY_PREFIX = "klint/";
9
10
 
10
11
  export type RunSource = "hook" | "direct";
11
12
 
@@ -23,18 +24,31 @@ interface OpenError {
23
24
  export interface KlintStats {
24
25
  version: typeof STATS_VERSION;
25
26
  runs: Record<RunSource, number>;
26
- prevented: {
27
- byRule: Record<string, number>;
28
- bySource: Record<RunSource, number>;
29
- };
27
+ reported: RuleCounts;
28
+ prevented: RuleCounts;
30
29
  open: OpenError[];
31
30
  }
32
31
 
32
+ interface RuleCounts {
33
+ byRule: Record<string, number>;
34
+ bySource: Record<RunSource, number>;
35
+ }
36
+
37
+ function emptyCounts(): RuleCounts {
38
+ return { byRule: {}, bySource: { hook: 0, direct: 0 } };
39
+ }
40
+
41
+ function bump(counts: RuleCounts, rule: string, source: RunSource): void {
42
+ counts.byRule[rule] = (counts.byRule[rule] ?? 0) + 1;
43
+ counts.bySource[source] += 1;
44
+ }
45
+
33
46
  export function emptyStats(): KlintStats {
34
47
  return {
35
48
  version: STATS_VERSION,
36
49
  runs: { hook: 0, direct: 0 },
37
- prevented: { byRule: {}, bySource: { hook: 0, direct: 0 } },
50
+ reported: emptyCounts(),
51
+ prevented: emptyCounts(),
38
52
  open: [],
39
53
  };
40
54
  }
@@ -43,6 +57,10 @@ function sameError(a: Omit<OpenError, "caughtBy">, b: Omit<OpenError, "caughtBy"
43
57
  return a.rule === b.rule && a.file === b.file && a.message === b.message;
44
58
  }
45
59
 
60
+ function isCodeViolation(violation: CountedViolation): boolean {
61
+ return !violation.rule.startsWith(KLINT_ADVISORY_PREFIX);
62
+ }
63
+
46
64
  function errorsOf(
47
65
  violations: readonly CountedViolation[]
48
66
  ): Omit<OpenError, "caughtBy">[] {
@@ -58,14 +76,16 @@ export function nextStats(
58
76
  ): KlintStats {
59
77
  const stats = structuredClone(previous);
60
78
  stats.runs[source] += 1;
79
+ for (const violation of violations.filter(isCodeViolation)) {
80
+ bump(stats.reported, violation.rule, source);
81
+ }
61
82
 
62
83
  const unmatched = errorsOf(violations);
63
84
  const stillOpen: OpenError[] = [];
64
85
  for (const error of previous.open) {
65
86
  const match = unmatched.findIndex((current) => sameError(current, error));
66
87
  if (match === -1) {
67
- stats.prevented.byRule[error.rule] = (stats.prevented.byRule[error.rule] ?? 0) + 1;
68
- stats.prevented.bySource[error.caughtBy] += 1;
88
+ bump(stats.prevented, error.rule, error.caughtBy);
69
89
  continue;
70
90
  }
71
91
  unmatched.splice(match, 1);
@@ -78,6 +98,15 @@ export function nextStats(
78
98
  return stats;
79
99
  }
80
100
 
101
+ function isRuleCounts(value: unknown): value is RuleCounts {
102
+ const candidate = value as Partial<RuleCounts> | undefined;
103
+ return (
104
+ typeof candidate?.byRule === "object" &&
105
+ typeof candidate.bySource?.hook === "number" &&
106
+ typeof candidate.bySource.direct === "number"
107
+ );
108
+ }
109
+
81
110
  function isStats(value: unknown): value is KlintStats {
82
111
  if (!value || typeof value !== "object") return false;
83
112
  const candidate = value as Partial<KlintStats>;
@@ -85,19 +114,22 @@ function isStats(value: unknown): value is KlintStats {
85
114
  candidate.version === STATS_VERSION &&
86
115
  typeof candidate.runs?.hook === "number" &&
87
116
  typeof candidate.runs.direct === "number" &&
88
- typeof candidate.prevented?.byRule === "object" &&
89
- typeof candidate.prevented.bySource?.hook === "number" &&
90
- typeof candidate.prevented.bySource.direct === "number" &&
117
+ isRuleCounts(candidate.prevented) &&
118
+ (candidate.reported === undefined || isRuleCounts(candidate.reported)) &&
91
119
  Array.isArray(candidate.open)
92
120
  );
93
121
  }
94
122
 
123
+ function withReportedCounts(stats: KlintStats): KlintStats {
124
+ return { ...stats, reported: stats.reported ?? emptyCounts() };
125
+ }
126
+
95
127
  export function readStats(configDir: string): KlintStats {
96
128
  try {
97
129
  const parsed: unknown = JSON.parse(
98
130
  readFileSync(join(configDir, STATS_DIR, STATS_FILE), "utf-8")
99
131
  );
100
- return isStats(parsed) ? parsed : emptyStats();
132
+ return isStats(parsed) ? withReportedCounts(parsed) : emptyStats();
101
133
  } catch {
102
134
  return emptyStats();
103
135
  }