@shivaedev/quality 0.22.0 → 0.24.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 (294) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +4 -0
  3. package/dist/baseline/compare.d.ts +2 -2
  4. package/dist/baseline/compare.d.ts.map +1 -1
  5. package/dist/baseline/compare.js +5 -3
  6. package/dist/baseline/compare.js.map +1 -1
  7. package/dist/baseline/format.d.ts +5 -5
  8. package/dist/baseline/format.d.ts.map +1 -1
  9. package/dist/baseline/format.js +23 -13
  10. package/dist/baseline/format.js.map +1 -1
  11. package/dist/baseline/legacy.d.ts +2 -2
  12. package/dist/baseline/legacy.d.ts.map +1 -1
  13. package/dist/baseline/legacy.js +4 -4
  14. package/dist/baseline/legacy.js.map +1 -1
  15. package/dist/baseline/rewrite.d.ts +1 -1
  16. package/dist/baseline/rewrite.d.ts.map +1 -1
  17. package/dist/baseline/rewrite.js +2 -2
  18. package/dist/baseline/rewrite.js.map +1 -1
  19. package/dist/baseline/update.d.ts +2 -2
  20. package/dist/baseline/update.d.ts.map +1 -1
  21. package/dist/baseline/update.js +24 -18
  22. package/dist/baseline/update.js.map +1 -1
  23. package/dist/cli/args.d.ts +1 -1
  24. package/dist/cli/args.d.ts.map +1 -1
  25. package/dist/cli/args.js +15 -9
  26. package/dist/cli/args.js.map +1 -1
  27. package/dist/cli/baseline.d.ts +1 -1
  28. package/dist/cli/baseline.d.ts.map +1 -1
  29. package/dist/cli/baseline.js +10 -6
  30. package/dist/cli/baseline.js.map +1 -1
  31. package/dist/cli/fix.d.ts +2 -2
  32. package/dist/cli/fix.d.ts.map +1 -1
  33. package/dist/cli/fix.js +34 -40
  34. package/dist/cli/fix.js.map +1 -1
  35. package/dist/cli/lint.d.ts +1 -1
  36. package/dist/cli/lint.d.ts.map +1 -1
  37. package/dist/cli/lint.js +1 -1
  38. package/dist/cli/lint.js.map +1 -1
  39. package/dist/cli/migrate.d.ts.map +1 -1
  40. package/dist/cli/migrate.js +1 -1
  41. package/dist/cli/migrate.js.map +1 -1
  42. package/dist/cli/program.d.ts +9 -0
  43. package/dist/cli/program.d.ts.map +1 -0
  44. package/dist/cli/program.js +44 -0
  45. package/dist/cli/program.js.map +1 -0
  46. package/dist/cli/run-main.d.ts +5 -2
  47. package/dist/cli/run-main.d.ts.map +1 -1
  48. package/dist/cli/run-main.js +16 -12
  49. package/dist/cli/run-main.js.map +1 -1
  50. package/dist/cli/shrink.d.ts +2 -2
  51. package/dist/cli/shrink.d.ts.map +1 -1
  52. package/dist/cli/shrink.js +9 -3
  53. package/dist/cli/shrink.js.map +1 -1
  54. package/dist/cli.js +2 -49
  55. package/dist/cli.js.map +1 -1
  56. package/dist/config/decode.d.ts +1 -1
  57. package/dist/config/decode.d.ts.map +1 -1
  58. package/dist/config/decode.js +13 -9
  59. package/dist/config/decode.js.map +1 -1
  60. package/dist/config/load.d.ts.map +1 -1
  61. package/dist/config/load.js +26 -20
  62. package/dist/config/load.js.map +1 -1
  63. package/dist/config/resolve.d.ts +22 -2
  64. package/dist/config/resolve.d.ts.map +1 -1
  65. package/dist/config/resolve.js +18 -10
  66. package/dist/config/resolve.js.map +1 -1
  67. package/dist/config.d.ts +1 -1
  68. package/dist/config.d.ts.map +1 -1
  69. package/dist/config.js +3 -1
  70. package/dist/config.js.map +1 -1
  71. package/dist/decoded.d.ts +2 -2
  72. package/dist/decoded.d.ts.map +1 -1
  73. package/dist/decoded.js +7 -5
  74. package/dist/decoded.js.map +1 -1
  75. package/dist/engine/baseline-file.d.ts +1 -1
  76. package/dist/engine/baseline-file.d.ts.map +1 -1
  77. package/dist/engine/baseline-file.js +4 -2
  78. package/dist/engine/baseline-file.js.map +1 -1
  79. package/dist/engine/evaluate.d.ts +2 -2
  80. package/dist/engine/evaluate.d.ts.map +1 -1
  81. package/dist/engine/evaluate.js +5 -3
  82. package/dist/engine/evaluate.js.map +1 -1
  83. package/dist/engine/run-rules.d.ts +1 -1
  84. package/dist/engine/run-rules.d.ts.map +1 -1
  85. package/dist/engine/run-rules.js +24 -14
  86. package/dist/engine/run-rules.js.map +1 -1
  87. package/dist/engine/session.d.ts.map +1 -1
  88. package/dist/engine/session.js +8 -4
  89. package/dist/engine/session.js.map +1 -1
  90. package/dist/engine/violation.d.ts +7 -7
  91. package/dist/engine/violation.d.ts.map +1 -1
  92. package/dist/engine/violation.js +22 -10
  93. package/dist/engine/violation.js.map +1 -1
  94. package/dist/exceptions/registry.d.ts +2 -2
  95. package/dist/exceptions/registry.d.ts.map +1 -1
  96. package/dist/exceptions/registry.js +10 -6
  97. package/dist/exceptions/registry.js.map +1 -1
  98. package/dist/git/base.d.ts +1 -2
  99. package/dist/git/base.d.ts.map +1 -1
  100. package/dist/git/base.js +22 -10
  101. package/dist/git/base.js.map +1 -1
  102. package/dist/git/command.d.ts +2 -2
  103. package/dist/git/command.d.ts.map +1 -1
  104. package/dist/git/command.js +13 -9
  105. package/dist/git/command.js.map +1 -1
  106. package/dist/git/history.d.ts +1 -1
  107. package/dist/git/history.d.ts.map +1 -1
  108. package/dist/git/history.js +5 -3
  109. package/dist/git/history.js.map +1 -1
  110. package/dist/inventory/collect.d.ts +1 -1
  111. package/dist/inventory/collect.d.ts.map +1 -1
  112. package/dist/inventory/collect.js +7 -5
  113. package/dist/inventory/collect.js.map +1 -1
  114. package/dist/inventory/filesystem.d.ts +1 -1
  115. package/dist/inventory/filesystem.d.ts.map +1 -1
  116. package/dist/inventory/filesystem.js +11 -7
  117. package/dist/inventory/filesystem.js.map +1 -1
  118. package/dist/inventory/ignore-scope.d.ts +4 -4
  119. package/dist/inventory/ignore-scope.d.ts.map +1 -1
  120. package/dist/inventory/ignore-scope.js +18 -9
  121. package/dist/inventory/ignore-scope.js.map +1 -1
  122. package/dist/manifests/sorted.d.ts +1 -1
  123. package/dist/manifests/sorted.d.ts.map +1 -1
  124. package/dist/manifests/sorted.js +14 -16
  125. package/dist/manifests/sorted.js.map +1 -1
  126. package/dist/report/plural.d.ts +1 -1
  127. package/dist/report/plural.d.ts.map +1 -1
  128. package/dist/report/plural.js +3 -1
  129. package/dist/report/plural.js.map +1 -1
  130. package/dist/report/render.d.ts +1 -1
  131. package/dist/report/render.d.ts.map +1 -1
  132. package/dist/report/render.js +29 -21
  133. package/dist/report/render.js.map +1 -1
  134. package/dist/report/stale.d.ts +1 -1
  135. package/dist/report/stale.d.ts.map +1 -1
  136. package/dist/report/stale.js +18 -8
  137. package/dist/report/stale.js.map +1 -1
  138. package/dist/rule.d.ts +13 -13
  139. package/dist/rule.d.ts.map +1 -1
  140. package/dist/rule.js +10 -8
  141. package/dist/rule.js.map +1 -1
  142. package/dist/rules/built-in.d.ts +2 -2
  143. package/dist/rules/built-in.d.ts.map +1 -1
  144. package/dist/rules/built-in.js +6 -0
  145. package/dist/rules/built-in.js.map +1 -1
  146. package/dist/rules/comments/kinds.d.ts +4 -4
  147. package/dist/rules/comments/kinds.d.ts.map +1 -1
  148. package/dist/rules/comments/kinds.js +16 -8
  149. package/dist/rules/comments/kinds.js.map +1 -1
  150. package/dist/rules/comments/max-per-file.d.ts.map +1 -1
  151. package/dist/rules/comments/max-per-file.js +13 -9
  152. package/dist/rules/comments/max-per-file.js.map +1 -1
  153. package/dist/rules/comments/pattern-rule.d.ts +1 -1
  154. package/dist/rules/comments/pattern-rule.d.ts.map +1 -1
  155. package/dist/rules/comments/pattern-rule.js +12 -10
  156. package/dist/rules/comments/pattern-rule.js.map +1 -1
  157. package/dist/rules/comments/scan.d.ts +3 -3
  158. package/dist/rules/comments/scan.d.ts.map +1 -1
  159. package/dist/rules/comments/scan.js +23 -19
  160. package/dist/rules/comments/scan.js.map +1 -1
  161. package/dist/rules/max-lines.d.ts.map +1 -1
  162. package/dist/rules/max-lines.js +12 -10
  163. package/dist/rules/max-lines.js.map +1 -1
  164. package/dist/rules/maxDepth.d.ts +2 -0
  165. package/dist/rules/maxDepth.d.ts.map +1 -0
  166. package/dist/rules/maxDepth.js +20 -0
  167. package/dist/rules/maxDepth.js.map +1 -0
  168. package/dist/rules/noBarrel.d.ts +2 -0
  169. package/dist/rules/noBarrel.d.ts.map +1 -0
  170. package/dist/rules/noBarrel.js +14 -0
  171. package/dist/rules/noBarrel.js.map +1 -0
  172. package/dist/rules/stories/origins.d.ts +10 -0
  173. package/dist/rules/stories/origins.d.ts.map +1 -0
  174. package/dist/rules/stories/origins.js +129 -0
  175. package/dist/rules/stories/origins.js.map +1 -0
  176. package/dist/rules/stories/prerequisites.d.ts +4 -0
  177. package/dist/rules/stories/prerequisites.d.ts.map +1 -0
  178. package/dist/rules/stories/prerequisites.js +32 -0
  179. package/dist/rules/stories/prerequisites.js.map +1 -0
  180. package/dist/rules/stories/program.d.ts +5 -0
  181. package/dist/rules/stories/program.d.ts.map +1 -0
  182. package/dist/rules/stories/program.js +22 -0
  183. package/dist/rules/stories/program.js.map +1 -0
  184. package/dist/rules/stories/specs.d.ts +2 -0
  185. package/dist/rules/stories/specs.d.ts.map +1 -0
  186. package/dist/rules/stories/specs.js +80 -0
  187. package/dist/rules/stories/specs.js.map +1 -0
  188. package/dist/rules/suppressions/biome/configs.d.ts +1 -1
  189. package/dist/rules/suppressions/biome/configs.d.ts.map +1 -1
  190. package/dist/rules/suppressions/biome/configs.js +23 -17
  191. package/dist/rules/suppressions/biome/configs.js.map +1 -1
  192. package/dist/rules/suppressions/biome/declarations.d.ts +4 -4
  193. package/dist/rules/suppressions/biome/declarations.d.ts.map +1 -1
  194. package/dist/rules/suppressions/biome/declarations.js +15 -7
  195. package/dist/rules/suppressions/biome/declarations.js.map +1 -1
  196. package/dist/rules/suppressions/biome/json.d.ts +6 -6
  197. package/dist/rules/suppressions/biome/json.d.ts.map +1 -1
  198. package/dist/rules/suppressions/biome/json.js +27 -13
  199. package/dist/rules/suppressions/biome/json.js.map +1 -1
  200. package/dist/rules/suppressions/biome/resolve.d.ts +1 -1
  201. package/dist/rules/suppressions/biome/resolve.d.ts.map +1 -1
  202. package/dist/rules/suppressions/biome/resolve.js +23 -17
  203. package/dist/rules/suppressions/biome/resolve.js.map +1 -1
  204. package/dist/rules/suppressions/biome/weakenings.d.ts +1 -1
  205. package/dist/rules/suppressions/biome/weakenings.d.ts.map +1 -1
  206. package/dist/rules/suppressions/biome/weakenings.js +78 -52
  207. package/dist/rules/suppressions/biome/weakenings.js.map +1 -1
  208. package/dist/rules/suppressions/biome-overrides.d.ts.map +1 -1
  209. package/dist/rules/suppressions/biome-overrides.js +45 -33
  210. package/dist/rules/suppressions/biome-overrides.js.map +1 -1
  211. package/dist/rules/suppressions/directives.d.ts +1 -1
  212. package/dist/rules/suppressions/directives.d.ts.map +1 -1
  213. package/dist/rules/suppressions/directives.js +5 -3
  214. package/dist/rules/suppressions/directives.js.map +1 -1
  215. package/dist/rules/suppressions/no-double-cast.d.ts.map +1 -1
  216. package/dist/rules/suppressions/no-double-cast.js +14 -10
  217. package/dist/rules/suppressions/no-double-cast.js.map +1 -1
  218. package/dist/rules/suppressions/no-inline.d.ts.map +1 -1
  219. package/dist/rules/suppressions/no-inline.js +8 -6
  220. package/dist/rules/suppressions/no-inline.js.map +1 -1
  221. package/dist/rules/suppressions/stylesheet-comments.d.ts +1 -1
  222. package/dist/rules/suppressions/stylesheet-comments.d.ts.map +1 -1
  223. package/dist/rules/suppressions/stylesheet-comments.js +11 -5
  224. package/dist/rules/suppressions/stylesheet-comments.js.map +1 -1
  225. package/dist/rules/syntax.d.ts +1 -1
  226. package/dist/rules/syntax.d.ts.map +1 -1
  227. package/dist/rules/syntax.js +2 -2
  228. package/dist/rules/syntax.js.map +1 -1
  229. package/dist/standard-schema.d.ts +6 -6
  230. package/dist/standard-schema.d.ts.map +1 -1
  231. package/docs/modules.md +19 -1
  232. package/docs/roadmap.md +1 -1
  233. package/docs/rules.md +9 -1
  234. package/package.json +2 -2
  235. package/src/baseline/compare.ts +5 -3
  236. package/src/baseline/format.ts +17 -10
  237. package/src/baseline/legacy.ts +4 -4
  238. package/src/baseline/rewrite.ts +2 -2
  239. package/src/baseline/update.ts +20 -16
  240. package/src/cli/args.ts +15 -10
  241. package/src/cli/baseline.ts +36 -35
  242. package/src/cli/fix.ts +39 -42
  243. package/src/cli/lint.ts +17 -19
  244. package/src/cli/migrate.ts +20 -21
  245. package/src/cli/program.ts +50 -0
  246. package/src/cli/run-main.ts +19 -13
  247. package/src/cli/shrink.ts +11 -8
  248. package/src/cli.ts +2 -51
  249. package/src/config/decode.ts +15 -10
  250. package/src/config/load.ts +44 -37
  251. package/src/config/resolve.ts +32 -27
  252. package/src/config.ts +3 -1
  253. package/src/decoded.ts +5 -4
  254. package/src/engine/baseline-file.ts +14 -12
  255. package/src/engine/evaluate.ts +6 -5
  256. package/src/engine/run-rules.ts +21 -13
  257. package/src/engine/session.ts +32 -25
  258. package/src/engine/violation.ts +22 -11
  259. package/src/exceptions/registry.ts +10 -8
  260. package/src/git/base.ts +45 -37
  261. package/src/git/command.ts +6 -4
  262. package/src/git/history.ts +5 -4
  263. package/src/inventory/collect.ts +39 -34
  264. package/src/inventory/filesystem.ts +38 -30
  265. package/src/inventory/ignore-scope.ts +20 -10
  266. package/src/manifests/sorted.ts +18 -18
  267. package/src/report/plural.ts +3 -1
  268. package/src/report/render.ts +23 -18
  269. package/src/report/stale.ts +33 -25
  270. package/src/rule.ts +25 -19
  271. package/src/rules/built-in.ts +6 -0
  272. package/src/rules/comments/kinds.ts +16 -9
  273. package/src/rules/comments/max-per-file.ts +15 -11
  274. package/src/rules/comments/pattern-rule.ts +5 -4
  275. package/src/rules/comments/scan.ts +18 -15
  276. package/src/rules/max-lines.ts +3 -2
  277. package/src/rules/maxDepth.ts +24 -0
  278. package/src/rules/noBarrel.ts +16 -0
  279. package/src/rules/stories/origins.ts +147 -0
  280. package/src/rules/stories/prerequisites.ts +37 -0
  281. package/src/rules/stories/program.ts +29 -0
  282. package/src/rules/stories/specs.ts +93 -0
  283. package/src/rules/suppressions/biome/configs.ts +23 -17
  284. package/src/rules/suppressions/biome/declarations.ts +15 -10
  285. package/src/rules/suppressions/biome/json.ts +25 -13
  286. package/src/rules/suppressions/biome/resolve.ts +23 -18
  287. package/src/rules/suppressions/biome/weakenings.ts +60 -41
  288. package/src/rules/suppressions/biome-overrides.ts +32 -22
  289. package/src/rules/suppressions/directives.ts +5 -4
  290. package/src/rules/suppressions/no-double-cast.ts +14 -10
  291. package/src/rules/suppressions/no-inline.ts +5 -4
  292. package/src/rules/suppressions/stylesheet-comments.ts +11 -6
  293. package/src/rules/syntax.ts +2 -2
  294. package/src/standard-schema.ts +4 -4
@@ -0,0 +1,93 @@
1
+ import ts from "typescript";
2
+ import { importGraph } from "#imports/graph.ts";
3
+ import { packageOf } from "#imports/workspace.ts";
4
+ import { testName } from "#naming/testName.ts";
5
+ import { defineRule } from "#rule.ts";
6
+ import { storyOrigins } from "./origins.ts";
7
+ import { storyPrerequisites } from "./prerequisites.ts";
8
+
9
+ function namesTest(node: ts.Expression): boolean {
10
+ if (ts.isIdentifier(node)) {
11
+ return node.text === "it" || node.text === "test";
12
+ }
13
+ if (ts.isPropertyAccessExpression(node)) {
14
+ return node.name.text === "it" || namesTest(node.expression);
15
+ }
16
+ return ts.isCallExpression(node) && namesTest(node.expression);
17
+ }
18
+
19
+ type OriginOf = ReturnType<typeof storyOrigins>["origin"];
20
+
21
+ function callback(node: ts.Node, origin: OriginOf): boolean {
22
+ return (
23
+ ts.isArrowFunction(node)
24
+ || ts.isFunctionExpression(node)
25
+ || ((ts.isIdentifier(node) || ts.isPropertyAccessExpression(node)) && origin(node) === "function")
26
+ );
27
+ }
28
+
29
+ function testShaped(call: ts.CallExpression, origin: OriginOf): boolean {
30
+ const title = call.arguments[0];
31
+ return (
32
+ title !== undefined
33
+ && (ts.isStringLiteralLike(title) || ts.isTemplateExpression(title))
34
+ && call.arguments.slice(1).some((argument) => callback(argument, origin))
35
+ );
36
+ }
37
+
38
+ function declarationScope(node: ts.Node, origin: OriginOf): boolean {
39
+ for (let current = node.parent; current !== undefined; current = current.parent) {
40
+ if (ts.isFunctionLike(current) && (!ts.isCallExpression(current.parent) || origin(current.parent.expression) !== "suite")) {
41
+ return false;
42
+ }
43
+ }
44
+ return true;
45
+ }
46
+
47
+ function uncoveredRegistration(call: ts.CallExpression, originOf: OriginOf): boolean {
48
+ const origin = originOf(call.expression);
49
+ if (origin === "story" || origin === "suite") {
50
+ return false;
51
+ }
52
+ return origin === "plain" || ((namesTest(call.expression) || testShaped(call, originOf)) && declarationScope(call, originOf));
53
+ }
54
+
55
+ export const storySpecs = defineRule({
56
+ check: async (inputs) => {
57
+ const graph = await importGraph(inputs);
58
+ const exempt = await storyPrerequisites(inputs, graph.packages);
59
+ const origins = storyOrigins(inputs, graph);
60
+ return inputs.sources.flatMap((file) => {
61
+ const name = testName(file.path);
62
+ const owner = packageOf(graph.packages, file.path);
63
+ if (name === undefined || name.modifiers.includes("typecheck") || exempt.has(owner?.directory ?? "")) {
64
+ return [];
65
+ }
66
+ const source = origins.source(file.path);
67
+ let plain: ts.CallExpression | undefined;
68
+ function visit(node: ts.Node): void {
69
+ if (ts.isCallExpression(node) && uncoveredRegistration(node, origins.origin)) {
70
+ plain ??= node;
71
+ }
72
+ ts.forEachChild(node, visit);
73
+ }
74
+ if (source !== undefined) {
75
+ visit(source);
76
+ }
77
+ if (source === undefined || plain === undefined) {
78
+ return [];
79
+ }
80
+ return [
81
+ {
82
+ file: file.path,
83
+ line: source.getLineAndCharacterOfPosition(plain.getStart(source)).line + 1,
84
+ message:
85
+ "Declares tests without a test-story story kit. Build the package's kit with storyKit from @shivaedev/test-story/storyKit.ts in src/test-support, and declare every test through the kit's it. See https://github.com/ShivaeDev/platform/tree/main/packages/quality#story-tests.",
86
+ },
87
+ ];
88
+ });
89
+ },
90
+ description:
91
+ "Every package tests its real engine through its own test-story kit. Typecheck tests and test-story's workspace prerequisites are exempt.",
92
+ id: "tests/story-specs",
93
+ });
@@ -35,23 +35,27 @@ const NESTED = /(?:^|\/)biome\.jsonc?$/u;
35
35
 
36
36
  const RELATIVE = /^\.\.?(?:\/|$)/u;
37
37
 
38
- const directoryOf = (path: string): string => (posix.dirname(path) === "." ? "" : posix.dirname(path));
38
+ function directoryOf(path: string): string {
39
+ return posix.dirname(path) === "." ? "" : posix.dirname(path);
40
+ }
39
41
 
40
- const read = async (readText: ReadText, path: string): Promise<Parsed> => {
42
+ async function read(readText: ReadText, path: string): Promise<Parsed> {
41
43
  const text = await readText(path);
42
44
  return text === undefined ? { _tag: "Unreadable", reason: "the file does not exist" } : parseJsonc(path, text);
43
- };
45
+ }
44
46
 
45
- const problem = (file: string, message: string, line?: number): Loaded => ({ layers: [], problems: [{ file, line, message }] });
47
+ function problem(file: string, message: string, line?: number): Loaded {
48
+ return { layers: [], problems: [{ file, line, message }] };
49
+ }
46
50
 
47
- const local = async (readText: ReadText, path: string): Promise<Loaded> => {
51
+ async function local(readText: ReadText, path: string): Promise<Loaded> {
48
52
  const parsed = await read(readText, path);
49
53
  return parsed._tag === "Parsed"
50
54
  ? { layers: [{ json: parsed.json, path, preset: undefined }], problems: [] }
51
55
  : problem(path, `Cannot read this Biome config: ${parsed.reason}.`);
52
- };
56
+ }
53
57
 
54
- const fromPackage = async (readText: ReadText, from: string, entry: Json, specifier: string): Promise<Loaded> => {
58
+ async function fromPackage(readText: ReadText, from: string, entry: Json, specifier: string): Promise<Loaded> {
55
59
  const target = await resolvePackage(readText, specifier);
56
60
  if (target === undefined) {
57
61
  return problem(from, `Cannot resolve the Biome config "${specifier}" from node_modules.`, entry.line);
@@ -67,9 +71,9 @@ const fromPackage = async (readText: ReadText, from: string, entry: Json, specif
67
71
  }
68
72
  const preset = { declared: declared.value, file: from, line: entry.line, specifier };
69
73
  return { layers: [{ json: parsed.json, path: target, preset }], problems: [] };
70
- };
74
+ }
71
75
 
72
- const extended = async (readText: ReadText, from: string, entry: Json): Promise<Loaded> => {
76
+ async function extended(readText: ReadText, from: string, entry: Json): Promise<Loaded> {
73
77
  const specifier = textOf(entry);
74
78
  if (specifier === undefined || specifier === "//") {
75
79
  return { layers: [], problems: [] };
@@ -79,11 +83,13 @@ const extended = async (readText: ReadText, from: string, entry: Json): Promise<
79
83
  }
80
84
  const relative = posix.normalize(specifier);
81
85
  return (await readText(relative)) === undefined ? fromPackage(readText, from, entry, specifier) : local(readText, relative);
82
- };
86
+ }
83
87
 
84
- const extendsOf = (json: Json): readonly Json[] => itemsOf(member(json, "extends"));
88
+ function extendsOf(json: Json): readonly Json[] {
89
+ return itemsOf(member(json, "extends"));
90
+ }
85
91
 
86
- const chainOf = async (readText: ReadText, path: string): Promise<Loaded> => {
92
+ async function chainOf(readText: ReadText, path: string): Promise<Loaded> {
87
93
  const own = await local(readText, path);
88
94
  const [layer] = own.layers;
89
95
  if (layer === undefined) {
@@ -91,12 +97,12 @@ const chainOf = async (readText: ReadText, path: string): Promise<Loaded> => {
91
97
  }
92
98
  const parents = await Promise.all(extendsOf(layer.json).map((entry) => extended(readText, path, entry)));
93
99
  return { layers: [...parents.flatMap((parent) => parent.layers), layer], problems: parents.flatMap((parent) => parent.problems) };
94
- };
100
+ }
95
101
 
96
- const rootConfig = async (readText: ReadText): Promise<string | undefined> => {
102
+ async function rootConfig(readText: ReadText): Promise<string | undefined> {
97
103
  const present = await Promise.all(ROOT_CONFIGS.map(async (path) => ((await readText(path)) === undefined ? [] : [path])));
98
104
  return present.flat()[0];
99
- };
105
+ }
100
106
 
101
107
  export interface BiomeConfigs {
102
108
  readonly chains: readonly Chain[];
@@ -104,7 +110,7 @@ export interface BiomeConfigs {
104
110
  readonly root: string;
105
111
  }
106
112
 
107
- export const biomeConfigs = async ({ files, readText }: RuleInputs): Promise<BiomeConfigs> => {
113
+ export async function biomeConfigs({ files, readText }: RuleInputs): Promise<BiomeConfigs> {
108
114
  const root = await rootConfig(readText);
109
115
  const rootChain = root === undefined ? { layers: [], problems: [] } : await chainOf(readText, root);
110
116
  const loaded = new Set(rootChain.layers.map((layer) => layer.path));
@@ -116,4 +122,4 @@ export const biomeConfigs = async ({ files, readText }: RuleInputs): Promise<Bio
116
122
  problems: all.flatMap((chain) => chain.problems),
117
123
  root: root ?? "biome.json",
118
124
  };
119
- };
125
+ }
@@ -15,23 +15,28 @@ const shipped = Schema.toStandardSchemaV1(Schema.fromJsonString(Schema.Array(Dec
15
15
  parseOptions: { errors: "all", onExcessProperty: "error" },
16
16
  });
17
17
 
18
- export const decodeShipped = (text: string | undefined): Promise<Decoded<readonly Declaration[]>> =>
19
- text === undefined ? Promise.resolve({ _tag: "Valid", value: [] }) : decodeWith(shipped, text);
18
+ export function decodeShipped(text: string | undefined): Promise<Decoded<readonly Declaration[]>> {
19
+ return text === undefined ? Promise.resolve({ _tag: "Valid", value: [] }) : decodeWith(shipped, text);
20
+ }
20
21
 
21
22
  export interface Scoped {
22
23
  readonly includes: readonly string[];
23
24
  readonly rule: string;
24
25
  }
25
26
 
26
- const sameScope = (left: readonly string[], right: readonly string[]): boolean =>
27
- new Set(left).size === new Set(right).size && left.every((glob) => right.includes(glob));
27
+ function sameScope(left: readonly string[], right: readonly string[]): boolean {
28
+ return new Set(left).size === new Set(right).size && left.every((glob) => right.includes(glob));
29
+ }
28
30
 
29
- export const matches = (declaration: Scoped, weakening: Scoped): boolean =>
30
- declaration.rule === weakening.rule && sameScope(declaration.includes, weakening.includes);
31
+ export function matches(declaration: Scoped, weakening: Scoped): boolean {
32
+ return declaration.rule === weakening.rule && sameScope(declaration.includes, weakening.includes);
33
+ }
31
34
 
32
- export const scoped = (base: string, glob: string): string => {
33
- const [, negation = "", path = glob] = /^(!*)(.*)$/u.exec(glob) ?? [];
35
+ export function scoped(base: string, glob: string): string {
36
+ const { negation = "", path = glob } = /^(?<negation>!*)(?<path>.*)$/u.exec(glob)?.groups ?? {};
34
37
  return base === "" ? glob : `${negation}${base}/${path}`;
35
- };
38
+ }
36
39
 
37
- export const scopeText = (includes: readonly string[]): string => includes.map((glob) => `"${glob}"`).join(", ");
40
+ export function scopeText(includes: readonly string[]): string {
41
+ return includes.map((glob) => `"${glob}"`).join(", ");
42
+ }
@@ -12,14 +12,17 @@ const LITERALS: ReadonlyMap<ts.SyntaxKind, boolean> = new Map([
12
12
  [ts.SyntaxKind.FalseKeyword, false],
13
13
  ]);
14
14
 
15
- const scalarOf = (node: ts.Expression): string | boolean | undefined => (ts.isStringLiteral(node) ? node.text : LITERALS.get(node.kind));
15
+ function scalarOf(node: ts.Expression): string | boolean | undefined {
16
+ return ts.isStringLiteral(node) ? node.text : LITERALS.get(node.kind);
17
+ }
16
18
 
17
- const entryOf = (source: ts.JsonSourceFile, property: ts.ObjectLiteralElementLike): ReadonlyArray<readonly [string, Json]> =>
18
- ts.isPropertyAssignment(property) && (ts.isStringLiteral(property.name) || ts.isIdentifier(property.name))
19
+ function entryOf(source: ts.JsonSourceFile, property: ts.ObjectLiteralElementLike): ReadonlyArray<readonly [string, Json]> {
20
+ return ts.isPropertyAssignment(property) && (ts.isStringLiteral(property.name) || ts.isIdentifier(property.name))
19
21
  ? [[property.name.text, toJson(source, property.initializer)]]
20
22
  : [];
23
+ }
21
24
 
22
- const toJson = (source: ts.JsonSourceFile, node: ts.Expression): Json => {
25
+ function toJson(source: ts.JsonSourceFile, node: ts.Expression): Json {
23
26
  const line = source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1;
24
27
  if (ts.isObjectLiteralExpression(node)) {
25
28
  return { _tag: "Object", entries: new Map(node.properties.flatMap((property) => entryOf(source, property))), line };
@@ -28,9 +31,9 @@ const toJson = (source: ts.JsonSourceFile, node: ts.Expression): Json => {
28
31
  return { _tag: "Array", items: node.elements.map((element) => toJson(source, element)), line };
29
32
  }
30
33
  return { _tag: "Value", line, value: scalarOf(node) };
31
- };
34
+ }
32
35
 
33
- export const parseJsonc = (path: string, text: string): Parsed => {
36
+ export function parseJsonc(path: string, text: string): Parsed {
34
37
  const error = ts.parseConfigFileTextToJson(path, text).error;
35
38
  const source = ts.parseJsonText(path, text);
36
39
  const root = source.statements[0]?.expression;
@@ -41,15 +44,24 @@ export const parseJsonc = (path: string, text: string): Parsed => {
41
44
  };
42
45
  }
43
46
  return { _tag: "Parsed", json: toJson(source, root) };
44
- };
47
+ }
45
48
 
46
- export const member = (node: Json | undefined, key: string): Json | undefined => (node?._tag === "Object" ? node.entries.get(key) : undefined);
49
+ export function member(node: Json | undefined, key: string): Json | undefined {
50
+ return node?._tag === "Object" ? node.entries.get(key) : undefined;
51
+ }
47
52
 
48
- export const entriesOf = (node: Json | undefined): ReadonlyArray<readonly [string, Json]> => (node?._tag === "Object" ? [...node.entries] : []);
53
+ export function entriesOf(node: Json | undefined): ReadonlyArray<readonly [string, Json]> {
54
+ return node?._tag === "Object" ? [...node.entries] : [];
55
+ }
49
56
 
50
- export const itemsOf = (node: Json | undefined): readonly Json[] => (node?._tag === "Array" ? node.items : []);
57
+ export function itemsOf(node: Json | undefined): readonly Json[] {
58
+ return node?._tag === "Array" ? node.items : [];
59
+ }
51
60
 
52
- export const textOf = (node: Json | undefined): string | undefined =>
53
- node?._tag === "Value" && typeof node.value === "string" ? node.value : undefined;
61
+ export function textOf(node: Json | undefined): string | undefined {
62
+ return node?._tag === "Value" && typeof node.value === "string" ? node.value : undefined;
63
+ }
54
64
 
55
- export const isFalse = (node: Json | undefined): boolean => node?._tag === "Value" && node.value === false;
65
+ export function isFalse(node: Json | undefined): boolean {
66
+ return node?._tag === "Value" && node.value === false;
67
+ }
@@ -15,11 +15,13 @@ const manifestSchema = Schema.toStandardSchemaV1(Schema.fromJsonString(Manifest)
15
15
 
16
16
  const CONDITIONS: ReadonlySet<string> = new Set(["biome", "default"]);
17
17
 
18
- const SPECIFIER = /^((?:@[^/]+\/)?[^/]+)(?:\/(.+))?$/u;
18
+ const SPECIFIER = /^(?<name>(?:@[^/]+\/)?[^/]+)(?:\/(?<subpath>.+))?$/u;
19
19
 
20
- const isRecord = (value: unknown): value is Readonly<Record<string, unknown>> => typeof value === "object" && value !== null && !Array.isArray(value);
20
+ function isRecord(value: unknown): value is Readonly<Record<string, unknown>> {
21
+ return typeof value === "object" && value !== null && !Array.isArray(value);
22
+ }
21
23
 
22
- const targetOf = (value: unknown, star: string | undefined): string | undefined => {
24
+ function targetOf(value: unknown, star: string | undefined): string | undefined {
23
25
  if (typeof value === "string") {
24
26
  return star === undefined ? value : value.replaceAll("*", star);
25
27
  }
@@ -28,11 +30,13 @@ const targetOf = (value: unknown, star: string | undefined): string | undefined
28
30
  }
29
31
  const condition = isRecord(value) ? Object.keys(value).find((key) => CONDITIONS.has(key)) : undefined;
30
32
  return isRecord(value) && condition !== undefined ? targetOf(value[condition], star) : undefined;
31
- };
33
+ }
32
34
 
33
- const bySpecificity = (left: string, right: string): number => right.indexOf("*") - left.indexOf("*") || right.length - left.length;
35
+ function bySpecificity(left: string, right: string): number {
36
+ return right.indexOf("*") - left.indexOf("*") || right.length - left.length;
37
+ }
34
38
 
35
- const patternTarget = (exports: Readonly<Record<string, unknown>>, key: string): string | undefined => {
39
+ function patternTarget(exports: Readonly<Record<string, unknown>>, key: string): string | undefined {
36
40
  const pattern = Object.keys(exports)
37
41
  .filter((candidate) => candidate.split("*").length === 2)
38
42
  .sort(bySpecificity)
@@ -42,26 +46,27 @@ const patternTarget = (exports: Readonly<Record<string, unknown>>, key: string):
42
46
  });
43
47
  const [prefix = "", suffix = ""] = pattern?.split("*") ?? [];
44
48
  return pattern === undefined ? undefined : targetOf(exports[pattern], key.slice(prefix.length, key.length - suffix.length));
45
- };
49
+ }
46
50
 
47
- const exported = (exports: unknown, subpath: string): string | undefined => {
51
+ function exported(exports: unknown, subpath: string): string | undefined {
48
52
  const key = subpath === "" ? "." : `./${subpath}`;
49
53
  if (!isRecord(exports)) {
50
54
  return targetOf(exports, undefined);
51
55
  }
52
56
  return Object.hasOwn(exports, key) ? targetOf(exports[key], undefined) : (patternTarget(exports, key) ?? targetOf(exports, undefined));
53
- };
57
+ }
54
58
 
55
- const inPackage = (directory: string, target: string | undefined): string | undefined =>
56
- target?.startsWith("./") === true ? posix.join(directory, target) : undefined;
59
+ function inPackage(directory: string, target: string | undefined): string | undefined {
60
+ return target?.startsWith("./") === true ? posix.join(directory, target) : undefined;
61
+ }
57
62
 
58
- const manifestAt = async (readText: RuleInputs["readText"], path: string): Promise<Manifest | undefined> => {
63
+ async function manifestAt(readText: RuleInputs["readText"], path: string): Promise<Manifest | undefined> {
59
64
  const text = await readText(path);
60
65
  const decoded = text === undefined ? undefined : await decodeWith(manifestSchema, text);
61
66
  return decoded?._tag === "Valid" ? decoded.value : undefined;
62
- };
67
+ }
63
68
 
64
- const fromDependency = (directory: string, manifest: Manifest, subpath: string): string | undefined => {
69
+ function fromDependency(directory: string, manifest: Manifest, subpath: string): string | undefined {
65
70
  if (manifest.exports !== undefined) {
66
71
  return inPackage(directory, exported(manifest.exports, subpath));
67
72
  }
@@ -69,10 +74,10 @@ const fromDependency = (directory: string, manifest: Manifest, subpath: string):
69
74
  return posix.join(directory, manifest.main);
70
75
  }
71
76
  return subpath === "" ? undefined : posix.join(directory, subpath);
72
- };
77
+ }
73
78
 
74
- export const resolvePackage = async (readText: RuleInputs["readText"], specifier: string): Promise<string | undefined> => {
75
- const [, name, subpath = ""] = SPECIFIER.exec(specifier) ?? [];
79
+ export async function resolvePackage(readText: RuleInputs["readText"], specifier: string): Promise<string | undefined> {
80
+ const { name, subpath = "" } = SPECIFIER.exec(specifier)?.groups ?? {};
76
81
  if (name === undefined) {
77
82
  return undefined;
78
83
  }
@@ -83,4 +88,4 @@ export const resolvePackage = async (readText: RuleInputs["readText"], specifier
83
88
  const directory = posix.join("node_modules", name);
84
89
  const manifest = await manifestAt(readText, posix.join(directory, "package.json"));
85
90
  return manifest === undefined ? undefined : fromDependency(directory, manifest, subpath);
86
- };
91
+ }
@@ -20,29 +20,36 @@ const LINT_WEAK = new Set(["off", "warn", "info"]);
20
20
  const ASSIST_WEAK = new Set(["off"]);
21
21
  const PRESETS = new Set(["recommended", "preset"]);
22
22
 
23
- const levelOf = (node: Json): string | undefined => textOf(node) ?? textOf(member(node, "level"));
23
+ function levelOf(node: Json): string | undefined {
24
+ return textOf(node) ?? textOf(member(node, "level"));
25
+ }
24
26
 
25
- const at = (rule: string, node: Json | undefined, weak: boolean, wholeGroup = false): readonly Setting[] =>
26
- node === undefined ? [] : [{ line: node.line, rule, weak, wholeGroup }];
27
+ function at(rule: string, node: Json | undefined, weak: boolean, wholeGroup = false): readonly Setting[] {
28
+ return node === undefined ? [] : [{ line: node.line, rule, weak, wholeGroup }];
29
+ }
27
30
 
28
- const enabled = (rule: string, section: Json | undefined): readonly Setting[] =>
29
- at(rule, member(section, "enabled"), isFalse(member(section, "enabled")));
31
+ function enabled(rule: string, section: Json | undefined): readonly Setting[] {
32
+ return at(rule, member(section, "enabled"), isFalse(member(section, "enabled")));
33
+ }
30
34
 
31
- const defaults = (rule: string, set: Json | undefined): readonly Setting[] => [
32
- ...at(rule, member(set, "recommended"), isFalse(member(set, "recommended"))),
33
- ...at(rule, member(set, "preset"), textOf(member(set, "preset")) === "none"),
34
- ];
35
+ function defaults(rule: string, set: Json | undefined): readonly Setting[] {
36
+ return [
37
+ ...at(rule, member(set, "recommended"), isFalse(member(set, "recommended"))),
38
+ ...at(rule, member(set, "preset"), textOf(member(set, "preset")) === "none"),
39
+ ];
40
+ }
35
41
 
36
- const ruleSettings = (group: string, weak: ReadonlySet<string>, rules: Json): readonly Setting[] =>
37
- entriesOf(rules)
42
+ function ruleSettings(group: string, weak: ReadonlySet<string>, rules: Json): readonly Setting[] {
43
+ return entriesOf(rules)
38
44
  .filter(([rule]) => !PRESETS.has(rule))
39
45
  .flatMap(([rule, setting]) => {
40
46
  const level = levelOf(setting);
41
47
  return level === undefined ? [] : at(`${group}/${rule}`, setting, weak.has(level));
42
48
  });
49
+ }
43
50
 
44
- const groupSettings = (prefix: string, weak: ReadonlySet<string>, groups: Json | undefined): readonly Setting[] =>
45
- entriesOf(groups)
51
+ function groupSettings(prefix: string, weak: ReadonlySet<string>, groups: Json | undefined): readonly Setting[] {
52
+ return entriesOf(groups)
46
53
  .filter(([group]) => !PRESETS.has(group))
47
54
  .flatMap(([group, rules]) => {
48
55
  const severity = textOf(rules);
@@ -50,19 +57,24 @@ const groupSettings = (prefix: string, weak: ReadonlySet<string>, groups: Json |
50
57
  ? [...defaults(`${prefix}/${group}`, rules), ...ruleSettings(`${prefix}/${group}`, weak, rules)]
51
58
  : at(`${prefix}/${group}`, rules, weak.has(severity), true);
52
59
  });
60
+ }
53
61
 
54
- const linter = (section: Json | undefined): readonly Setting[] => [
55
- ...enabled("lint", section),
56
- ...defaults("lint/recommended", member(section, "rules")),
57
- ...groupSettings("lint", LINT_WEAK, member(section, "rules")),
58
- ...entriesOf(member(section, "domains")).flatMap(([domain, value]) => at(`lint/domains/${domain}`, value, textOf(value) === "none")),
59
- ];
62
+ function linter(section: Json | undefined): readonly Setting[] {
63
+ return [
64
+ ...enabled("lint", section),
65
+ ...defaults("lint/recommended", member(section, "rules")),
66
+ ...groupSettings("lint", LINT_WEAK, member(section, "rules")),
67
+ ...entriesOf(member(section, "domains")).flatMap(([domain, value]) => at(`lint/domains/${domain}`, value, textOf(value) === "none")),
68
+ ];
69
+ }
60
70
 
61
- const assist = (section: Json | undefined): readonly Setting[] => [
62
- ...enabled("assist", section),
63
- ...defaults("assist/recommended", member(section, "actions")),
64
- ...groupSettings("assist", ASSIST_WEAK, member(section, "actions")),
65
- ];
71
+ function assist(section: Json | undefined): readonly Setting[] {
72
+ return [
73
+ ...enabled("assist", section),
74
+ ...defaults("assist/recommended", member(section, "actions")),
75
+ ...groupSettings("assist", ASSIST_WEAK, member(section, "actions")),
76
+ ];
77
+ }
66
78
 
67
79
  const LANGUAGES: readonly string[] = ["javascript", "json", "css", "graphql", "grit", "html"];
68
80
 
@@ -74,33 +86,39 @@ const TOOLS: ReadonlyArray<readonly [string, string]> = [
74
86
 
75
87
  const SCOPED_SECTIONS: ReadonlyArray<readonly [string, string]> = [["files", "files"], ...TOOLS];
76
88
 
77
- const settingsOf = (scope: Json): readonly Setting[] => [
78
- ...linter(member(scope, "linter")),
79
- ...assist(member(scope, "assist")),
80
- ...enabled("format", member(scope, "formatter")),
81
- ...LANGUAGES.flatMap((language) => TOOLS.flatMap(([key, name]) => enabled(`${language}/${name}`, member(member(scope, language), key)))),
82
- ];
89
+ function settingsOf(scope: Json): readonly Setting[] {
90
+ return [
91
+ ...linter(member(scope, "linter")),
92
+ ...assist(member(scope, "assist")),
93
+ ...enabled("format", member(scope, "formatter")),
94
+ ...LANGUAGES.flatMap((language) => TOOLS.flatMap(([key, name]) => enabled(`${language}/${name}`, member(member(scope, language), key)))),
95
+ ];
96
+ }
83
97
 
84
- const replaces = (later: Setting, earlier: Setting): boolean =>
85
- later.rule === earlier.rule || (later.wholeGroup && earlier.rule.startsWith(`${later.rule}/`));
98
+ function replaces(later: Setting, earlier: Setting): boolean {
99
+ return later.rule === earlier.rule || (later.wholeGroup && earlier.rule.startsWith(`${later.rule}/`));
100
+ }
86
101
 
87
- const includesOf = (override: Json): readonly string[] => {
102
+ function includesOf(override: Json): readonly string[] {
88
103
  const includes = itemsOf(member(override, "includes")).flatMap((item) => textOf(item) ?? []);
89
104
  return includes.length === 0 ? ["**"] : includes;
90
- };
105
+ }
91
106
 
92
- const narrowsScope = (globs: readonly string[]): boolean => globs.length > 0 && (globs.some((glob) => glob.startsWith("!")) || !globs.includes("**"));
107
+ function narrowsScope(globs: readonly string[]): boolean {
108
+ return globs.length > 0 && (globs.some((glob) => glob.startsWith("!")) || !globs.includes("**"));
109
+ }
93
110
 
94
- const narrowings = (config: Json, base: string): readonly Omit<Weakening, "layer">[] =>
95
- SCOPED_SECTIONS.flatMap(([key, name]) => {
111
+ function narrowings(config: Json, base: string): readonly Omit<Weakening, "layer">[] {
112
+ return SCOPED_SECTIONS.flatMap(([key, name]) => {
96
113
  const list = member(member(config, key), "includes");
97
114
  const globs = itemsOf(list).flatMap((item) => textOf(item) ?? []);
98
115
  return list === undefined || !narrowsScope(globs)
99
116
  ? []
100
117
  : [{ includes: globs.map((glob) => scoped(base, glob)), line: list.line, rule: `${name}/includes` }];
101
118
  });
119
+ }
102
120
 
103
- const layerWeakenings = (layer: Layer, later: readonly Layer[], base: string): readonly Weakening[] => {
121
+ function layerWeakenings(layer: Layer, later: readonly Layer[], base: string): readonly Weakening[] {
104
122
  const overriding = later.flatMap((next) => settingsOf(next.json));
105
123
  const top = settingsOf(layer.json).filter((setting) => setting.weak && !overriding.some((next) => replaces(next, setting)));
106
124
  return [
@@ -112,7 +130,8 @@ const layerWeakenings = (layer: Layer, later: readonly Layer[], base: string): r
112
130
  .map(({ line, rule }) => ({ includes: includesOf(override).map((glob) => scoped(base, glob)), line, rule })),
113
131
  ),
114
132
  ].map((weakening) => ({ ...weakening, layer }));
115
- };
133
+ }
116
134
 
117
- export const weakeningsOf = ({ base, layers }: Chain): readonly Weakening[] =>
118
- layers.flatMap((layer, index) => layerWeakenings(layer, layers.slice(index + 1), base));
135
+ export function weakeningsOf({ base, layers }: Chain): readonly Weakening[] {
136
+ return layers.flatMap((layer, index) => layerWeakenings(layer, layers.slice(index + 1), base));
137
+ }
@@ -14,15 +14,16 @@ interface Shipped extends Scoped {
14
14
  readonly specifier: string;
15
15
  }
16
16
 
17
- const shippedBy = ({ base, layers }: Chain): readonly Shipped[] =>
18
- layers.flatMap(({ preset }) =>
17
+ function shippedBy({ base, layers }: Chain): readonly Shipped[] {
18
+ return layers.flatMap(({ preset }) =>
19
19
  preset === undefined
20
20
  ? []
21
21
  : preset.declared.map(({ includes, rule }) => ({ includes: includes.map((glob) => scoped(base, glob)), rule, specifier: preset.specifier })),
22
22
  );
23
+ }
23
24
 
24
- const undeclared = ({ includes, layer, line, rule }: Weakening, configFile: string): Finding =>
25
- layer.preset === undefined
25
+ function undeclared({ includes, layer, line, rule }: Weakening, configFile: string): Finding {
26
+ return layer.preset === undefined
26
27
  ? {
27
28
  file: layer.path,
28
29
  line,
@@ -35,26 +36,33 @@ const undeclared = ({ includes, layer, line, rule }: Weakening, configFile: stri
35
36
  message: `Extends "${layer.preset.specifier}", which weakens "${rule}" for ${scopeText(includes)} without a declaration. Repair the code and restore the check in the preset.`,
36
37
  subject: rule,
37
38
  };
39
+ }
38
40
 
39
- const unused = (file: string, declaration: Scoped): Finding => ({
40
- file,
41
- message: `Declares "${declaration.rule}" for ${scopeText(declaration.includes)}, which no Biome config weakens. Remove the declaration.`,
42
- subject: declaration.rule,
43
- });
41
+ function unused(file: string, declaration: Scoped): Finding {
42
+ return {
43
+ file,
44
+ message: `Declares "${declaration.rule}" for ${scopeText(declaration.includes)}, which no Biome config weakens. Remove the declaration.`,
45
+ subject: declaration.rule,
46
+ };
47
+ }
44
48
 
45
- const repeated = (file: string, declaration: Scoped, shipped: Shipped): Finding => ({
46
- file,
47
- message: `Declares "${declaration.rule}" for ${scopeText(declaration.includes)}, which "${shipped.specifier}" already declares. Remove the declaration.`,
48
- subject: declaration.rule,
49
- });
49
+ function repeated(file: string, declaration: Scoped, shipped: Shipped): Finding {
50
+ return {
51
+ file,
52
+ message: `Declares "${declaration.rule}" for ${scopeText(declaration.includes)}, which "${shipped.specifier}" already declares. Remove the declaration.`,
53
+ subject: declaration.rule,
54
+ };
55
+ }
50
56
 
51
- const staleOrRepeated = (file: string, weakenings: readonly Weakening[], shipped: readonly Shipped[]) => (declaration: Scoped) => {
52
- if (!weakenings.some((weakening) => matches(declaration, weakening))) {
53
- return [unused(file, declaration)];
54
- }
55
- const already = shipped.find((candidate) => matches(declaration, candidate));
56
- return already === undefined ? [] : [repeated(file, declaration, already)];
57
- };
57
+ function staleOrRepeated(file: string, weakenings: readonly Weakening[], shipped: readonly Shipped[]) {
58
+ return (declaration: Scoped) => {
59
+ if (!weakenings.some((weakening) => matches(declaration, weakening))) {
60
+ return [unused(file, declaration)];
61
+ }
62
+ const already = shipped.find((candidate) => matches(declaration, candidate));
63
+ return already === undefined ? [] : [repeated(file, declaration, already)];
64
+ };
65
+ }
58
66
 
59
67
  export const biomeOverrides = defineRule({
60
68
  check: async (inputs) => {
@@ -62,7 +70,9 @@ export const biomeOverrides = defineRule({
62
70
  const { declared } = inputs.options;
63
71
  const weakenings = chains.flatMap(weakeningsOf);
64
72
  const shipped = chains.flatMap(shippedBy);
65
- const covered = (weakening: Weakening): boolean => [...declared, ...shipped].some((declaration) => matches(declaration, weakening));
73
+ function covered(weakening: Weakening): boolean {
74
+ return [...declared, ...shipped].some((declaration) => matches(declaration, weakening));
75
+ }
66
76
  return [
67
77
  ...problems,
68
78
  ...(await missingBiomeFiles(inputs)),
@@ -15,11 +15,12 @@ const INLINE_ESLINT_CONFIG = /^eslint\s+["']?[\w@/-]+["']?\s*:/u;
15
15
 
16
16
  const LEADER = /^[\s/*]+/u;
17
17
 
18
- const directiveOn = (comment: SourceComment, line: string): string | undefined => {
18
+ function directiveOn(comment: SourceComment, line: string): string | undefined {
19
19
  const content = line.replace(LEADER, "");
20
20
  const patterns = comment.kind === "block" ? [...SUPPRESSIONS, INLINE_ESLINT_CONFIG] : SUPPRESSIONS;
21
21
  return patterns.map((pattern) => pattern.exec(content)?.[0].replace(/\s*:$/u, "")).find((match) => match !== undefined);
22
- };
22
+ }
23
23
 
24
- export const suppressionIn = (comment: SourceComment): string | undefined =>
25
- comment.body.map((line) => directiveOn(comment, line)).find((directive) => directive !== undefined);
24
+ export function suppressionIn(comment: SourceComment): string | undefined {
25
+ return comment.body.map((line) => directiveOn(comment, line)).find((directive) => directive !== undefined);
26
+ }