@konvert7/klint 0.37.1 → 1.0.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.
Files changed (56) hide show
  1. package/README.md +49 -166
  2. package/bin/klint.js +47 -0
  3. package/klint.schema.json +1 -1
  4. package/klint.schema.yaml +1 -1
  5. package/package.json +15 -25
  6. package/cli/command.ts +0 -189
  7. package/cli/commands.ts +0 -90
  8. package/cli/help.ts +0 -21
  9. package/cli/install-skill.ts +0 -260
  10. package/cli/output.ts +0 -178
  11. package/cli/paths.ts +0 -20
  12. package/cli/rust-config.ts +0 -145
  13. package/cli/rust-engine.ts +0 -323
  14. package/cli/stats.ts +0 -92
  15. package/cli.ts +0 -214
  16. package/core/arch-comments.ts +0 -125
  17. package/core/arch.ts +0 -470
  18. package/core/ast.ts +0 -63
  19. package/core/config.schema.ts +0 -413
  20. package/core/fixer.ts +0 -47
  21. package/core/native-binary.ts +0 -123
  22. package/core/paths.ts +0 -9
  23. package/core/rule-helpers.ts +0 -76
  24. package/core/runner.ts +0 -196
  25. package/core/schema-version.ts +0 -46
  26. package/core/skill.ts +0 -164
  27. package/core/stats.ts +0 -160
  28. package/core/types.ts +0 -146
  29. package/plugins/index.ts +0 -6
  30. package/plugins/sonar.ts +0 -27
  31. package/rules/index.ts +0 -26
  32. package/rules/no-async-predicate.ts +0 -51
  33. package/rules/no-consecutive-array-push.ts +0 -61
  34. package/rules/no-date-equality.ts +0 -35
  35. package/rules/no-floating-promise.ts +0 -37
  36. package/rules/no-misused-promises.ts +0 -59
  37. package/rules/no-nested-template-literals.ts +0 -47
  38. package/rules/no-object-in-template.ts +0 -95
  39. package/rules/no-optional-chain-on-non-nullable.ts +0 -47
  40. package/rules/no-single-char-class.ts +0 -113
  41. package/rules/no-string-match.ts +0 -63
  42. package/rules/no-sync-in-async.ts +0 -43
  43. package/rules/no-unguarded-json-parse.ts +0 -35
  44. package/rules/prefer-at.ts +0 -46
  45. package/rules/prefer-nullish-coalescing-assign.ts +0 -111
  46. package/rules/prefer-string-raw-regexp.ts +0 -93
  47. package/rules/prefer-string-raw.ts +0 -39
  48. package/rules/prefer-string-replaceall.ts +0 -67
  49. package/skill/klint-rules/SKILL.md +0 -264
  50. package/tools/generate-homebrew-formula.ts +0 -64
  51. package/tools/prepare-native-release.ts +0 -166
  52. package/tools/prepare-nuget-release.ts +0 -62
  53. package/tools/prepare-python-release.ts +0 -76
  54. package/tools/semantic-release-crates.js +0 -96
  55. package/tools/semantic-release-native-npm.js +0 -121
  56. package/tools/semantic-release-root-npm.js +0 -87
package/core/runner.ts DELETED
@@ -1,196 +0,0 @@
1
- import { readdirSync, readFileSync } from "node:fs";
2
- import { join, resolve } from "node:path";
3
- import { BUILT_IN_PLUGINS } from "../plugins/index";
4
- import { BUILT_IN_RULES } from "../rules/index";
5
- import { runArchRules } from "./arch";
6
- import { clearAstCache } from "./ast";
7
- import { relativeSlashPath, toSlashPath } from "./paths";
8
- import type {
9
- KlintConfig,
10
- KlintRule,
11
- RuleConfigValue,
12
- Severity,
13
- Violation,
14
- } from "./types";
15
-
16
- export type KlintDebugEvent =
17
- | { type: "walk:start"; dir: string }
18
- | { type: "walk:done"; dir: string; files: number }
19
- | { type: "files:resolved"; files: number }
20
- | { type: "rule:start"; rule: string; files: number }
21
- | { type: "rule:done"; rule: string; violations: number }
22
- | { type: "arch:start"; files: number }
23
- | { type: "arch:done"; violations: number };
24
-
25
- export interface RunKlintOptions {
26
- onDebug?: (event: KlintDebugEvent) => void;
27
- }
28
-
29
- function walk(dir: string, root: string, excludes: string[] = []): string[] {
30
- const out: string[] = [];
31
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
32
- const full = join(dir, entry.name);
33
- const rel = relativeSlashPath(root, full);
34
- if (entry.isDirectory()) {
35
- if (
36
- excludes.some(
37
- (pattern) => matchPattern(rel, pattern) || matchPattern(`${rel}/`, pattern)
38
- )
39
- )
40
- continue;
41
- out.push(...walk(full, root, excludes));
42
- } else if (/\.(tsx?|jsx?|mts|cts)$/.test(entry.name)) out.push(toSlashPath(full));
43
- }
44
- return out;
45
- }
46
-
47
- function resolveFiles(
48
- include: string[],
49
- root: string,
50
- onDebug?: (event: KlintDebugEvent) => void
51
- ): string[] {
52
- const files = new Set<string>();
53
- const excludes = include.filter((p) => p.startsWith("!")).map((p) => p.slice(1));
54
- for (const pattern of include) {
55
- if (pattern.startsWith("!")) continue;
56
- const base = pattern.split("/**")[0].split("/*")[0];
57
- const dir = resolve(root, base === "**" ? "." : base);
58
- try {
59
- onDebug?.({ type: "walk:start", dir });
60
- const before = files.size;
61
- for (const file of walk(dir, root, excludes)) files.add(file);
62
- onDebug?.({ type: "walk:done", dir, files: files.size - before });
63
- } catch {
64
- // directory doesn't exist — skip
65
- }
66
- }
67
- return [...files].sort(byLexicographicOrder);
68
- }
69
-
70
- // Not localeCompare — must match the Rust engine's locale-independent Vec<PathBuf>::sort().
71
- function byLexicographicOrder(a: string, b: string): number {
72
- if (a < b) return -1;
73
- if (a > b) return 1;
74
- return 0;
75
- }
76
-
77
- function matchPattern(relPath: string, pattern: string): boolean {
78
- const norm = toSlashPath(relPath);
79
- const p = toSlashPath(pattern);
80
- if (p === "." || p === "**") return true;
81
- if (!p.includes("*")) return norm === p || norm.startsWith(`${p}/`);
82
- return globToRegExp(p).test(norm);
83
- }
84
-
85
- function globToRegExp(pattern: string): RegExp {
86
- let source = "";
87
- for (let i = 0; i < pattern.length; i++) {
88
- const char = pattern[i];
89
- const next = pattern[i + 1];
90
-
91
- if (char === "*" && next === "*") {
92
- const after = pattern[i + 2];
93
- if (after === "/") {
94
- source += "(?:.*/)?";
95
- i += 2;
96
- } else {
97
- source += ".*";
98
- i++;
99
- }
100
- continue;
101
- }
102
-
103
- if (char === "*") {
104
- source += "[^/]*";
105
- continue;
106
- }
107
-
108
- source += /[.+?^${}()|[\]\\]/.test(char) ? `\\${char}` : char;
109
- }
110
- return new RegExp(`^${source}$`);
111
- }
112
-
113
- function applyPatterns(files: string[], patterns: string[], root: string): string[] {
114
- const includes = patterns.filter((p) => !p.startsWith("!"));
115
- const excludes = patterns.filter((p) => p.startsWith("!")).map((p) => p.slice(1));
116
- return files.filter((file) => {
117
- const rel = relativeSlashPath(root, file);
118
- const included = includes.length === 0 || includes.some((p) => matchPattern(rel, p));
119
- const excluded = excludes.some((p) => matchPattern(rel, p));
120
- return included && !excluded;
121
- });
122
- }
123
-
124
- function resolveSeverity(value: RuleConfigValue): Severity {
125
- return typeof value === "string" ? value : (value.severity ?? "error");
126
- }
127
-
128
- function resolveInclude(value: RuleConfigValue): string[] | undefined {
129
- return typeof value === "object" ? value.include : undefined;
130
- }
131
-
132
- export function runKlint(
133
- config: KlintConfig,
134
- customRules: Record<string, KlintRule> = {},
135
- options: RunKlintOptions = {}
136
- ): Violation[] {
137
- clearAstCache();
138
-
139
- // All plugin implementations are always available by their prefixed names
140
- const pluginImpls: Record<string, KlintRule> = Object.assign(
141
- {},
142
- ...Object.values(BUILT_IN_PLUGINS).map((p) => p.implementations)
143
- );
144
- const registry: Record<string, KlintRule> = {
145
- ...BUILT_IN_RULES,
146
- ...pluginImpls,
147
- ...customRules,
148
- };
149
-
150
- // Plugin defaults applied first; explicit rules take precedence
151
- const pluginDefaults: Record<string, RuleConfigValue> = {};
152
- for (const pluginName of config.plugins ?? []) {
153
- const plugin = BUILT_IN_PLUGINS[pluginName];
154
- if (!plugin) throw new Error(`Unknown klint plugin: "${pluginName}"`);
155
- Object.assign(pluginDefaults, plugin.rules);
156
- }
157
- const effectiveRules: Record<string, RuleConfigValue> = {
158
- ...pluginDefaults,
159
- ...config.rules,
160
- };
161
-
162
- const allFiles = applyPatterns(
163
- resolveFiles(config.include, config.root, options.onDebug),
164
- config.include,
165
- config.root
166
- );
167
- options.onDebug?.({ type: "files:resolved", files: allFiles.length });
168
- const fileContents = new Map(allFiles.map((f) => [f, readFileSync(f, "utf-8")]));
169
- const violations: Violation[] = [];
170
-
171
- for (const [ruleName, configValue] of Object.entries(effectiveRules)) {
172
- const severity = resolveSeverity(configValue);
173
- if (severity === "off") continue;
174
-
175
- const rule = registry[ruleName];
176
- if (!rule) throw new Error(`Unknown klint rule: "${ruleName}"`);
177
-
178
- const include = resolveInclude(configValue);
179
- const files = include ? applyPatterns(allFiles, include, config.root) : allFiles;
180
-
181
- const batch: Omit<Violation, "severity">[] = [];
182
- options.onDebug?.({ type: "rule:start", rule: ruleName, files: files.length });
183
- rule.check({ files, root: config.root, fileContents }, batch);
184
- options.onDebug?.({ type: "rule:done", rule: ruleName, violations: batch.length });
185
- for (const v of batch) violations.push({ ...v, rule: ruleName, severity });
186
- }
187
-
188
- if (config.arch) {
189
- options.onDebug?.({ type: "arch:start", files: allFiles.length });
190
- const archViolations = runArchRules(config.arch, allFiles, fileContents, config.root);
191
- options.onDebug?.({ type: "arch:done", violations: archViolations.length });
192
- violations.push(...archViolations);
193
- }
194
-
195
- return violations;
196
- }
@@ -1,46 +0,0 @@
1
- import type { Violation } from "./types";
2
-
3
- const SCHEMA_VERSION_RULE = "klint/schema-version";
4
-
5
- const TAGGED_SCHEMA_URL = /\/refs\/tags\/v(\d+\.\d+\.\d+(?:-[0-9A-Za-z.]+)?)\//;
6
- const UNRELEASED = "0.0.0";
7
-
8
- export function schemaUrlForVersion(version: string): string {
9
- return `https://raw.githubusercontent.com/konvert7/klint/refs/tags/v${version}/klint.schema.json`;
10
- }
11
-
12
- export function declaredSchemaVersion(schema: string | undefined): string | undefined {
13
- return schema?.match(TAGGED_SCHEMA_URL)?.[1];
14
- }
15
-
16
- export function schemaVersionAdvisory({
17
- schema,
18
- installed,
19
- configFile,
20
- configText,
21
- }: {
22
- schema: string | undefined;
23
- installed: string;
24
- configFile: string;
25
- configText: string;
26
- }): Array<Omit<Violation, "fix">> {
27
- const declared = declaredSchemaVersion(schema);
28
- if (declared === undefined || declared === installed || installed === UNRELEASED) {
29
- return [];
30
- }
31
-
32
- return [
33
- {
34
- file: configFile,
35
- line: schemaDeclarationLine(configText),
36
- rule: SCHEMA_VERSION_RULE,
37
- severity: "warn",
38
- message: `config declares schema v${declared} but klint ${installed} is installed — update $schema to ${schemaUrlForVersion(installed)}`,
39
- },
40
- ];
41
- }
42
-
43
- function schemaDeclarationLine(configText: string): number {
44
- const index = configText.split("\n").findIndex((line) => line.includes("$schema"));
45
- return index === -1 ? 1 : index + 1;
46
- }
package/core/skill.ts DELETED
@@ -1,164 +0,0 @@
1
- import { createHash } from "node:crypto";
2
- import { lstatSync, readFileSync, readlinkSync, realpathSync } from "node:fs";
3
- import { join } from "node:path";
4
- import type { Violation } from "./types";
5
-
6
- export const AGENT_SKILL_DIRS = {
7
- claude: ".claude/skills",
8
- codex: ".agents/skills",
9
- copilot: ".agents/skills",
10
- cursor: ".cursor/skills",
11
- opencode: ".agents/skills",
12
- } as const;
13
-
14
- export type AgentKey = keyof typeof AGENT_SKILL_DIRS;
15
-
16
- export const CANONICAL_SKILL_DIR = ".agents/skills";
17
- export const SKILL_DIR_NAME = "klint-rules";
18
- export const SKILL_FILE_NAME = "SKILL.md";
19
- export const RECEIPT_FILE_NAME = ".klint-skill.json";
20
-
21
- const STALE_RULE = "klint/skill-stale";
22
- const LEGACY_RULE = "klint/skill-legacy-link";
23
- const LEGACY_TARGET = "node_modules";
24
-
25
- export interface SkillReceipt {
26
- version: string;
27
- sha256: string;
28
- }
29
-
30
- export function skillHash(contents: Buffer | string): string {
31
- return createHash("sha256").update(contents).digest("hex");
32
- }
33
-
34
- export function agentSkillDirs(agents?: readonly AgentKey[]): string[] {
35
- const selected = (agents ?? (Object.keys(AGENT_SKILL_DIRS) as AgentKey[])).map(
36
- (agent) => AGENT_SKILL_DIRS[agent]
37
- );
38
- return [...new Set(selected)];
39
- }
40
-
41
- export function skillAdvisories({
42
- configDir,
43
- installed,
44
- shippedSkillPath,
45
- }: {
46
- configDir: string;
47
- installed: string;
48
- shippedSkillPath: string;
49
- }): Array<Omit<Violation, "fix">> {
50
- return [
51
- ...legacyLinkAdvisories(configDir),
52
- ...stalenessAdvisories({ configDir, installed, shippedSkillPath }),
53
- ];
54
- }
55
-
56
- function legacyLinkAdvisories(configDir: string): Array<Omit<Violation, "fix">> {
57
- return agentSkillDirs().flatMap((dir) => {
58
- const target = symlinkTarget(join(configDir, dir, SKILL_DIR_NAME));
59
- if (target === undefined || !target.includes(LEGACY_TARGET)) return [];
60
- return [
61
- {
62
- file: `${dir}/${SKILL_DIR_NAME}`,
63
- line: 1,
64
- rule: LEGACY_RULE,
65
- severity: "warn",
66
- message: `this skill is a symlink into ${LEGACY_TARGET} (${target}), which breaks as soon as dependencies are reinstalled — run: klint install-skill`,
67
- },
68
- ];
69
- });
70
- }
71
-
72
- function stalenessAdvisories({
73
- configDir,
74
- installed,
75
- shippedSkillPath,
76
- }: {
77
- configDir: string;
78
- installed: string;
79
- shippedSkillPath: string;
80
- }): Array<Omit<Violation, "fix">> {
81
- const shipped = readShippedHash(shippedSkillPath);
82
- if (shipped === undefined) return [];
83
-
84
- const reported = new Set<string>();
85
- return realSkillDirsFirst(configDir).flatMap((dir) => {
86
- const skillDir = join(configDir, dir, SKILL_DIR_NAME);
87
- const canonical = resolveSkillDir(skillDir);
88
- if (canonical === undefined || reported.has(canonical)) return [];
89
-
90
- const receipt = readReceipt(join(skillDir, RECEIPT_FILE_NAME));
91
- if (receipt === undefined || receipt.sha256 === shipped) return [];
92
-
93
- reported.add(canonical);
94
- return [staleAdvisory(dir, receipt.version, installed)];
95
- });
96
- }
97
-
98
- function staleAdvisory(
99
- dir: string,
100
- installedFrom: string,
101
- installed: string
102
- ): Omit<Violation, "fix"> {
103
- return {
104
- file: `${dir}/${SKILL_DIR_NAME}/${SKILL_FILE_NAME}`,
105
- line: 1,
106
- rule: STALE_RULE,
107
- severity: "warn",
108
- message: `this klint-rules skill was installed from klint ${installedFrom} and no longer matches klint ${installed} — reinstall it with: klint install-skill`,
109
- };
110
- }
111
-
112
- function realSkillDirsFirst(configDir: string): string[] {
113
- const dirs = agentSkillDirs();
114
- const linked = new Set(
115
- dirs.filter(
116
- (dir) => symlinkTarget(join(configDir, dir, SKILL_DIR_NAME)) !== undefined
117
- )
118
- );
119
- return [...dirs.filter((dir) => !linked.has(dir)), ...linked];
120
- }
121
-
122
- function symlinkTarget(path: string): string | undefined {
123
- try {
124
- if (!lstatSync(path).isSymbolicLink()) return undefined;
125
- return readlinkSync(path);
126
- } catch {
127
- return undefined;
128
- }
129
- }
130
-
131
- function resolveSkillDir(path: string): string | undefined {
132
- try {
133
- return realpathSync(path);
134
- } catch {
135
- return undefined;
136
- }
137
- }
138
-
139
- function readShippedHash(shippedSkillPath: string): string | undefined {
140
- try {
141
- return skillHash(readFileSync(shippedSkillPath));
142
- } catch {
143
- return undefined;
144
- }
145
- }
146
-
147
- function readReceipt(path: string): SkillReceipt | undefined {
148
- let text: string;
149
- try {
150
- text = readFileSync(path, "utf-8");
151
- } catch {
152
- return undefined;
153
- }
154
-
155
- try {
156
- const parsed = JSON.parse(text) as Partial<SkillReceipt>;
157
- if (typeof parsed.version !== "string" || typeof parsed.sha256 !== "string") {
158
- return undefined;
159
- }
160
- return { version: parsed.version, sha256: parsed.sha256 };
161
- } catch {
162
- return undefined;
163
- }
164
- }
package/core/stats.ts DELETED
@@ -1,160 +0,0 @@
1
- import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2
- import { join } from "node:path";
3
- import type { Violation } from "./types";
4
-
5
- export const STATS_DIR = ".klint";
6
- export const STATS_FILE = "stats.json";
7
- const SELF_IGNORE = "# Created by klint automatically.\n*\n";
8
- const STATS_VERSION = 1;
9
- const KLINT_ADVISORY_PREFIX = "klint/";
10
-
11
- export type RunSource = "hook" | "direct";
12
-
13
- type CountedViolation = Pick<Violation, "file" | "rule" | "message" | "severity">;
14
-
15
- export type RunRecorder = (violations: readonly CountedViolation[]) => void;
16
-
17
- interface OpenError {
18
- rule: string;
19
- file: string;
20
- message: string;
21
- caughtBy: RunSource;
22
- }
23
-
24
- export interface KlintStats {
25
- version: typeof STATS_VERSION;
26
- runs: Record<RunSource, number>;
27
- reported: RuleCounts;
28
- prevented: RuleCounts;
29
- open: OpenError[];
30
- }
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
-
46
- export function emptyStats(): KlintStats {
47
- return {
48
- version: STATS_VERSION,
49
- runs: { hook: 0, direct: 0 },
50
- reported: emptyCounts(),
51
- prevented: emptyCounts(),
52
- open: [],
53
- };
54
- }
55
-
56
- function sameError(a: Omit<OpenError, "caughtBy">, b: Omit<OpenError, "caughtBy">) {
57
- return a.rule === b.rule && a.file === b.file && a.message === b.message;
58
- }
59
-
60
- function isCodeViolation(violation: CountedViolation): boolean {
61
- return !violation.rule.startsWith(KLINT_ADVISORY_PREFIX);
62
- }
63
-
64
- function errorsOf(
65
- violations: readonly CountedViolation[]
66
- ): Omit<OpenError, "caughtBy">[] {
67
- return violations
68
- .filter((v) => v.severity === "error")
69
- .map(({ rule, file, message }) => ({ rule, file, message }));
70
- }
71
-
72
- export function nextStats(
73
- previous: KlintStats,
74
- violations: readonly CountedViolation[],
75
- source: RunSource
76
- ): KlintStats {
77
- const stats = structuredClone(previous);
78
- stats.runs[source] += 1;
79
- for (const violation of violations.filter(isCodeViolation)) {
80
- bump(stats.reported, violation.rule, source);
81
- }
82
-
83
- const unmatched = errorsOf(violations);
84
- const stillOpen: OpenError[] = [];
85
- for (const error of previous.open) {
86
- const match = unmatched.findIndex((current) => sameError(current, error));
87
- if (match === -1) {
88
- bump(stats.prevented, error.rule, error.caughtBy);
89
- continue;
90
- }
91
- unmatched.splice(match, 1);
92
- stillOpen.push(error);
93
- }
94
- stats.open = [
95
- ...stillOpen,
96
- ...unmatched.map((error) => ({ ...error, caughtBy: source })),
97
- ];
98
- return stats;
99
- }
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
-
110
- function isStats(value: unknown): value is KlintStats {
111
- if (!value || typeof value !== "object") return false;
112
- const candidate = value as Partial<KlintStats>;
113
- return (
114
- candidate.version === STATS_VERSION &&
115
- typeof candidate.runs?.hook === "number" &&
116
- typeof candidate.runs.direct === "number" &&
117
- isRuleCounts(candidate.prevented) &&
118
- (candidate.reported === undefined || isRuleCounts(candidate.reported)) &&
119
- Array.isArray(candidate.open)
120
- );
121
- }
122
-
123
- function withReportedCounts(stats: KlintStats): KlintStats {
124
- return { ...stats, reported: stats.reported ?? emptyCounts() };
125
- }
126
-
127
- export function readStats(configDir: string): KlintStats {
128
- try {
129
- const parsed: unknown = JSON.parse(
130
- readFileSync(join(configDir, STATS_DIR, STATS_FILE), "utf-8")
131
- );
132
- return isStats(parsed) ? withReportedCounts(parsed) : emptyStats();
133
- } catch {
134
- return emptyStats();
135
- }
136
- }
137
-
138
- function writeStats(configDir: string, stats: KlintStats): void {
139
- const dir = join(configDir, STATS_DIR);
140
- mkdirSync(dir, { recursive: true });
141
- const ignoreFile = join(dir, ".gitignore");
142
- if (!existsSync(ignoreFile)) writeFileSync(ignoreFile, SELF_IGNORE);
143
- const target = join(dir, STATS_FILE);
144
- const staging = `${target}.${process.pid}.tmp`;
145
- writeFileSync(staging, `${JSON.stringify(stats, null, 2)}\n`);
146
- renameSync(staging, target);
147
- }
148
-
149
- function statsDisabled(): boolean {
150
- return Boolean(process.env.KLINT_NO_STATS);
151
- }
152
-
153
- export function runRecorder(configDir: string, source: RunSource): RunRecorder {
154
- return (violations) => {
155
- if (statsDisabled()) return;
156
- try {
157
- writeStats(configDir, nextStats(readStats(configDir), violations, source));
158
- } catch {}
159
- };
160
- }
package/core/types.ts DELETED
@@ -1,146 +0,0 @@
1
- interface ViolationFix {
2
- startLine: number;
3
- endLine: number;
4
- replacement: string;
5
- }
6
-
7
- export type Severity = "error" | "warn" | "off";
8
-
9
- /** @lintignore */
10
- export interface RuleOptions {
11
- severity?: Severity;
12
- include?: string[];
13
- }
14
-
15
- export type RuleConfigValue = Severity | RuleOptions;
16
-
17
- export interface Violation {
18
- file: string;
19
- line: number;
20
- rule: string;
21
- message: string;
22
- severity: Severity;
23
- fix?: ViolationFix;
24
- }
25
-
26
- /** @lintignore */
27
- export interface RuleContext {
28
- files: string[];
29
- root: string;
30
- fileContents: Map<string, string>;
31
- }
32
-
33
- /** Violation as emitted by a rule — rule name and severity are stamped by the runner. */
34
- export type RawViolation = Omit<Violation, "rule" | "severity">;
35
-
36
- export interface RuleMeta {
37
- /** One- or two-line description of what the rule catches. */
38
- description: string;
39
- /** Concrete YAML config snippets showing usage. Rendered as code blocks in docs. */
40
- examples?: string[];
41
- }
42
-
43
- export interface KlintRule {
44
- check: (ctx: RuleContext, violations: RawViolation[]) => void;
45
- meta?: RuleMeta;
46
- }
47
-
48
- /** A named bundle of rules with their default severities and implementations. */
49
- export interface KlintPlugin {
50
- name: string;
51
- /** Default severity for each rule. Keys use the prefixed form e.g. "sonar/rule-name". */
52
- rules: Record<string, RuleConfigValue>;
53
- /** Rule implementations keyed by the same prefixed names. */
54
- implementations: Record<string, KlintRule>;
55
- }
56
-
57
- interface ArchImportRule {
58
- from: string | string[];
59
- deny?: string | string[];
60
- allow?: string | string[];
61
- "deny-packages"?: string | string[];
62
- "type-only"?: "allow";
63
- message?: string;
64
- severity?: Exclude<Severity, "off">;
65
- }
66
-
67
- interface ArchForbiddenRuleBase {
68
- in: string | string[];
69
- message: string;
70
- severity?: Exclude<Severity, "off">;
71
- }
72
-
73
- interface ArchForbiddenPatternRule extends ArchForbiddenRuleBase {
74
- pattern: string;
75
- }
76
-
77
- interface ArchForbiddenJsxRule extends ArchForbiddenRuleBase {
78
- "jsx-element": string | string[];
79
- }
80
-
81
- type ArchForbiddenRule = ArchForbiddenPatternRule | ArchForbiddenJsxRule;
82
-
83
- interface ArchSingletonRuleBase {
84
- only: string;
85
- message: string;
86
- /** Limit scan to these files/layers. Defaults to all files. */
87
- in?: string | string[];
88
- severity?: Exclude<Severity, "off">;
89
- }
90
-
91
- interface ArchSingletonPatternRule extends ArchSingletonRuleBase {
92
- pattern: string;
93
- }
94
-
95
- interface ArchSingletonJsxRule extends ArchSingletonRuleBase {
96
- "jsx-element": string | string[];
97
- }
98
-
99
- type ArchSingletonRule = ArchSingletonPatternRule | ArchSingletonJsxRule;
100
-
101
- interface ArchMaxLinesRule {
102
- limit: number;
103
- in: string | string[];
104
- message?: string;
105
- severity?: Exclude<Severity, "off">;
106
- }
107
-
108
- interface ArchCommentRuleBase {
109
- limit: number;
110
- in: string | string[];
111
- /** Count doc-comments (`/** *​/` JSDoc, Swift `///`, Rust `///` and `//!`) toward the limit. Defaults to false. No effect on Python, whose docstrings are never counted. */
112
- countDocComments?: boolean;
113
- /** Comment lines matching these patterns count toward neither limit. Literal substring, or `re:` for a regex. */
114
- ignore?: string | string[];
115
- message?: string;
116
- severity?: Exclude<Severity, "off">;
117
- }
118
-
119
- /** Caps the share of a file's physical lines that are non-doc comments. */
120
- interface ArchMaxCommentDensityRule extends ArchCommentRuleBase {}
121
-
122
- /** Caps how many consecutive comment lines may stack into a single block. */
123
- interface ArchMaxCommentBlockRule extends ArchCommentRuleBase {}
124
-
125
- export interface ArchConfig {
126
- layers?: Record<string, string[]>;
127
- imports?: ArchImportRule[];
128
- forbidden?: ArchForbiddenRule[];
129
- singleton?: ArchSingletonRule[];
130
- maxLines?: ArchMaxLinesRule[];
131
- maxCommentDensity?: ArchMaxCommentDensityRule[];
132
- maxCommentBlock?: ArchMaxCommentBlockRule[];
133
- }
134
-
135
- export interface KlintConfig {
136
- root: string;
137
- include: string[];
138
- plugins?: string[];
139
- rules: Record<string, RuleConfigValue>;
140
- arch?: ArchConfig;
141
- }
142
-
143
- export const defineRule = (r: KlintRule): KlintRule => r;
144
-
145
- /** @lintignore */
146
- export const defineConfig = (c: KlintConfig): KlintConfig => c;