@shivaedev/quality 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/CHANGELOG.md +114 -0
  2. package/README.md +207 -35
  3. package/dist/baseline/compare.d.ts +3 -8
  4. package/dist/baseline/compare.d.ts.map +1 -1
  5. package/dist/baseline/compare.js +13 -23
  6. package/dist/baseline/compare.js.map +1 -1
  7. package/dist/baseline/format.d.ts +7 -5
  8. package/dist/baseline/format.d.ts.map +1 -1
  9. package/dist/baseline/format.js +31 -23
  10. package/dist/baseline/format.js.map +1 -1
  11. package/dist/baseline/guard.d.ts +23 -0
  12. package/dist/baseline/guard.d.ts.map +1 -0
  13. package/dist/baseline/guard.js +31 -0
  14. package/dist/baseline/guard.js.map +1 -0
  15. package/dist/baseline/legacy.d.ts +11 -0
  16. package/dist/baseline/legacy.d.ts.map +1 -0
  17. package/dist/baseline/legacy.js +33 -0
  18. package/dist/baseline/legacy.js.map +1 -0
  19. package/dist/baseline/rewrite.d.ts +3 -0
  20. package/dist/baseline/rewrite.d.ts.map +1 -0
  21. package/dist/baseline/rewrite.js +19 -0
  22. package/dist/baseline/rewrite.js.map +1 -0
  23. package/dist/baseline/update.d.ts +7 -6
  24. package/dist/baseline/update.d.ts.map +1 -1
  25. package/dist/baseline/update.js +28 -28
  26. package/dist/baseline/update.js.map +1 -1
  27. package/dist/cli/args.d.ts +14 -1
  28. package/dist/cli/args.d.ts.map +1 -1
  29. package/dist/cli/args.js +54 -29
  30. package/dist/cli/args.js.map +1 -1
  31. package/dist/cli/baseline.d.ts +3 -1
  32. package/dist/cli/baseline.d.ts.map +1 -1
  33. package/dist/cli/baseline.js +19 -10
  34. package/dist/cli/baseline.js.map +1 -1
  35. package/dist/cli/check.d.ts +5 -0
  36. package/dist/cli/check.d.ts.map +1 -0
  37. package/dist/cli/check.js +39 -0
  38. package/dist/cli/check.js.map +1 -0
  39. package/dist/cli/lint.js +1 -1
  40. package/dist/cli/lint.js.map +1 -1
  41. package/dist/cli/migrate.d.ts +4 -0
  42. package/dist/cli/migrate.d.ts.map +1 -0
  43. package/dist/cli/migrate.js +31 -0
  44. package/dist/cli/migrate.js.map +1 -0
  45. package/dist/cli/run-main.d.ts +2 -1
  46. package/dist/cli/run-main.d.ts.map +1 -1
  47. package/dist/cli/run-main.js +5 -2
  48. package/dist/cli/run-main.js.map +1 -1
  49. package/dist/cli/shrink.d.ts +6 -0
  50. package/dist/cli/shrink.d.ts.map +1 -0
  51. package/dist/cli/shrink.js +8 -0
  52. package/dist/cli/shrink.js.map +1 -0
  53. package/dist/cli.js +11 -2
  54. package/dist/cli.js.map +1 -1
  55. package/dist/config/decode.d.ts +1 -0
  56. package/dist/config/decode.d.ts.map +1 -1
  57. package/dist/config/decode.js +1 -0
  58. package/dist/config/decode.js.map +1 -1
  59. package/dist/config/file.d.ts +2 -0
  60. package/dist/config/file.d.ts.map +1 -0
  61. package/dist/config/file.js +2 -0
  62. package/dist/config/file.js.map +1 -0
  63. package/dist/config/load.d.ts +1 -1
  64. package/dist/config/load.d.ts.map +1 -1
  65. package/dist/config/load.js +3 -2
  66. package/dist/config/load.js.map +1 -1
  67. package/dist/config/resolve.d.ts +1 -0
  68. package/dist/config/resolve.d.ts.map +1 -1
  69. package/dist/config/resolve.js +6 -4
  70. package/dist/config/resolve.js.map +1 -1
  71. package/dist/config.d.ts +2 -7
  72. package/dist/config.d.ts.map +1 -1
  73. package/dist/config.js.map +1 -1
  74. package/dist/engine/baseline-file.d.ts +11 -0
  75. package/dist/engine/baseline-file.d.ts.map +1 -0
  76. package/dist/engine/baseline-file.js +18 -0
  77. package/dist/engine/baseline-file.js.map +1 -0
  78. package/dist/engine/evaluate.d.ts +2 -2
  79. package/dist/engine/evaluate.d.ts.map +1 -1
  80. package/dist/engine/evaluate.js +3 -3
  81. package/dist/engine/evaluate.js.map +1 -1
  82. package/dist/engine/session.d.ts +3 -5
  83. package/dist/engine/session.d.ts.map +1 -1
  84. package/dist/engine/session.js +12 -10
  85. package/dist/engine/session.js.map +1 -1
  86. package/dist/exceptions/registry.d.ts +1 -1
  87. package/dist/exceptions/registry.d.ts.map +1 -1
  88. package/dist/exceptions/registry.js +8 -3
  89. package/dist/exceptions/registry.js.map +1 -1
  90. package/dist/failure.d.ts +0 -2
  91. package/dist/failure.d.ts.map +1 -1
  92. package/dist/failure.js +0 -2
  93. package/dist/failure.js.map +1 -1
  94. package/dist/git/base.d.ts +9 -0
  95. package/dist/git/base.d.ts.map +1 -0
  96. package/dist/git/base.js +59 -0
  97. package/dist/git/base.js.map +1 -0
  98. package/dist/git/command.d.ts +12 -0
  99. package/dist/git/command.d.ts.map +1 -0
  100. package/dist/git/command.js +13 -0
  101. package/dist/git/command.js.map +1 -0
  102. package/dist/git/history.d.ts +10 -0
  103. package/dist/git/history.d.ts.map +1 -0
  104. package/dist/git/history.js +28 -0
  105. package/dist/git/history.js.map +1 -0
  106. package/dist/inventory/ignore-scope.d.ts.map +1 -1
  107. package/dist/inventory/ignore-scope.js +0 -1
  108. package/dist/inventory/ignore-scope.js.map +1 -1
  109. package/dist/inventory/walk.d.ts +0 -1
  110. package/dist/inventory/walk.d.ts.map +1 -1
  111. package/dist/inventory/walk.js +1 -1
  112. package/dist/inventory/walk.js.map +1 -1
  113. package/dist/report/guard.d.ts +9 -0
  114. package/dist/report/guard.d.ts.map +1 -0
  115. package/dist/report/guard.js +34 -0
  116. package/dist/report/guard.js.map +1 -0
  117. package/dist/report/render.js +1 -2
  118. package/dist/report/render.js.map +1 -1
  119. package/dist/report/stale.d.ts.map +1 -1
  120. package/dist/report/stale.js +4 -3
  121. package/dist/report/stale.js.map +1 -1
  122. package/dist/rule.d.ts +4 -7
  123. package/dist/rule.d.ts.map +1 -1
  124. package/dist/rule.js +1 -1
  125. package/dist/rule.js.map +1 -1
  126. package/dist/rules/built-in.d.ts +17 -0
  127. package/dist/rules/built-in.d.ts.map +1 -1
  128. package/dist/rules/built-in.js +21 -1
  129. package/dist/rules/built-in.js.map +1 -1
  130. package/dist/rules/comments/kinds.d.ts +9 -0
  131. package/dist/rules/comments/kinds.d.ts.map +1 -0
  132. package/dist/rules/comments/kinds.js +34 -0
  133. package/dist/rules/comments/kinds.js.map +1 -0
  134. package/dist/rules/comments/max-per-file.d.ts +5 -0
  135. package/dist/rules/comments/max-per-file.d.ts.map +1 -0
  136. package/dist/rules/comments/max-per-file.js +39 -0
  137. package/dist/rules/comments/max-per-file.js.map +1 -0
  138. package/dist/rules/comments/no-banner.d.ts +2 -0
  139. package/dist/rules/comments/no-banner.d.ts.map +1 -0
  140. package/dist/rules/comments/no-banner.js +9 -0
  141. package/dist/rules/comments/no-banner.js.map +1 -0
  142. package/dist/rules/comments/no-jsdoc.d.ts +4 -0
  143. package/dist/rules/comments/no-jsdoc.d.ts.map +1 -0
  144. package/dist/rules/comments/no-jsdoc.js +14 -0
  145. package/dist/rules/comments/no-jsdoc.js.map +1 -0
  146. package/dist/rules/comments/no-line-reference.d.ts +2 -0
  147. package/dist/rules/comments/no-line-reference.d.ts.map +1 -0
  148. package/dist/rules/comments/no-line-reference.js +9 -0
  149. package/dist/rules/comments/no-line-reference.js.map +1 -0
  150. package/dist/rules/comments/no-pr-reference.d.ts +2 -0
  151. package/dist/rules/comments/no-pr-reference.d.ts.map +1 -0
  152. package/dist/rules/comments/no-pr-reference.js +13 -0
  153. package/dist/rules/comments/no-pr-reference.js.map +1 -0
  154. package/dist/rules/comments/no-todo.d.ts +2 -0
  155. package/dist/rules/comments/no-todo.d.ts.map +1 -0
  156. package/dist/rules/comments/no-todo.js +8 -0
  157. package/dist/rules/comments/no-todo.js.map +1 -0
  158. package/dist/rules/comments/pattern-rule.d.ts +10 -0
  159. package/dist/rules/comments/pattern-rule.d.ts.map +1 -0
  160. package/dist/rules/comments/pattern-rule.js +16 -0
  161. package/dist/rules/comments/pattern-rule.js.map +1 -0
  162. package/dist/rules/comments/scan.d.ts +13 -0
  163. package/dist/rules/comments/scan.d.ts.map +1 -0
  164. package/dist/rules/comments/scan.js +62 -0
  165. package/dist/rules/comments/scan.js.map +1 -0
  166. package/dist/rules/max-lines.d.ts.map +1 -1
  167. package/dist/rules/max-lines.js +8 -1
  168. package/dist/rules/max-lines.js.map +1 -1
  169. package/dist/rules/suppressions/biome/configs.d.ts +14 -0
  170. package/dist/rules/suppressions/biome/configs.d.ts.map +1 -0
  171. package/dist/rules/suppressions/biome/configs.js +39 -0
  172. package/dist/rules/suppressions/biome/configs.js.map +1 -0
  173. package/dist/rules/suppressions/biome/json.d.ts +27 -0
  174. package/dist/rules/suppressions/biome/json.d.ts.map +1 -0
  175. package/dist/rules/suppressions/biome/json.js +37 -0
  176. package/dist/rules/suppressions/biome/json.js.map +1 -0
  177. package/dist/rules/suppressions/biome/weakenings.d.ts +9 -0
  178. package/dist/rules/suppressions/biome/weakenings.d.ts.map +1 -0
  179. package/dist/rules/suppressions/biome/weakenings.js +68 -0
  180. package/dist/rules/suppressions/biome/weakenings.js.map +1 -0
  181. package/dist/rules/suppressions/biome-overrides.d.ts +8 -0
  182. package/dist/rules/suppressions/biome-overrides.d.ts.map +1 -0
  183. package/dist/rules/suppressions/biome-overrides.js +47 -0
  184. package/dist/rules/suppressions/biome-overrides.js.map +1 -0
  185. package/dist/rules/suppressions/directives.d.ts +4 -0
  186. package/dist/rules/suppressions/directives.d.ts.map +1 -0
  187. package/dist/rules/suppressions/directives.js +19 -0
  188. package/dist/rules/suppressions/directives.js.map +1 -0
  189. package/dist/rules/suppressions/no-double-cast.d.ts +2 -0
  190. package/dist/rules/suppressions/no-double-cast.d.ts.map +1 -0
  191. package/dist/rules/suppressions/no-double-cast.js +38 -0
  192. package/dist/rules/suppressions/no-double-cast.js.map +1 -0
  193. package/dist/rules/suppressions/no-inline.d.ts +8 -0
  194. package/dist/rules/suppressions/no-inline.d.ts.map +1 -0
  195. package/dist/rules/suppressions/no-inline.js +48 -0
  196. package/dist/rules/suppressions/no-inline.js.map +1 -0
  197. package/dist/rules/suppressions/stylesheet-comments.d.ts +4 -0
  198. package/dist/rules/suppressions/stylesheet-comments.d.ts.map +1 -0
  199. package/dist/rules/suppressions/stylesheet-comments.js +20 -0
  200. package/dist/rules/suppressions/stylesheet-comments.js.map +1 -0
  201. package/dist/rules/syntax.d.ts +4 -0
  202. package/dist/rules/syntax.d.ts.map +1 -0
  203. package/dist/rules/syntax.js +12 -0
  204. package/dist/rules/syntax.js.map +1 -0
  205. package/package.json +4 -5
  206. package/src/baseline/compare.ts +15 -39
  207. package/src/baseline/format.ts +35 -32
  208. package/src/baseline/guard.ts +47 -0
  209. package/src/baseline/legacy.ts +46 -0
  210. package/src/baseline/rewrite.ts +24 -0
  211. package/src/baseline/update.ts +47 -30
  212. package/src/cli/args.ts +64 -30
  213. package/src/cli/baseline.ts +27 -13
  214. package/src/cli/check.ts +51 -0
  215. package/src/cli/lint.ts +1 -1
  216. package/src/cli/migrate.ts +36 -0
  217. package/src/cli/run-main.ts +8 -3
  218. package/src/cli/shrink.ts +31 -0
  219. package/src/cli.ts +11 -2
  220. package/src/config/decode.ts +1 -0
  221. package/src/config/file.ts +1 -0
  222. package/src/config/load.ts +4 -3
  223. package/src/config/resolve.ts +7 -4
  224. package/src/config.ts +3 -7
  225. package/src/engine/baseline-file.ts +27 -0
  226. package/src/engine/evaluate.ts +4 -2
  227. package/src/engine/session.ts +14 -12
  228. package/src/exceptions/registry.ts +9 -2
  229. package/src/failure.ts +0 -2
  230. package/src/git/base.ts +89 -0
  231. package/src/git/command.ts +31 -0
  232. package/src/git/history.ts +41 -0
  233. package/src/inventory/ignore-scope.ts +0 -1
  234. package/src/inventory/walk.ts +1 -1
  235. package/src/report/guard.ts +46 -0
  236. package/src/report/render.ts +2 -2
  237. package/src/report/stale.ts +11 -3
  238. package/src/rule.ts +5 -8
  239. package/src/rules/built-in.ts +21 -1
  240. package/src/rules/comments/kinds.ts +45 -0
  241. package/src/rules/comments/max-per-file.ts +46 -0
  242. package/src/rules/comments/no-banner.ts +10 -0
  243. package/src/rules/comments/no-jsdoc.ts +18 -0
  244. package/src/rules/comments/no-line-reference.ts +11 -0
  245. package/src/rules/comments/no-pr-reference.ts +13 -0
  246. package/src/rules/comments/no-todo.ts +8 -0
  247. package/src/rules/comments/pattern-rule.ts +28 -0
  248. package/src/rules/comments/scan.ts +80 -0
  249. package/src/rules/max-lines.ts +8 -1
  250. package/src/rules/suppressions/biome/configs.ts +50 -0
  251. package/src/rules/suppressions/biome/json.ts +55 -0
  252. package/src/rules/suppressions/biome/weakenings.ts +102 -0
  253. package/src/rules/suppressions/biome-overrides.ts +61 -0
  254. package/src/rules/suppressions/directives.ts +25 -0
  255. package/src/rules/suppressions/no-double-cast.ts +47 -0
  256. package/src/rules/suppressions/no-inline.ts +63 -0
  257. package/src/rules/suppressions/stylesheet-comments.ts +27 -0
  258. package/src/rules/syntax.ts +14 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,119 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.1 - 2026-10-03
4
+
5
+ ### Changed
6
+
7
+ - `quality baseline check` fetches the full history with `git fetch --unshallow`
8
+ when a shallow clone does not reach the merge base, and looks for it again
9
+ before it exits 2. The fetch never stops to ask for a
10
+ password.
11
+
12
+ ## 0.3.0 - 2026-10-03
13
+
14
+ ### Added
15
+
16
+ - Add comment rules, each an error by default: `comments/no-jsdoc` (with an
17
+ `allow` list of tool pragmas such as `@vitest-environment`),
18
+ `comments/no-line-reference`, `comments/no-pr-reference`, `comments/no-banner`,
19
+ `comments/no-todo` and `comments/max-per-file` (2 comments per file by
20
+ default; a block comment or a run of adjacent line comments counts once, and
21
+ pragmas and directives are not counted). They find comments with the
22
+ TypeScript parser, so strings, template literals, regular expressions and JSX
23
+ text never count.
24
+
25
+ - Add suppression rules, each an error by default:
26
+ `suppressions/no-inline` reports every comment directive that silences a
27
+ linter, the compiler or a formatter (`biome-ignore…`, `@ts-ignore`,
28
+ `@ts-expect-error`, `@ts-nocheck`, `eslint-disable…`, `prettier-ignore` and
29
+ their equivalents for Oxlint, Stylelint, Deno, TSLint and Flow), in
30
+ TypeScript and JavaScript modules and in CSS, SCSS and Less files, except
31
+ `@ts-expect-error` in type-test files the config declares with a reason;
32
+ `suppressions/no-double-cast` reports casts through `unknown`, `any` or
33
+ `never`; `suppressions/biome-overrides` reports a Biome setting that turns a
34
+ lint rule, group, domain, assist action, the linter, the assist or the
35
+ formatter off or down, per language too, or keeps files out of a check
36
+ through an `includes` list, unless the quality config declares it with its
37
+ rule, its `includes` and a reason. Both rules report a declaration that
38
+ matches nothing. Coverage hints stay allowed.
39
+
40
+ - Add `quality baseline check [--against <ref>]`, which compares the baseline
41
+ with its version at the merge base of `HEAD` and the target branch
42
+ (`origin/HEAD`, `origin/main` or `origin/master` by default) and fails when it
43
+ gained an entry, a higher `count`, or a rule baselined for the first time. Comparing with the merge base means a branch that is behind never
44
+ fails. An entry whose file git sees as moved is compared with its old path. It
45
+ exits 2 without a git work tree, a target or a merge base, and says to fetch
46
+ more history in a shallow clone.
47
+
48
+ - Add the `adopt` config key: a rule enters the baseline for the first time only
49
+ while `adopt` names it, so the adoption shows in the config's diff. The check
50
+ fails when `adopt` names a rule with nothing baselined.
51
+
52
+ - Add `quality baseline tighten [--staged]`, which lowers and removes the
53
+ entries of the files changed since `HEAD`, or staged for the next commit, and
54
+ leaves every other line as it is. Run it in a pre-commit hook and stage the
55
+ baseline again.
56
+
57
+ - Add `quality baseline migrate [--from <file>]`, which moves a baseline from
58
+ the earlier JSON format to the configured file. An old entry that stored a
59
+ size becomes that size less the rule's current limit, and an entry whose file
60
+ is now within the limit is dropped.
61
+
62
+ - `quality baseline prune` and `tighten` carry the entry of a file that git
63
+ sees as moved to its new path, at the lower of its old count and what the
64
+ file has now.
65
+
66
+ - Add `registrable: false` to `defineRule`, for a rule the registry must never
67
+ excuse. The suppression rules set it, so a registry entry that names one fails
68
+ the gate as stale; existing suppressions are adopted through the baseline.
69
+
70
+ ### Changed
71
+
72
+ - **Breaking:** The baseline is a JSON Lines file, `quality/baseline.jsonl` by
73
+ default, with one entry per line (`{"path":…,"rule":…,"count":…}`) sorted by
74
+ path and then rule. `count` is the number of violations; for a rule with a
75
+ limit, such as `structure/max-lines` or `comments/max-per-file`, it is the
76
+ amount over the limit, so changing a limit shifts every count of the rule. A
77
+ repository with a `quality/baseline.json` fails to run until it runs
78
+ `quality baseline migrate`.
79
+
80
+ - **Breaking:** A finding reports `count` (the violations it stands for, 1 by
81
+ default) and `threshold` (the limit it applied) instead of `measure`. A local
82
+ rule that set `measure` reports the amount over its limit as `count`.
83
+
84
+ - `quality baseline write --rule <id>` records a rule that the baseline already
85
+ covers again, replacing its entries, for example after a limit change.
86
+ `quality baseline check` fails on any entry this raises.
87
+
88
+ - `quality lint` passes a baseline entry that allows more than is left,
89
+ including a file with no violations left, and lists it as a note. An entry for
90
+ a rule that is off or unknown still fails.
91
+
92
+ - `quality baseline write --rule` and `prune` rewrite only the lines they add,
93
+ lower or remove, and never re-sort or reformat the rest.
94
+
95
+ - **Breaking:** The comment rules are on by default, so upgrading fails a
96
+ repository with existing comment violations until it fixes them or adopts the
97
+ rules with `quality baseline write --rule <id>`.
98
+
99
+ - **Breaking:** The suppression rules are on by default, so upgrading fails a
100
+ repository with inline suppressions, double casts or undeclared Biome
101
+ overrides until it fixes them, declares its Biome overrides, or adopts the
102
+ rules with `quality baseline write --rule <id>`.
103
+
104
+ - Comment rules also skip the Oxlint, Stylelint, Deno, TSLint and Flow
105
+ directives, like the other compiler and linter directives.
106
+
107
+ - Depend on `typescript`, resolved to the TypeScript 6 compiler API, to find
108
+ comments.
109
+
110
+ - Remove the JSDoc from the package's own source; the README documents the
111
+ config, rule and finding fields.
112
+
113
+ - Consolidate duplicate test cases around observable package behavior.
114
+
115
+ - Validate packed consumers and executable bins through the shared workspace gate.
116
+
3
117
  ## 0.2.0 - 2026-09-28
4
118
 
5
119
  ### Changed
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  One quality gate for a repository: typed rules, one report, a baseline that only shrinks and a registry of permanent exceptions, each with its reason.
4
4
 
5
- Every rule is an error by default. A repository adopts the gate at once: it records its existing violations in the baseline, and from then on a baselined file may not get worse, and fixed debt must leave the baseline.
5
+ Every rule is an error by default. A repository adopts the gate at once: it records its existing violations in the baseline, and from then on a baselined file may not get worse, the baseline may not gain an entry or a higher number, and fixed debt leaves it as files change.
6
6
 
7
7
  ## Setup
8
8
 
@@ -22,15 +22,16 @@ Node 24 or later loads `quality.config.ts` directly (type stripping), so no buil
22
22
  ```ts
23
23
  // quality.config.ts
24
24
  import { defineConfig } from "@shivaedev/quality";
25
- import { noTodo } from "./quality/no-todo.ts";
25
+ import { noConsoleLog } from "./quality/no-console-log.ts";
26
26
 
27
27
  export default defineConfig({
28
28
  sources: ["src", "scripts"],
29
29
  exclude: ["src/components/ui/", "*.gen.ts"],
30
- local: [noTodo],
30
+ local: [noConsoleLog],
31
31
  rules: {
32
32
  "structure/max-lines": { options: { source: 200, testFiles: ["e2e/"] } },
33
- "local/no-todo": "warn",
33
+ "comments/max-per-file": { options: { max: 3 } },
34
+ "local/no-console-log": "warn",
34
35
  },
35
36
  });
36
37
  ```
@@ -41,7 +42,8 @@ export default defineConfig({
41
42
  | `exclude` | `[]` | Paths never checked, in `.gitignore` syntax. Files ignored by git, `.git` and `node_modules` are always skipped. |
42
43
  | `extensions` | TypeScript and JavaScript modules | Which files rules read as sources. |
43
44
  | `registry` | `quality/registry.json` | Permanent exceptions. |
44
- | `baseline` | `quality/baseline.json` | Existing violations that may only shrink. |
45
+ | `baseline` | `quality/baseline.jsonl` | Existing violations that may only shrink. |
46
+ | `adopt` | `[]` | Rules the baseline takes in for the first time; see [Adopting a rule](#adopting-a-rule). |
45
47
  | `local` | `[]` | The repository's own rules, made with `defineRule`. |
46
48
  | `rules` | every rule at `error` | A level per rule id, or `{ level, options }`. |
47
49
 
@@ -55,51 +57,218 @@ The config is typed: an unknown rule id, a misspelled option or an option of the
55
57
 
56
58
  ## Rules
57
59
 
58
- `structure/max-lines` keeps each module to one job: a source file may have 150 lines and a test file 300, counted the way an editor numbers them. Declaration files are exempt. Its options are `source` and `test` (the limits) and `testFiles`, `.gitignore` patterns that mark test files (`*.test.*`, `*.spec.*`, `test/`, `tests/` and `__tests__/` by default). A violation's measure is the file's line count, so a baselined file may shrink but never grow.
60
+ `structure/max-lines` keeps each module to one job: a source file may have 150 lines and a test file 300, counted the way an editor numbers them. Declaration files are exempt. Its options are `source` and `test` (the limits) and `testFiles`, `.gitignore` patterns that mark test files (`*.test.*`, `*.spec.*`, `test/`, `tests/` and `__tests__/` by default). A file over its limit counts one violation for each line above it: 168 lines under a limit of 150 count 18.
61
+
62
+ ### Comments
63
+
64
+ A comment says why, never what the code already says or what it used to be. Six rules hold every comment to that:
65
+
66
+ | Rule | Reports | Options |
67
+ | --- | --- | --- |
68
+ | `comments/no-jsdoc` | Every `/** */` block, except a tool pragma | `allow` |
69
+ | `comments/no-line-reference` | A line number: `file.ts:42`, `file.ts#L42`, `line 42` | none |
70
+ | `comments/no-pr-reference` | A pull request or issue: `#123`, `PR 123`, `MR 123`, `pull request 123`, `merge request 123`, `issue 123`, `ticket 123`, `/pull/123`, `/pulls/123`, `/issues/123`, `/merge_requests/123`, `GH-123` | none |
71
+ | `comments/no-banner` | A banner, divider or region: a line that starts or ends with three or more of `- = * # ~ _ + / \ ─ ━ ═ ┄ ┈`, `#region`, `#endregion` | none |
72
+ | `comments/no-todo` | `TODO`, `FIXME`, `XXX` and `@todo` | none |
73
+ | `comments/max-per-file` | A file with more than `max` comments, 2 by default | `max`, `allow` |
74
+
75
+ The rules find comments with the TypeScript parser, so text inside strings, template literals, regular expressions and JSX never counts as a comment. They read the TypeScript and JavaScript modules among the sources and skip declaration files and the directives listed below. Each finding names the line its comment starts on.
76
+
77
+ `comments/max-per-file` counts comments this way:
78
+
79
+ - A block comment counts once, however many lines it spans.
80
+ - Line comments that each stand alone on adjacent lines form one run and count once. A blank line, code, a directive, or a comment after code on the same line starts a new one.
81
+ - Tool pragmas and directives are not counted: compiler and linter directives (`@ts-…`, triple-slash directives such as `/// <reference …>`, `biome-ignore…`, `eslint-disable…`, `eslint-enable…`, `oxlint-…`, `stylelint-…`, `deno-lint-ignore…`, `tslint:disable…`, `prettier-ignore`, Flow's `$FlowFixMe`, `$FlowIgnore`, `$FlowExpectedError`, `$FlowIssue` and `@noflow`), bundler annotations (`#__PURE__`, `@__PURE__`, `#__NO_SIDE_EFFECTS__`, `@__NO_SIDE_EFFECTS__`) and coverage hints (`c8 ignore`, `v8 ignore`, `istanbul ignore`). The suppression rules below report the directives that silence a check.
82
+
83
+ A file over the limit counts one violation for each comment above it: 5 comments against a limit of 2 count 3. A finding names the first comment over the limit.
84
+
85
+ A tool pragma is a comment whose every line starts with an allowed tag, such as `/** @vitest-environment happy-dom */`. `allow` lists the tags and defaults to `@vitest-environment`, `@vitest-environment-options`, `@jest-environment`, `@jsx`, `@jsxFrag`, `@jsxImportSource` and `@jsxRuntime`. A list given replaces the default, and `comments/no-jsdoc` and `comments/max-per-file` each take their own, so give both the same list:
86
+
87
+ ```ts
88
+ const pragmas = ["@vitest-environment", "@license"];
89
+
90
+ export default defineConfig({
91
+ rules: {
92
+ "comments/no-jsdoc": { options: { allow: pragmas } },
93
+ "comments/max-per-file": { options: { allow: pragmas } },
94
+ },
95
+ });
96
+ ```
97
+
98
+ A repository with existing comments adopts the rules through the baseline, for example `quality baseline write --rule comments/no-jsdoc --rule comments/max-per-file`.
99
+
100
+ ### Suppressions
101
+
102
+ A check that is silenced at one site hides the problem instead of fixing it. Three rules close the escape hatches, and none of them takes registry exceptions: a registry entry that names one fails the gate as stale. A repository with existing suppressions adopts the rules through the baseline, which only shrinks.
103
+
104
+ | Rule | Reports | Options |
105
+ | --- | --- | --- |
106
+ | `suppressions/no-inline` | Every comment directive that silences a linter, the compiler or a formatter, except a declared `@ts-expect-error` | `declared` |
107
+ | `suppressions/no-double-cast` | A cast through `unknown`, `any` or `never`: `x as unknown as T`, `x as any as T`, `x as never as T`, `<T><unknown>x` | none |
108
+ | `suppressions/biome-overrides` | A Biome setting that turns a check off or down, or keeps files out of it, without a declaration, and a declaration that matches no setting | `declared` |
109
+
110
+ `suppressions/no-inline` reports these directives, wherever a line of a comment starts with one:
111
+
112
+ - Biome: `biome-ignore`, `biome-ignore-all` and `biome-ignore-start`, for lint, assist and format alike. The `biome-ignore-end` that closes a range is not reported again.
113
+ - TypeScript: `@ts-ignore`, `@ts-expect-error` and `@ts-nocheck`. `@ts-check` turns checking on and is allowed.
114
+ - Other linters: `eslint-disable`, `eslint-disable-line`, `eslint-disable-next-line` and inline rule settings such as `/* eslint no-console: "off" */`; the same `-disable` forms of `oxlint` and `stylelint`; `deno-lint-ignore` and `deno-lint-ignore-file`; `tslint:disable…`.
115
+ - Formatters: `prettier-ignore`, since `prettier --check` fails on the code it skips.
116
+ - Flow: `$FlowFixMe`, `$FlowIgnore`, `$FlowExpectedError`, `$FlowIssue` and `@noflow`.
117
+
118
+ Coverage hints (`c8 ignore`, `v8 ignore`, `istanbul ignore`) are allowed: they leave code out of a coverage measure and silence no linter or compiler. The rule reads the TypeScript and JavaScript modules among the sources, declaration files included, and every `.css`, `.scss` and `.less` file among the checked files, whatever `extensions` says. Each finding names the line its comment starts on and has the directive as its subject.
119
+
120
+ #### Declared type tests
121
+
122
+ A type test proves that an API rejects what its types forbid, and TypeScript asserts a compile error only through `@ts-expect-error`, which fails as soon as the error it expects goes away. So `@ts-expect-error`, and no other directive, may be declared for a set of files:
123
+
124
+ ```ts
125
+ export default defineConfig({
126
+ rules: {
127
+ "suppressions/no-inline": {
128
+ options: {
129
+ declared: [
130
+ {
131
+ directive: "@ts-expect-error",
132
+ includes: ["*.typecheck.ts"],
133
+ reason: "Type tests assert the compile errors the public types must raise.",
134
+ },
135
+ ],
136
+ },
137
+ },
138
+ },
139
+ });
140
+ ```
141
+
142
+ `includes` takes `.gitignore` patterns, like `exclude`. `@ts-ignore`, `@ts-nocheck` and every linter and formatter directive stay reported in those files. A declaration that matches no `@ts-expect-error` is reported against `quality.config.ts` until it is removed. A test that must pass a rejected value at run time, to prove the runtime refuses it too, calls the API through `Reflect.apply` instead of a directive.
143
+
144
+ `suppressions/no-double-cast` finds casts with the TypeScript parser, through parentheses and in either assertion syntax. A single cast is left to the linter, and `as const` is not a cast.
145
+
146
+ #### Declared Biome overrides
147
+
148
+ A scope that truly cannot follow a lint rule keeps its exception in the Biome config, and the quality config declares it with a reason. `suppressions/biome-overrides` reads `biome.json` or `biome.jsonc` at the root, every nested `biome.json` and `biome.jsonc` among the checked files, and the local files a config `extends` (an entry that starts with `.`). These settings count as overrides, at the top level and in every `overrides` entry:
149
+
150
+ | Setting | Declared as |
151
+ | --- | --- |
152
+ | A lint rule at `off`, `warn` or `info`, as a string or a `level` | `lint/<group>/<rule>` |
153
+ | A lint group at `off`, `warn` or `info`, or with `recommended: false` or `preset: "none"` | `lint/<group>` |
154
+ | `linter.rules` with `recommended: false` or `preset: "none"` | `lint/recommended` |
155
+ | A domain at `none` | `lint/domains/<domain>` |
156
+ | `linter.enabled: false` | `lint` |
157
+ | An assist action at `off`, such as `useSortedKeys` or `organizeImports` | `assist/<group>/<action>` |
158
+ | `assist.actions` with `recommended: false` or `preset: "none"` | `assist/recommended` |
159
+ | `assist.enabled: false` | `assist` |
160
+ | `formatter.enabled: false` | `format` |
161
+ | `enabled: false` in a language's `linter`, `assist` or `formatter`, such as `css.linter` | `<language>/lint`, `<language>/assist`, `<language>/format` |
162
+ | An `includes` list in `files`, `linter`, `assist` or `formatter` that excludes a pattern with `!` or lacks `**` | `files/includes`, `lint/includes`, `assist/includes`, `format/includes` |
163
+
164
+ Biome runs assist actions, key sorting among them, as part of `biome check` and reports what they would change, so turning one off is an override. Formatter options such as `indentStyle` or `lineWidth` choose a style, not an exception, and are not overrides. Settings that raise a rule or enable it are not overrides either. Rule options are not read, so an option that loosens a rule, such as a higher complexity limit, is left to review.
165
+
166
+ ```ts
167
+ export default defineConfig({
168
+ rules: {
169
+ "suppressions/biome-overrides": {
170
+ options: {
171
+ declared: [
172
+ {
173
+ rule: "lint/style/noDefaultExport",
174
+ includes: ["*.config.ts"],
175
+ reason: "Tools load their config files through the default export.",
176
+ },
177
+ {
178
+ rule: "lint/suspicious/noConsole",
179
+ includes: ["scripts/**"],
180
+ reason: "Scripts report to the terminal they run in.",
181
+ },
182
+ ],
183
+ },
184
+ },
185
+ },
186
+ });
187
+ ```
188
+
189
+ A declaration covers a setting when its `rule` and its `includes` match the setting's exactly, in any order. An `includes` list that keeps files out is declared as the whole list, so every pattern added to it needs the declaration to change too. A setting at the top level of the root config has the scope `["**"]`, and an `overrides` entry has its own `includes` (`["**"]` when it has none). Patterns in a nested config are relative to its folder, so they are declared with the folder in front: `"src/**"` in `packages/web/biome.json` is declared as `"packages/web/src/**"`, and its top level as `"packages/web/**"`. A setting without a declaration is reported at its line in the Biome config; a declaration that no setting matches is reported against the root config, so the list cannot outlive the overrides it explains.
59
190
 
60
191
  ### Local rules
61
192
 
62
193
  ```ts
63
194
  import { defineRule } from "@shivaedev/quality";
64
195
 
65
- export const noTodo = defineRule({
66
- id: "local/no-todo",
67
- description: "Resolve TODOs before merging.",
196
+ export const noConsoleLog = defineRule({
197
+ id: "local/no-console-log",
198
+ description: "Log through the application logger.",
68
199
  check: ({ sources }) =>
69
200
  sources.flatMap((file) =>
70
- file.lines.flatMap((text, index) => (text.includes("TODO") ? [{ file: file.path, line: index + 1, message: "Resolve this TODO." }] : [])),
201
+ file.lines.flatMap((text, index) =>
202
+ text.includes("console.log(") ? [{ file: file.path, line: index + 1, message: "Logs to the console." }] : [],
203
+ ),
71
204
  ),
72
205
  });
73
206
  ```
74
207
 
75
- `check` receives the repository `root`, every checked path in `files`, the `sources` with their `text` and `lines`, `readText(path)` for any other file (undefined when absent) and the validated `options`. It returns findings, or a promise of them. A finding names its `file`, relative to the root (an absolute path under it is made relative), and its `message`, and optionally a `line`, a `subject` that tells apart exceptions of one rule in one file, and a `measure` where larger is worse.
208
+ A rule that must never be excused, like the suppression rules, sets `registrable: false`; the registry then refuses entries for it.
209
+
210
+ `check` receives the repository `root`, every checked path in `files`, the `sources` with their `text` and `lines`, `readText(path)` for any other file (undefined when absent) and the validated `options`. It returns findings, or a promise of them. A finding names its `file`, relative to the root (an absolute path under it is made relative), and its `message`, and optionally a `line`, a `subject` that tells apart exceptions of one rule in one file, a `count` of the violations it stands for (1 by default) and the `threshold` it applied. A rule with a limit reports one finding per file, with the amount over the limit as its `count` and the limit as its `threshold`.
76
211
 
77
212
  A rule with options declares them with any [Standard Schema](https://standardschema.dev), such as `Schema.toStandardSchemaV1(...)` from Effect. Options are an object: when the config gives none, the schema validates `{}`, so give each option a default or make it optional. A rule without an options schema refuses options.
78
213
 
79
214
  ## Baseline
80
215
 
81
- ```json
82
- {
83
- "structure/max-lines": {
84
- "src/server/db.ts": {
85
- "count": 1,
86
- "measure": 412
87
- }
88
- },
89
- "local/no-todo": {
90
- "src/legacy/sync.ts": {
91
- "count": 3
92
- }
93
- }
94
- }
216
+ ```jsonl
217
+ {"path":"src/legacy/sync.ts","rule":"comments/no-jsdoc","count":3}
218
+ {"path":"src/server/db.ts","rule":"structure/max-lines","count":262}
219
+ ```
220
+
221
+ The baseline is a JSON Lines file with one entry per line: the violations of one rule in one file. `count` is how many violations the file may have. For a rule that counts occurrences, such as `comments/no-jsdoc`, that is the number of occurrences. For a rule with a limit, it is the amount over the limit: the entry above lets `src/server/db.ts` have 412 lines under a limit of 150. Entries are sorted by path and then rule, and each tool that changes the file rewrites only the lines it changes, so a diff names exactly the entries that moved and two branches conflict only when they touch the same or neighbouring entries.
222
+
223
+ `quality lint` fails when a baselined file has more violations than its `count`; the report then lists all of that file's violations for the rule. A file at or below its entry passes. An entry that allows more than is left, including a file with no violations left, is listed as a note and does not fail, so fixing debt never breaks the build; `tighten` and `prune` lower it. An entry for a rule that is off or unknown fails until it is pruned. The baseline covers violations at any level.
224
+
225
+ - `quality baseline write` records every error-level violation when there is no baseline yet. Once a baseline exists it refuses, unless `--rule <id>` names the rules to record: it then replaces the entries of those rules with what the files have now, and leaves every other entry as it is.
226
+ - `quality baseline tighten` lowers and removes the entries of files changed since `HEAD`, and with `--staged`, of the files staged for the next commit. It carries the entry of a file that git sees as moved to the new path. Every other line stays byte for byte.
227
+ - `quality baseline prune` does the same for every entry. It carries the entries of files moved since the merge base (see `--against` under [Command line](#command-line)), and prunes without following moves outside a git work tree.
228
+ - `quality baseline migrate` moves a baseline from the earlier JSON format, `quality/baseline.json` or the file `--from` names, to the configured file, and removes the old one. An old entry that stored a size, such as a line count, becomes that size less the rule's current limit; an entry whose file is now within the limit is dropped.
229
+
230
+ Neither `tighten` nor `prune` ever adds or raises an entry. They lower an entry to what its file has now, and remove it when that is 0. A moved file's entry keeps the lower of its old count and what the file has now. A move is seen when both of its sides are tracked, as after `git mv`; git's rename detection decides what counts as a move.
231
+
232
+ ### Pre-commit
233
+
234
+ Lowering entries in the commit that fixes them keeps the baseline current without a separate cleanup:
235
+
236
+ ```sh
237
+ quality baseline tighten --staged && git add quality/baseline.jsonl
238
+ quality lint
239
+ quality baseline check
240
+ ```
241
+
242
+ `tighten` counts the files as they are on disk. When a commit stages only part of a file, stash the rest first (as lint-staged does), or the entry may be lowered below what the commit holds.
243
+
244
+ ### Baseline check
245
+
246
+ `quality lint` reads only the working tree, so on its own it cannot tell a raised entry from a recorded one: a hand edit, or deleting the baseline and writing it again, would hide new debt. `quality baseline check` compares the baseline with its version at the merge base of `HEAD` and the target branch, and fails when:
247
+
248
+ - an entry is new, for a rule the base baseline already covers;
249
+ - an entry's `count` is higher than at the base;
250
+ - a rule is baselined for the first time without `adopt` naming it;
251
+ - `adopt` names a rule with nothing baselined.
252
+
253
+ It compares with the merge base, never the tip of the target branch, so a branch that is behind never fails for debt the target branch paid off since. An entry whose file git sees as moved since the merge base is compared with the entry at the old path. When the base holds the earlier JSON format at `quality/baseline.json`, the check converts it the way `migrate` does, so the change that migrates the baseline passes.
254
+
255
+ The check reads git, not the sources, so it is cheap enough for every commit; only a base in the earlier format makes it run the rules. The target is `--against <ref>`, or else `origin/HEAD`, `origin/main` and then `origin/master`, whichever exists first. In a shallow clone that does not reach the merge base, it runs `git fetch --unshallow`, which never stops to ask for a password, and looks again. It exits 2 when there is no git work tree, no target or no merge base, including when that fetch fails; then check out with `fetch-depth: 0` in GitHub Actions, or fetch enough history for `git merge-base HEAD <target>` to succeed.
256
+
257
+ ### Adopting a rule
258
+
259
+ A rule enters the baseline for the first time only while the config names it under `adopt`, so the adoption shows in the config's diff, not only in the baseline's:
260
+
261
+ ```ts
262
+ export default defineConfig({
263
+ adopt: ["comments/no-jsdoc"],
264
+ });
95
265
  ```
96
266
 
97
- Each entry covers the violations of one rule in one file. The gate fails when a baselined file has more violations than its `count`, or a larger measure than its `measure`; the report then lists all of that file's violations for the rule. It also fails when an entry allows more than is left, including a file with no violations left and a rule that is off or unknown. The baseline covers violations at any level.
267
+ Run `quality baseline write --rule comments/no-jsdoc` with the rule at `error`, and commit both. `adopt` lets in only rules that the base baseline does not cover: once the adoption is merged, the rule's entries only shrink like any other. Leave the rule in `adopt` while it has debt; when its last entry is gone, the check fails until it is removed, so `adopt` cannot let the rule back in later. The first baseline of a repository adopts each of its rules the same way.
98
268
 
99
- - `quality baseline write` records every error-level violation when there is no baseline yet. Once a baseline exists it refuses, unless `--rule <id>` names rules the baseline does not cover yet: that is how a rule is adopted later. It never raises an entry.
100
- - `quality baseline prune` drops fixed debt and lowers entries to what is left. It never adds or raises an entry.
269
+ ### Changing a limit
101
270
 
102
- The file keeps the indentation it has, and its rules and files are sorted, so its diffs stay small.
271
+ A count depends on the configured limit, so changing a limit shifts every count of the rule. A looser limit leaves entries that allow more than is left: they pass, and `tighten` and `prune` lower them. A stricter limit makes the rule's files fail `quality lint`, since each is now further over the limit. Run `quality baseline write --rule <id>` to record the rule again under the new limit, and commit it with the config change. `quality baseline check` then fails on the raised entries, as it does for any growth; the change merges only when an owner of the repository merges it over the failed check on purpose.
103
272
 
104
273
  ## Registry
105
274
 
@@ -113,24 +282,27 @@ The file keeps the indentation it has, and its rules and files are sorted, so it
113
282
  ]
114
283
  ```
115
284
 
116
- An entry covers a rule's violations in one file, for good, and must say why. With a `subject`, it covers only the violations with that subject. The registry applies before the baseline. An entry that covers nothing, or names a rule that is off or unknown, fails the gate until it is removed.
285
+ An entry covers a rule's violations in one file, for good, and must say why. With a `subject`, it covers only the violations with that subject. The registry applies before the baseline. An entry that covers nothing, or names a rule that is off, unknown or takes no exceptions, fails the gate until it is removed.
117
286
 
118
287
  ## Command line
119
288
 
120
289
  ```text
121
290
  quality lint [--config <file>] [--warnings summary|all]
122
291
  quality baseline write [--config <file>] [--rule <id>]...
123
- quality baseline prune [--config <file>]
292
+ quality baseline prune [--config <file>] [--against <ref>]
293
+ quality baseline tighten [--config <file>] [--staged]
294
+ quality baseline check [--config <file>] [--against <ref>]
295
+ quality baseline migrate [--config <file>] [--from <file>]
124
296
  ```
125
297
 
126
- `quality` alone runs `lint`. The report groups violations by rule and states each rule's description once; warnings are summarized per rule with the files that have the most, and `--warnings all` lists each one.
298
+ `quality` alone runs `lint`. `--against <ref>` names the branch the work merges into. The report groups violations by rule and states each rule's description once; warnings are summarized per rule with the files that have the most, and `--warnings all` lists each one.
127
299
 
128
300
  | Exit code | Meaning |
129
301
  | --- | --- |
130
302
  | 0 | Passed. Warnings may remain. |
131
- | 1 | Failed: an uncovered error-level violation, a baselined file that got worse, or a stale baseline or registry entry. |
132
- | 2 | Could not run: no or invalid config, an invalid baseline or registry, a missing source, a rule that threw, or a usage error. |
303
+ | 1 | Failed: an uncovered error-level violation, a baselined file that got worse, a stale registry entry, a baseline entry for a rule that is off or unknown, or a baseline that grew against the merge base. |
304
+ | 2 | Could not run: no or invalid config, an invalid baseline or registry, a baseline left in the earlier format, a missing source, a rule that threw, git history the check cannot read, or a usage error. |
133
305
 
134
306
  ## Validation
135
307
 
136
- `pnpm ready` checks formatting, both TypeScript compilers, the rule, config, discovery, registry, baseline and report behavior, the command line against seeded repositories, and an installed tarball consumer that type-checks a config and runs the `quality` bin through a baseline cycle.
308
+ `pnpm ready` checks formatting, both TypeScript compilers, the rules, config, discovery, registry, baseline and report behavior, the command line against seeded repositories and git histories, and an installed tarball consumer that type-checks a config and runs the `quality` bin through a baseline cycle.
@@ -1,13 +1,9 @@
1
1
  import type { Level } from "../config.ts";
2
2
  import { type Violation } from "../engine/violation.ts";
3
3
  import type { BaselineEntry } from "./format.ts";
4
- export interface Tally {
5
- readonly count: number;
6
- readonly measure: number | undefined;
7
- }
8
4
  export interface Regression {
9
5
  readonly entry: BaselineEntry;
10
- readonly tally: Tally;
6
+ readonly count: number;
11
7
  }
12
8
  export interface StaleBaselineEntry {
13
9
  readonly entry: BaselineEntry;
@@ -17,10 +13,9 @@ export interface BaselineCheck {
17
13
  readonly kept: ReadonlyArray<Violation>;
18
14
  readonly regressions: ReadonlyArray<Regression>;
19
15
  readonly stale: ReadonlyArray<StaleBaselineEntry>;
16
+ readonly loose: ReadonlyArray<StaleBaselineEntry>;
20
17
  readonly baselined: number;
21
18
  }
22
- export declare const tallyOf: (violations: ReadonlyArray<Violation>) => Tally;
23
- export declare const describeTally: (tally: Tally) => string;
24
- export declare const describeEntry: (entry: BaselineEntry) => string;
19
+ export declare const countOf: (violations: ReadonlyArray<Violation>) => number;
25
20
  export declare const applyBaseline: (violations: ReadonlyArray<Violation>, entries: ReadonlyArray<BaselineEntry>, levels: ReadonlyMap<string, Level>) => BaselineCheck;
26
21
  //# sourceMappingURL=compare.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../src/baseline/compare.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC5F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,WAAW,KAAK;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IACxC,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAChD,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAClD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;AAOD,eAAO,MAAM,OAAO,GAAI,YAAY,aAAa,CAAC,SAAS,CAAC,KAAG,KAG9D,CAAC;AAaF,eAAO,MAAM,aAAa,GAAI,OAAO,KAAK,KAAG,MACgF,CAAC;AAE9H,eAAO,MAAM,aAAa,GAAI,OAAO,aAAa,KAAG,MAAuE,CAAC;AAE7H,eAAO,MAAM,aAAa,GACzB,YAAY,aAAa,CAAC,SAAS,CAAC,EACpC,SAAS,aAAa,CAAC,aAAa,CAAC,EACrC,QAAQ,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,KAChC,aAwBF,CAAC"}
1
+ {"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../src/baseline/compare.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC5F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IACxC,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAChD,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAClD,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC;IAClD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;AAED,eAAO,MAAM,OAAO,GAAI,YAAY,aAAa,CAAC,SAAS,CAAC,KAAG,MAAoF,CAAC;AAEpJ,eAAO,MAAM,aAAa,GACzB,YAAY,aAAa,CAAC,SAAS,CAAC,EACpC,SAAS,aAAa,CAAC,aAAa,CAAC,EACrC,QAAQ,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,KAChC,aA4BF,CAAC"}
@@ -1,42 +1,32 @@
1
1
  import { groupBy, keyOf, unusedEntryProblem } from "../engine/violation.js";
2
- const measures = (violations) => violations.flatMap((violation) => (violation.measure === undefined ? [] : [violation.measure]));
3
- export const tallyOf = (violations) => {
4
- const found = measures(violations);
5
- return { count: violations.length, measure: found.length === 0 ? undefined : Math.max(...found) };
6
- };
7
- const measureTrend = (entry, tally) => entry.measure === undefined || tally.measure === undefined ? 0 : Math.sign(tally.measure - entry.measure);
8
- const verdictOf = (entry, tally) => {
9
- const trend = measureTrend(entry, tally);
10
- if (tally.count > entry.count || trend > 0) {
11
- return { _tag: "Regressed" };
12
- }
13
- return { _tag: "Covered", loose: tally.count < entry.count || trend < 0 };
14
- };
15
- export const describeTally = (tally) => `${tally.count} violation${tally.count === 1 ? "" : "s"}${tally.measure === undefined ? "" : ` measuring ${tally.measure}`}`;
16
- export const describeEntry = (entry) => describeTally({ count: entry.count, measure: entry.measure });
2
+ export const countOf = (violations) => violations.reduce((total, violation) => total + (violation.count ?? 1), 0);
17
3
  export const applyBaseline = (violations, entries, levels) => {
18
4
  const groups = groupBy(violations, (violation) => keyOf(violation.rule, violation.file));
19
5
  const covered = new Set();
20
6
  const regressions = [];
21
7
  const stale = [];
8
+ const loose = [];
22
9
  for (const entry of entries) {
23
10
  const group = groups.get(keyOf(entry.rule, entry.file)) ?? [];
24
11
  if (group.length === 0) {
25
- stale.push({ entry, problem: unusedEntryProblem(levels, entry.rule, "has no violations left") });
12
+ const level = levels.get(entry.rule);
13
+ (level === undefined || level === "off" ? stale : loose).push({
14
+ entry,
15
+ problem: unusedEntryProblem(levels, entry.rule, "has no violations left"),
16
+ });
26
17
  continue;
27
18
  }
28
- const tally = tallyOf(group);
29
- const verdict = verdictOf(entry, tally);
30
- if (verdict._tag === "Regressed") {
31
- regressions.push({ entry, tally });
19
+ const count = countOf(group);
20
+ if (count > entry.count) {
21
+ regressions.push({ count, entry });
32
22
  continue;
33
23
  }
34
24
  covered.add(keyOf(entry.rule, entry.file));
35
- if (verdict.loose) {
36
- stale.push({ entry, problem: `allows more than is left: ${describeTally(tally)} against ${describeEntry(entry)} baselined` });
25
+ if (count < entry.count) {
26
+ loose.push({ entry, problem: `allows more than is left: ${count} against ${entry.count} baselined` });
37
27
  }
38
28
  }
39
29
  const kept = violations.filter((violation) => !covered.has(keyOf(violation.rule, violation.file)));
40
- return { baselined: violations.length - kept.length, kept, regressions, stale };
30
+ return { baselined: violations.length - kept.length, kept, loose, regressions, stale };
41
31
  };
42
32
  //# sourceMappingURL=compare.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compare.js","sourceRoot":"","sources":["../../src/baseline/compare.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compare.js","sourceRoot":"","sources":["../../src/baseline/compare.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAkB,MAAM,wBAAwB,CAAC;AAqB5F,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,UAAoC,EAAU,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAEpJ,MAAM,CAAC,MAAM,aAAa,GAAG,CAC5B,UAAoC,EACpC,OAAqC,EACrC,MAAkC,EAClB,EAAE;IAClB,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IACzF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,MAAM,WAAW,GAAiB,EAAE,CAAC;IACrC,MAAM,KAAK,GAAyB,EAAE,CAAC;IACvC,MAAM,KAAK,GAAyB,EAAE,CAAC;IACvC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACrC,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC;gBAC7D,KAAK;gBACL,OAAO,EAAE,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE,wBAAwB,CAAC;aACzE,CAAC,CAAC;YACH,SAAS;QACV,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAC7B,IAAI,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;YACzB,WAAW,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YACnC,SAAS;QACV,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3C,IAAI,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;YACzB,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,6BAA6B,KAAK,YAAY,KAAK,CAAC,KAAK,YAAY,EAAE,CAAC,CAAC;QACvG,CAAC;IACF,CAAC;IACD,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACnG,OAAO,EAAE,SAAS,EAAE,UAAU,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;AACxF,CAAC,CAAC"}
@@ -3,11 +3,13 @@ export interface BaselineEntry {
3
3
  readonly rule: string;
4
4
  readonly file: string;
5
5
  readonly count: number;
6
- readonly measure?: number | undefined;
7
6
  }
8
- export declare const byRuleAndFile: (left: BaselineEntry, right: BaselineEntry) => number;
7
+ export declare const byPathAndRule: (left: BaselineEntry, right: BaselineEntry) => number;
8
+ export declare const linesOf: (raw: string | undefined) => ReadonlyArray<{
9
+ readonly line: number;
10
+ readonly text: string;
11
+ }>;
9
12
  export declare const decodeBaseline: (raw: string | undefined) => Promise<Decoded<ReadonlyArray<BaselineEntry>>>;
10
- export declare const encodeBaseline: (entries: ReadonlyArray<BaselineEntry>, indent: string) => string;
11
- /** Keeps the indentation of the file being rewritten, so the repository formatter leaves it alone. */
12
- export declare const indentOf: (raw: string | undefined) => string;
13
+ export declare const encodeEntry: (entry: BaselineEntry) => string;
14
+ export declare const encodeBaseline: (entries: ReadonlyArray<BaselineEntry>) => string;
13
15
  //# sourceMappingURL=format.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/baseline/format.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,OAAO,EAAc,MAAM,eAAe,CAAC;AAWzD,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AAMD,eAAO,MAAM,aAAa,GAAI,MAAM,aAAa,EAAE,OAAO,aAAa,KAAG,MACH,CAAC;AAExE,eAAO,MAAM,cAAc,GAAU,KAAK,MAAM,GAAG,SAAS,KAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAY3G,CAAC;AAKF,eAAO,MAAM,cAAc,GAAI,SAAS,aAAa,CAAC,aAAa,CAAC,EAAE,QAAQ,MAAM,KAAG,MAMtF,CAAC;AAEF,sGAAsG;AACtG,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,GAAG,SAAS,KAAG,MAAqD,CAAC"}
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/baseline/format.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,OAAO,EAAc,MAAM,eAAe,CAAC;AAWzD,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACvB;AAID,eAAO,MAAM,aAAa,GAAI,MAAM,aAAa,EAAE,OAAO,aAAa,KAAG,MACH,CAAC;AAExE,eAAO,MAAM,OAAO,GAAI,KAAK,MAAM,GAAG,SAAS,KAAG,aAAa,CAAC;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAItE,CAAC;AAE3C,eAAO,MAAM,cAAc,GAAU,KAAK,MAAM,GAAG,SAAS,KAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAkB3G,CAAC;AAEF,eAAO,MAAM,WAAW,GAAI,OAAO,aAAa,KAAG,MAAoF,CAAC;AAExI,eAAO,MAAM,cAAc,GAAI,SAAS,aAAa,CAAC,aAAa,CAAC,KAAG,MAI5D,CAAC"}