-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathschema.ts
More file actions
265 lines (261 loc) · 9.51 KB
/
Copy pathschema.ts
File metadata and controls
265 lines (261 loc) · 9.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
import { severities } from "../core/severity.js";
import { gatePresets } from "../core/gatePresets.js";
import { detectorIds } from "../detectors/index.js";
import { listRulePacks, RULE_PACK_IDS } from "./packs.js";
import { defaultConfig } from "./defaults.js";
export const SCHEMA_ID =
"https://raw.githubusercontent.com/ColumbusLabs/DebtLens/main/schema/debtlens.config.schema.json";
/**
* Build the JSON Schema for `debtlens.config.json`. Generated from the live rule list,
* severity set, and default threshold keys so it cannot drift from the code. A test
* asserts the committed schema file matches this output.
*/
export function buildConfigSchema(): Record<string, unknown> {
const knownThresholds = Object.fromEntries(
Object.keys({
...defaultConfig.thresholds,
...Object.assign({}, ...listRulePacks().map((pack) => pack.thresholds ?? {})),
}).map((key) => [key, { type: "number" }]),
);
const packAlternation = RULE_PACK_IDS.map(escapeRegex).join("|");
const severityValue = { enum: [...severities] };
const confidenceFloorValue = { type: "number", minimum: 0, maximum: 1 };
const knownRuleSeverities = Object.fromEntries(detectorIds.map((id) => [id, severityValue]));
const knownRuleConfidenceFloors = Object.fromEntries(detectorIds.map((id) => [id, confidenceFloorValue]));
return {
$schema: "http://json-schema.org/draft-07/schema#",
$id: SCHEMA_ID,
title: "DebtLens configuration",
description: "Configuration for the DebtLens static-analysis CLI.",
type: "object",
additionalProperties: false,
properties: {
$schema: { type: "string" },
include: {
type: "array",
items: { type: "string" },
description: "Glob patterns to scan.",
},
exclude: {
type: "array",
items: { type: "string" },
description: "Glob patterns to skip.",
},
minSeverity: {
enum: [...severities],
description: "Lowest severity to report.",
},
pack: {
anyOf: [
{ enum: [...RULE_PACK_IDS] },
{ type: "string", pattern: `^\\s*(?:${packAlternation})(?:\\s*,\\s*(?:${packAlternation}))*\\s*$` },
],
description: "Built-in rule pack preset, or a comma-separated list of presets. Explicit rules override the pack.",
},
rules: {
type: "array",
uniqueItems: true,
items: {
anyOf: [
{ enum: [...detectorIds] },
{ type: "string", description: "A plugin-provided rule id." },
],
},
description: "Rule ids to run. Omit to run all rules. May include plugin rule ids when plugins are configured.",
},
pluginApiVersion: {
type: "integer",
minimum: 1,
description: "Plugin API version this config targets; must match the DebtLens runtime version.",
},
plugins: {
type: "array",
items: { type: "string" },
description: "Paths to local ESM plugin modules, resolved relative to the config file directory.",
},
maxFiles: {
type: "integer",
minimum: 1,
description: "Maximum number of files to scan.",
},
respectGitignore: {
type: "boolean",
description: "When true, skip files ignored by git in addition to configured exclude globs.",
},
thresholds: {
type: "object",
description: "Per-rule numeric threshold overrides.",
properties: knownThresholds,
additionalProperties: { type: "number" },
},
vocabulary: {
type: "object",
description: "Naming-drift concept groups (concept id -> competing terms).",
additionalProperties: {
type: "array",
items: { type: "string" },
},
},
ruleSeverities: {
type: "object",
description: "Rule id -> severity reported for that rule's issues, replacing the detector's choice. May include plugin rule ids.",
properties: knownRuleSeverities,
additionalProperties: severityValue,
},
ruleConfidenceFloors: {
type: "object",
description: "Rule id -> minimum confidence (0-1); issues from that rule below the floor are not reported. May include plugin rule ids.",
properties: knownRuleConfidenceFloors,
additionalProperties: confidenceFloorValue,
},
propDrilling: {
type: "object",
description: "Prop-drilling rule configuration.",
properties: {
ignoreComponents: {
type: "array",
items: { type: "string" },
description: "Additional UI primitive component names to ignore (extends built-in host components).",
},
},
additionalProperties: false,
},
duplicatedLiteral: {
type: "object",
description: "Duplicated-literal rule configuration.",
properties: {
ignoreStrings: {
type: "array",
items: { type: "string" },
description: "Exact string literal values to ignore, such as framework directives.",
},
},
additionalProperties: false,
},
todoComment: {
type: "object",
description: "Todo-comment rule configuration.",
properties: {
markers: {
type: "array",
description: "Additional or replacement comment marker patterns.",
items: {
type: "object",
additionalProperties: false,
properties: {
pattern: { type: "string", description: "Case-insensitive regex pattern." },
severity: { enum: [...severities], description: "Severity when this marker matches." },
label: { type: "string", description: "Human-readable marker label for reports." },
},
required: ["pattern"],
},
},
replaceDefaults: {
type: "boolean",
description: "When true, built-in marker patterns are not used.",
},
disableDefaults: {
type: "array",
items: { type: "string" },
description: "Built-in marker labels to disable (e.g. \"todo marker\").",
},
},
additionalProperties: false,
},
namingDrift: {
type: "object",
description: "Naming-drift rule configuration.",
properties: {
disableBuiltInVocabulary: {
type: "boolean",
description: "When true, skip built-in concept groups; only user vocabulary applies.",
},
},
additionalProperties: false,
},
featureFlags: {
type: "object",
description: "Configuration for the opt-in stale feature-flag detector.",
properties: {
accessPatterns: {
type: "array",
description: "Call shapes that read a literal feature-flag key.",
items: {
type: "object",
additionalProperties: false,
properties: {
callee: { type: "string", minLength: 1 },
keyArgument: { type: "integer", minimum: 0, default: 0 },
},
required: ["callee"],
},
},
registryGlobs: {
type: "array",
items: { type: "string", minLength: 1 },
description: "Registry file globs relative to the scan target.",
},
constantNamePatterns: {
type: "array",
items: { type: "string", minLength: 1 },
description: "Regexes identifying top-level boolean flag constants outside registries.",
},
},
additionalProperties: false,
},
failOn: {
enum: [...severities],
description: "Exit with code 1 when any reported issue meets this severity. The --fail-on CLI flag overrides this.",
},
failOnConfidence: {
type: "number",
minimum: 0,
maximum: 1,
description: "With fail-on severity policy, require at least this confidence to exit with code 1.",
},
gatePreset: {
enum: [...gatePresets],
description: "Named quality-gate rollout preset. Explicit CLI flags override preset defaults.",
},
budgets: {
type: "object",
description: "Per-path debt budgets. Keys are path globs; values cap issue counts per area.",
additionalProperties: {
type: "object",
additionalProperties: false,
properties: {
maxIssues: { type: "integer", minimum: 0 },
maxHigh: { type: "integer", minimum: 0 },
maxMedium: { type: "integer", minimum: 0 },
},
},
},
badge: {
type: "object",
description: "Color thresholds for badge output.",
additionalProperties: false,
properties: {
greenMax: { type: "integer", minimum: 0 },
yellowMax: { type: "integer", minimum: 0 },
},
},
priority: {
type: "object",
description: "Payoff ranking weights for --sort payoff.",
additionalProperties: false,
properties: {
churn: { type: "number", minimum: 0 },
age: { type: "number", minimum: 0 },
severity: {
type: "object",
additionalProperties: false,
properties: Object.fromEntries(severities.map((severity) => [severity, { type: "number", minimum: 0 }])),
},
},
},
},
};
}
function escapeRegex(value: string): string {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}