@shivaedev/quality 0.23.0 → 0.25.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 (411) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +6 -2
  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 +6 -4
  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 +27 -21
  22. package/dist/baseline/update.js.map +1 -1
  23. package/dist/cli/args.d.ts +2 -4
  24. package/dist/cli/args.d.ts.map +1 -1
  25. package/dist/cli/args.js +19 -18
  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 +35 -41
  34. package/dist/cli/fix.js.map +1 -1
  35. package/dist/cli/lint.d.ts +1 -2
  36. package/dist/cli/lint.d.ts.map +1 -1
  37. package/dist/cli/lint.js +1 -2
  38. package/dist/cli/lint.js.map +1 -1
  39. package/dist/cli/messages.d.ts +1 -1
  40. package/dist/cli/messages.d.ts.map +1 -1
  41. package/dist/cli/messages.js +2 -2
  42. package/dist/cli/migrate.d.ts.map +1 -1
  43. package/dist/cli/migrate.js +2 -2
  44. package/dist/cli/migrate.js.map +1 -1
  45. package/dist/cli/migrateExceptions.js +1 -1
  46. package/dist/cli/migrateExceptions.js.map +1 -1
  47. package/dist/cli/program.js +1 -1
  48. package/dist/cli/program.js.map +1 -1
  49. package/dist/cli/{run-main.d.ts → runMain.d.ts} +1 -1
  50. package/dist/cli/runMain.d.ts.map +1 -0
  51. package/dist/cli/{run-main.js → runMain.js} +3 -3
  52. package/dist/cli/runMain.js.map +1 -0
  53. package/dist/cli/shrink.d.ts +2 -2
  54. package/dist/cli/shrink.d.ts.map +1 -1
  55. package/dist/cli/shrink.js +9 -3
  56. package/dist/cli/shrink.js.map +1 -1
  57. package/dist/cli.js +1 -1
  58. package/dist/cli.js.map +1 -1
  59. package/dist/config/decode.d.ts +1 -1
  60. package/dist/config/decode.d.ts.map +1 -1
  61. package/dist/config/decode.js +13 -9
  62. package/dist/config/decode.js.map +1 -1
  63. package/dist/config/load.d.ts.map +1 -1
  64. package/dist/config/load.js +26 -20
  65. package/dist/config/load.js.map +1 -1
  66. package/dist/config/resolve.d.ts +23 -3
  67. package/dist/config/resolve.d.ts.map +1 -1
  68. package/dist/config/resolve.js +19 -11
  69. package/dist/config/resolve.js.map +1 -1
  70. package/dist/config.d.ts +3 -3
  71. package/dist/config.d.ts.map +1 -1
  72. package/dist/config.js +3 -1
  73. package/dist/config.js.map +1 -1
  74. package/dist/decoded.d.ts +3 -3
  75. package/dist/decoded.d.ts.map +1 -1
  76. package/dist/decoded.js +8 -6
  77. package/dist/decoded.js.map +1 -1
  78. package/dist/engine/{baseline-file.d.ts → baselineFile.d.ts} +2 -2
  79. package/dist/engine/baselineFile.d.ts.map +1 -0
  80. package/dist/engine/{baseline-file.js → baselineFile.js} +5 -3
  81. package/dist/engine/baselineFile.js.map +1 -0
  82. package/dist/engine/evaluate.d.ts +2 -3
  83. package/dist/engine/evaluate.d.ts.map +1 -1
  84. package/dist/engine/evaluate.js +6 -5
  85. package/dist/engine/evaluate.js.map +1 -1
  86. package/dist/engine/{run-rules.d.ts → runRules.d.ts} +2 -2
  87. package/dist/engine/runRules.d.ts.map +1 -0
  88. package/dist/engine/runRules.js +44 -0
  89. package/dist/engine/runRules.js.map +1 -0
  90. package/dist/engine/session.d.ts +1 -1
  91. package/dist/engine/session.d.ts.map +1 -1
  92. package/dist/engine/session.js +10 -6
  93. package/dist/engine/session.js.map +1 -1
  94. package/dist/engine/violation.d.ts +8 -8
  95. package/dist/engine/violation.d.ts.map +1 -1
  96. package/dist/engine/violation.js +23 -11
  97. package/dist/engine/violation.js.map +1 -1
  98. package/dist/exceptions/registry.d.ts +2 -2
  99. package/dist/exceptions/registry.d.ts.map +1 -1
  100. package/dist/exceptions/registry.js +10 -6
  101. package/dist/exceptions/registry.js.map +1 -1
  102. package/dist/git/base.d.ts +1 -2
  103. package/dist/git/base.d.ts.map +1 -1
  104. package/dist/git/base.js +22 -10
  105. package/dist/git/base.js.map +1 -1
  106. package/dist/git/command.d.ts +2 -2
  107. package/dist/git/command.d.ts.map +1 -1
  108. package/dist/git/command.js +13 -9
  109. package/dist/git/command.js.map +1 -1
  110. package/dist/git/history.d.ts +1 -1
  111. package/dist/git/history.d.ts.map +1 -1
  112. package/dist/git/history.js +5 -3
  113. package/dist/git/history.js.map +1 -1
  114. package/dist/hooks/preCommit.js +1 -1
  115. package/dist/hooks/preCommit.js.map +1 -1
  116. package/dist/imports/generated.js +1 -1
  117. package/dist/imports/generated.js.map +1 -1
  118. package/dist/imports/{pnpm-workspace.d.ts → pnpmWorkspacePatterns.d.ts} +1 -1
  119. package/dist/imports/pnpmWorkspacePatterns.d.ts.map +1 -0
  120. package/dist/imports/{pnpm-workspace.js → pnpmWorkspacePatterns.js} +1 -1
  121. package/dist/imports/pnpmWorkspacePatterns.js.map +1 -0
  122. package/dist/imports/resolve.js +1 -1
  123. package/dist/imports/resolve.js.map +1 -1
  124. package/dist/imports/workspace.js +1 -1
  125. package/dist/imports/workspace.js.map +1 -1
  126. package/dist/inventory/collect.d.ts +1 -1
  127. package/dist/inventory/collect.d.ts.map +1 -1
  128. package/dist/inventory/collect.js +8 -6
  129. package/dist/inventory/collect.js.map +1 -1
  130. package/dist/inventory/filesystem.d.ts +1 -1
  131. package/dist/inventory/filesystem.d.ts.map +1 -1
  132. package/dist/inventory/filesystem.js +11 -7
  133. package/dist/inventory/filesystem.js.map +1 -1
  134. package/dist/inventory/ignoreScope.d.ts +17 -0
  135. package/dist/inventory/ignoreScope.d.ts.map +1 -0
  136. package/dist/inventory/{ignore-scope.js → ignoreScope.js} +19 -10
  137. package/dist/inventory/ignoreScope.js.map +1 -0
  138. package/dist/inventory/walk.d.ts +1 -1
  139. package/dist/inventory/walk.d.ts.map +1 -1
  140. package/dist/inventory/walk.js +1 -1
  141. package/dist/inventory/walk.js.map +1 -1
  142. package/dist/manifests/sorted.d.ts +1 -1
  143. package/dist/manifests/sorted.d.ts.map +1 -1
  144. package/dist/manifests/sorted.js +15 -17
  145. package/dist/manifests/sorted.js.map +1 -1
  146. package/dist/report/plural.d.ts +1 -1
  147. package/dist/report/plural.d.ts.map +1 -1
  148. package/dist/report/plural.js +3 -1
  149. package/dist/report/plural.js.map +1 -1
  150. package/dist/report/render.d.ts +1 -3
  151. package/dist/report/render.d.ts.map +1 -1
  152. package/dist/report/render.js +30 -32
  153. package/dist/report/render.js.map +1 -1
  154. package/dist/report/stale.d.ts +1 -1
  155. package/dist/report/stale.d.ts.map +1 -1
  156. package/dist/report/stale.js +18 -8
  157. package/dist/report/stale.js.map +1 -1
  158. package/dist/rule.d.ts +14 -14
  159. package/dist/rule.d.ts.map +1 -1
  160. package/dist/rule.js +11 -9
  161. package/dist/rule.js.map +1 -1
  162. package/dist/rules/{built-in.d.ts → builtIn.d.ts} +3 -3
  163. package/dist/rules/builtIn.d.ts.map +1 -0
  164. package/dist/rules/{built-in.js → builtIn.js} +17 -13
  165. package/dist/rules/builtIn.js.map +1 -0
  166. package/dist/rules/comments/{pattern-rule.d.ts → definePattern.d.ts} +2 -2
  167. package/dist/rules/comments/definePattern.d.ts.map +1 -0
  168. package/dist/rules/comments/definePattern.js +18 -0
  169. package/dist/rules/comments/definePattern.js.map +1 -0
  170. package/dist/rules/comments/kinds.d.ts +4 -4
  171. package/dist/rules/comments/kinds.d.ts.map +1 -1
  172. package/dist/rules/comments/kinds.js +16 -8
  173. package/dist/rules/comments/kinds.js.map +1 -1
  174. package/dist/rules/comments/{max-per-file.d.ts → maxPerFile.d.ts} +1 -1
  175. package/dist/rules/comments/maxPerFile.d.ts.map +1 -0
  176. package/dist/rules/comments/{max-per-file.js → maxPerFile.js} +14 -10
  177. package/dist/rules/comments/maxPerFile.js.map +1 -0
  178. package/dist/rules/comments/{no-banner.d.ts → noBanner.d.ts} +1 -1
  179. package/dist/rules/comments/noBanner.d.ts.map +1 -0
  180. package/dist/rules/comments/{no-banner.js → noBanner.js} +2 -2
  181. package/dist/rules/comments/noBanner.js.map +1 -0
  182. package/dist/rules/comments/{no-environment-pragma.d.ts → noEnvironmentPragma.d.ts} +1 -1
  183. package/dist/rules/comments/noEnvironmentPragma.d.ts.map +1 -0
  184. package/dist/rules/comments/{no-environment-pragma.js → noEnvironmentPragma.js} +2 -2
  185. package/dist/rules/comments/noEnvironmentPragma.js.map +1 -0
  186. package/dist/rules/comments/{no-jsdoc.d.ts → noJsdoc.d.ts} +1 -1
  187. package/dist/rules/comments/noJsdoc.d.ts.map +1 -0
  188. package/dist/rules/comments/{no-jsdoc.js → noJsdoc.js} +1 -1
  189. package/dist/rules/comments/noJsdoc.js.map +1 -0
  190. package/dist/rules/comments/{no-line-reference.d.ts → noLineReference.d.ts} +1 -1
  191. package/dist/rules/comments/noLineReference.d.ts.map +1 -0
  192. package/dist/rules/comments/{no-line-reference.js → noLineReference.js} +2 -2
  193. package/dist/rules/comments/noLineReference.js.map +1 -0
  194. package/dist/rules/comments/{no-pr-reference.d.ts → noPrReference.d.ts} +1 -1
  195. package/dist/rules/comments/noPrReference.d.ts.map +1 -0
  196. package/dist/rules/comments/{no-pr-reference.js → noPrReference.js} +2 -2
  197. package/dist/rules/comments/noPrReference.js.map +1 -0
  198. package/dist/rules/comments/{no-todo.d.ts → noTodo.d.ts} +1 -1
  199. package/dist/rules/comments/noTodo.d.ts.map +1 -0
  200. package/dist/rules/comments/{no-todo.js → noTodo.js} +2 -2
  201. package/dist/rules/comments/noTodo.js.map +1 -0
  202. package/dist/rules/comments/scan.d.ts +3 -3
  203. package/dist/rules/comments/scan.d.ts.map +1 -1
  204. package/dist/rules/comments/scan.js +23 -19
  205. package/dist/rules/comments/scan.js.map +1 -1
  206. package/dist/rules/{manifests-sorted.d.ts → manifestsSorted.d.ts} +1 -1
  207. package/dist/rules/manifestsSorted.d.ts.map +1 -0
  208. package/dist/rules/{manifests-sorted.js → manifestsSorted.js} +1 -1
  209. package/dist/rules/manifestsSorted.js.map +1 -0
  210. package/dist/rules/manifestsSourceFirst.d.ts +2 -0
  211. package/dist/rules/manifestsSourceFirst.d.ts.map +1 -0
  212. package/dist/rules/manifestsSourceFirst.js +43 -0
  213. package/dist/rules/manifestsSourceFirst.js.map +1 -0
  214. package/dist/rules/{max-lines.d.ts → maxLines.d.ts} +1 -1
  215. package/dist/rules/maxLines.d.ts.map +1 -0
  216. package/dist/rules/{max-lines.js → maxLines.js} +13 -11
  217. package/dist/rules/maxLines.js.map +1 -0
  218. package/dist/rules/stories/origins.d.ts +10 -0
  219. package/dist/rules/stories/origins.d.ts.map +1 -0
  220. package/dist/rules/stories/origins.js +129 -0
  221. package/dist/rules/stories/origins.js.map +1 -0
  222. package/dist/rules/stories/prerequisites.d.ts +4 -0
  223. package/dist/rules/stories/prerequisites.d.ts.map +1 -0
  224. package/dist/rules/stories/prerequisites.js +32 -0
  225. package/dist/rules/stories/prerequisites.js.map +1 -0
  226. package/dist/rules/stories/program.d.ts +5 -0
  227. package/dist/rules/stories/program.d.ts.map +1 -0
  228. package/dist/rules/stories/program.js +22 -0
  229. package/dist/rules/stories/program.js.map +1 -0
  230. package/dist/rules/stories/specs.d.ts +2 -0
  231. package/dist/rules/stories/specs.d.ts.map +1 -0
  232. package/dist/rules/stories/specs.js +80 -0
  233. package/dist/rules/stories/specs.js.map +1 -0
  234. package/dist/rules/suppressions/biome/configs.d.ts +1 -1
  235. package/dist/rules/suppressions/biome/configs.d.ts.map +1 -1
  236. package/dist/rules/suppressions/biome/configs.js +23 -17
  237. package/dist/rules/suppressions/biome/configs.js.map +1 -1
  238. package/dist/rules/suppressions/biome/declarations.d.ts +4 -4
  239. package/dist/rules/suppressions/biome/declarations.d.ts.map +1 -1
  240. package/dist/rules/suppressions/biome/declarations.js +15 -7
  241. package/dist/rules/suppressions/biome/declarations.js.map +1 -1
  242. package/dist/rules/suppressions/biome/json.d.ts +6 -6
  243. package/dist/rules/suppressions/biome/json.d.ts.map +1 -1
  244. package/dist/rules/suppressions/biome/json.js +27 -13
  245. package/dist/rules/suppressions/biome/json.js.map +1 -1
  246. package/dist/rules/suppressions/biome/resolve.d.ts +1 -1
  247. package/dist/rules/suppressions/biome/resolve.d.ts.map +1 -1
  248. package/dist/rules/suppressions/biome/resolve.js +23 -17
  249. package/dist/rules/suppressions/biome/resolve.js.map +1 -1
  250. package/dist/rules/suppressions/biome/weakenings.d.ts +1 -1
  251. package/dist/rules/suppressions/biome/weakenings.d.ts.map +1 -1
  252. package/dist/rules/suppressions/biome/weakenings.js +78 -52
  253. package/dist/rules/suppressions/biome/weakenings.js.map +1 -1
  254. package/dist/rules/suppressions/{biome-overrides.d.ts → biomeOverrides.d.ts} +1 -1
  255. package/dist/rules/suppressions/biomeOverrides.d.ts.map +1 -0
  256. package/dist/rules/suppressions/biomeOverrides.js +75 -0
  257. package/dist/rules/suppressions/biomeOverrides.js.map +1 -0
  258. package/dist/rules/suppressions/directives.d.ts +1 -1
  259. package/dist/rules/suppressions/directives.d.ts.map +1 -1
  260. package/dist/rules/suppressions/directives.js +5 -3
  261. package/dist/rules/suppressions/directives.js.map +1 -1
  262. package/dist/rules/suppressions/{no-double-cast.d.ts → noDoubleCast.d.ts} +1 -1
  263. package/dist/rules/suppressions/noDoubleCast.d.ts.map +1 -0
  264. package/dist/rules/suppressions/{no-double-cast.js → noDoubleCast.js} +15 -11
  265. package/dist/rules/suppressions/noDoubleCast.js.map +1 -0
  266. package/dist/rules/suppressions/noIgnoreDeprecations.js +1 -1
  267. package/dist/rules/suppressions/noIgnoreDeprecations.js.map +1 -1
  268. package/dist/rules/suppressions/{no-inline.d.ts → noInline.d.ts} +1 -1
  269. package/dist/rules/suppressions/noInline.d.ts.map +1 -0
  270. package/dist/rules/suppressions/{no-inline.js → noInline.js} +10 -8
  271. package/dist/rules/suppressions/noInline.js.map +1 -0
  272. package/dist/rules/suppressions/stylesheetComments.d.ts +4 -0
  273. package/dist/rules/suppressions/stylesheetComments.d.ts.map +1 -0
  274. package/dist/rules/suppressions/{stylesheet-comments.js → stylesheetComments.js} +12 -6
  275. package/dist/rules/suppressions/stylesheetComments.js.map +1 -0
  276. package/dist/rules/syntax.d.ts +1 -1
  277. package/dist/rules/syntax.d.ts.map +1 -1
  278. package/dist/rules/syntax.js +2 -2
  279. package/dist/rules/syntax.js.map +1 -1
  280. package/dist/{standard-schema.d.ts → standardSchema.d.ts} +7 -7
  281. package/dist/standardSchema.d.ts.map +1 -0
  282. package/dist/{standard-schema.js → standardSchema.js} +1 -1
  283. package/dist/standardSchema.js.map +1 -0
  284. package/docs/debt-and-hooks.md +2 -2
  285. package/docs/modules.md +27 -24
  286. package/docs/roadmap.md +1 -1
  287. package/docs/rules.md +2 -0
  288. package/package.json +2 -3
  289. package/src/baseline/compare.ts +6 -4
  290. package/src/baseline/format.ts +17 -10
  291. package/src/baseline/legacy.ts +4 -4
  292. package/src/baseline/rewrite.ts +2 -2
  293. package/src/baseline/update.ts +23 -19
  294. package/src/cli/args.ts +20 -21
  295. package/src/cli/baseline.ts +36 -35
  296. package/src/cli/fix.ts +40 -43
  297. package/src/cli/lint.ts +17 -21
  298. package/src/cli/messages.ts +2 -2
  299. package/src/cli/migrate.ts +21 -22
  300. package/src/cli/migrateExceptions.ts +1 -1
  301. package/src/cli/program.ts +1 -1
  302. package/src/cli/{run-main.ts → runMain.ts} +2 -2
  303. package/src/cli/shrink.ts +11 -8
  304. package/src/cli.ts +1 -1
  305. package/src/config/decode.ts +15 -10
  306. package/src/config/load.ts +44 -37
  307. package/src/config/resolve.ts +34 -29
  308. package/src/config.ts +5 -3
  309. package/src/decoded.ts +6 -5
  310. package/src/engine/baselineFile.ts +29 -0
  311. package/src/engine/evaluate.ts +7 -8
  312. package/src/engine/{run-rules.ts → runRules.ts} +22 -14
  313. package/src/engine/session.ts +34 -27
  314. package/src/engine/violation.ts +24 -13
  315. package/src/exceptions/registry.ts +10 -8
  316. package/src/git/base.ts +45 -37
  317. package/src/git/command.ts +6 -4
  318. package/src/git/history.ts +5 -4
  319. package/src/hooks/preCommit.ts +1 -1
  320. package/src/imports/generated.ts +1 -1
  321. package/src/imports/resolve.ts +1 -1
  322. package/src/imports/workspace.ts +1 -1
  323. package/src/inventory/collect.ts +40 -35
  324. package/src/inventory/filesystem.ts +38 -30
  325. package/src/inventory/{ignore-scope.ts → ignoreScope.ts} +20 -10
  326. package/src/inventory/walk.ts +1 -1
  327. package/src/manifests/sorted.ts +19 -19
  328. package/src/report/plural.ts +3 -1
  329. package/src/report/render.ts +24 -39
  330. package/src/report/stale.ts +33 -25
  331. package/src/rule.ts +26 -20
  332. package/src/rules/{built-in.ts → builtIn.ts} +16 -12
  333. package/src/rules/comments/{pattern-rule.ts → definePattern.ts} +5 -4
  334. package/src/rules/comments/kinds.ts +16 -9
  335. package/src/rules/comments/{max-per-file.ts → maxPerFile.ts} +15 -11
  336. package/src/rules/comments/{no-banner.ts → noBanner.ts} +1 -1
  337. package/src/rules/comments/{no-environment-pragma.ts → noEnvironmentPragma.ts} +1 -1
  338. package/src/rules/comments/{no-line-reference.ts → noLineReference.ts} +1 -1
  339. package/src/rules/comments/{no-pr-reference.ts → noPrReference.ts} +1 -1
  340. package/src/rules/comments/{no-todo.ts → noTodo.ts} +1 -1
  341. package/src/rules/comments/scan.ts +18 -15
  342. package/src/rules/manifestsSourceFirst.ts +48 -0
  343. package/src/rules/{max-lines.ts → maxLines.ts} +3 -2
  344. package/src/rules/stories/origins.ts +147 -0
  345. package/src/rules/stories/prerequisites.ts +37 -0
  346. package/src/rules/stories/program.ts +29 -0
  347. package/src/rules/stories/specs.ts +93 -0
  348. package/src/rules/suppressions/biome/configs.ts +23 -17
  349. package/src/rules/suppressions/biome/declarations.ts +15 -10
  350. package/src/rules/suppressions/biome/json.ts +25 -13
  351. package/src/rules/suppressions/biome/resolve.ts +23 -18
  352. package/src/rules/suppressions/biome/weakenings.ts +60 -41
  353. package/src/rules/suppressions/{biome-overrides.ts → biomeOverrides.ts} +32 -22
  354. package/src/rules/suppressions/directives.ts +5 -4
  355. package/src/rules/suppressions/{no-double-cast.ts → noDoubleCast.ts} +14 -10
  356. package/src/rules/suppressions/noIgnoreDeprecations.ts +1 -1
  357. package/src/rules/suppressions/{no-inline.ts → noInline.ts} +6 -5
  358. package/src/rules/suppressions/{stylesheet-comments.ts → stylesheetComments.ts} +11 -6
  359. package/src/rules/syntax.ts +2 -2
  360. package/src/{standard-schema.ts → standardSchema.ts} +4 -4
  361. package/dist/cli/run-main.d.ts.map +0 -1
  362. package/dist/cli/run-main.js.map +0 -1
  363. package/dist/engine/baseline-file.d.ts.map +0 -1
  364. package/dist/engine/baseline-file.js.map +0 -1
  365. package/dist/engine/run-rules.d.ts.map +0 -1
  366. package/dist/engine/run-rules.js +0 -34
  367. package/dist/engine/run-rules.js.map +0 -1
  368. package/dist/imports/pnpm-workspace.d.ts.map +0 -1
  369. package/dist/imports/pnpm-workspace.js.map +0 -1
  370. package/dist/inventory/ignore-scope.d.ts +0 -17
  371. package/dist/inventory/ignore-scope.d.ts.map +0 -1
  372. package/dist/inventory/ignore-scope.js.map +0 -1
  373. package/dist/rules/built-in.d.ts.map +0 -1
  374. package/dist/rules/built-in.js.map +0 -1
  375. package/dist/rules/comments/max-per-file.d.ts.map +0 -1
  376. package/dist/rules/comments/max-per-file.js.map +0 -1
  377. package/dist/rules/comments/no-banner.d.ts.map +0 -1
  378. package/dist/rules/comments/no-banner.js.map +0 -1
  379. package/dist/rules/comments/no-environment-pragma.d.ts.map +0 -1
  380. package/dist/rules/comments/no-environment-pragma.js.map +0 -1
  381. package/dist/rules/comments/no-jsdoc.d.ts.map +0 -1
  382. package/dist/rules/comments/no-jsdoc.js.map +0 -1
  383. package/dist/rules/comments/no-line-reference.d.ts.map +0 -1
  384. package/dist/rules/comments/no-line-reference.js.map +0 -1
  385. package/dist/rules/comments/no-pr-reference.d.ts.map +0 -1
  386. package/dist/rules/comments/no-pr-reference.js.map +0 -1
  387. package/dist/rules/comments/no-todo.d.ts.map +0 -1
  388. package/dist/rules/comments/no-todo.js.map +0 -1
  389. package/dist/rules/comments/pattern-rule.d.ts.map +0 -1
  390. package/dist/rules/comments/pattern-rule.js +0 -16
  391. package/dist/rules/comments/pattern-rule.js.map +0 -1
  392. package/dist/rules/manifests-sorted.d.ts.map +0 -1
  393. package/dist/rules/manifests-sorted.js.map +0 -1
  394. package/dist/rules/max-lines.d.ts.map +0 -1
  395. package/dist/rules/max-lines.js.map +0 -1
  396. package/dist/rules/suppressions/biome-overrides.d.ts.map +0 -1
  397. package/dist/rules/suppressions/biome-overrides.js +0 -63
  398. package/dist/rules/suppressions/biome-overrides.js.map +0 -1
  399. package/dist/rules/suppressions/no-double-cast.d.ts.map +0 -1
  400. package/dist/rules/suppressions/no-double-cast.js.map +0 -1
  401. package/dist/rules/suppressions/no-inline.d.ts.map +0 -1
  402. package/dist/rules/suppressions/no-inline.js.map +0 -1
  403. package/dist/rules/suppressions/stylesheet-comments.d.ts +0 -4
  404. package/dist/rules/suppressions/stylesheet-comments.d.ts.map +0 -1
  405. package/dist/rules/suppressions/stylesheet-comments.js.map +0 -1
  406. package/dist/standard-schema.d.ts.map +0 -1
  407. package/dist/standard-schema.js.map +0 -1
  408. package/src/engine/baseline-file.ts +0 -27
  409. /package/src/imports/{pnpm-workspace.ts → pnpmWorkspacePatterns.ts} +0 -0
  410. /package/src/rules/comments/{no-jsdoc.ts → noJsdoc.ts} +0 -0
  411. /package/src/rules/{manifests-sorted.ts → manifestsSorted.ts} +0 -0
@@ -8,31 +8,39 @@ interface StaleFiles {
8
8
  readonly registry: string;
9
9
  }
10
10
 
11
- const section = (label: string, file: string, guidance: string, lines: readonly string[]): readonly string[] =>
12
- lines.length === 0 ? [] : [[`${label} ${file}: ${plural(lines.length, "stale entry", "stale entries")}`, ` ${guidance}`, ...lines].join("\n")];
11
+ function section(label: string, file: string, guidance: string, lines: readonly string[]): readonly string[] {
12
+ return lines.length === 0
13
+ ? []
14
+ : [[`${label} ${file}: ${plural(lines.length, "stale entry", "stale entries")}`, ` ${guidance}`, ...lines].join("\n")];
15
+ }
13
16
 
14
- const baselineLine = ({ entry, problem }: StaleBaselineEntry): string => ` ${entry.rule} ${entry.file} ${problem}.`;
17
+ function baselineLine({ entry, problem }: StaleBaselineEntry): string {
18
+ return ` ${entry.rule} ${entry.file} ${problem}.`;
19
+ }
15
20
 
16
- const registryLine = ({ entry, problem }: StaleRegistryEntry): string =>
17
- ` ${entry.rule} ${entry.file}${entry.subject === undefined ? "" : ` (${entry.subject})`} ${problem}.`;
21
+ function registryLine({ entry, problem }: StaleRegistryEntry): string {
22
+ return ` ${entry.rule} ${entry.file}${entry.subject === undefined ? "" : ` (${entry.subject})`} ${problem}.`;
23
+ }
18
24
 
19
- export const staleSections = (outcome: Outcome, files: StaleFiles): readonly string[] => [
20
- ...section(
21
- "error",
22
- files.baseline,
23
- "An entry for a rule that is off or unknown covers nothing. Run `quality baseline prune` to drop it.",
24
- outcome.staleBaseline.map(baselineLine),
25
- ),
26
- ...section(
27
- "note",
28
- files.baseline,
29
- "These pass, and `quality baseline tighten` lowers them when their files change. `quality baseline prune` lowers them all.",
30
- outcome.looseBaseline.map(baselineLine),
31
- ),
32
- ...section(
33
- "error",
34
- files.registry,
35
- "Every registered exception must still apply. Remove the entries whose exception is gone.",
36
- outcome.staleRegistry.map(registryLine),
37
- ),
38
- ];
25
+ export function staleSections(outcome: Outcome, files: StaleFiles): readonly string[] {
26
+ return [
27
+ ...section(
28
+ "error",
29
+ files.baseline,
30
+ "An entry for an unknown rule covers nothing. Run `quality baseline prune` to drop it.",
31
+ outcome.staleBaseline.map(baselineLine),
32
+ ),
33
+ ...section(
34
+ "note",
35
+ files.baseline,
36
+ "These pass, and `quality baseline tighten` lowers them when their files change. `quality baseline prune` lowers them all.",
37
+ outcome.looseBaseline.map(baselineLine),
38
+ ),
39
+ ...section(
40
+ "error",
41
+ files.registry,
42
+ "Every registered exception must still apply. Remove the entries whose exception is gone.",
43
+ outcome.staleRegistry.map(registryLine),
44
+ ),
45
+ ];
46
+ }
package/src/rule.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { Bivariant } from "@shivaedev/types/bivariant.ts";
2
2
  import type { ExceptionDeclaration } from "#exceptions/declaration.ts";
3
- import { describeIssue, type StandardSchemaV1 } from "./standard-schema.ts";
3
+ import { describeIssue, type StandardSchemaV1 } from "./standardSchema.ts";
4
4
 
5
5
  export interface Finding {
6
6
  readonly count?: number | undefined;
@@ -28,35 +28,35 @@ export interface RuleInputs {
28
28
  readonly sources: readonly SourceFile[];
29
29
  }
30
30
 
31
- export interface RuleContext<Options> extends RuleInputs {
32
- readonly options: Options;
31
+ export interface RuleContext<TOptions> extends RuleInputs {
32
+ readonly options: TOptions;
33
33
  }
34
34
 
35
35
  export type Configured =
36
36
  | { readonly _tag: "Invalid"; readonly issues: readonly string[] }
37
37
  | { readonly _tag: "Ready"; readonly check: (inputs: RuleInputs) => Promise<Findings> };
38
38
 
39
- export interface Rule<Id extends string = string, Input = unknown> {
39
+ export interface Rule<TId extends string = string, TInput = unknown> {
40
40
  // Bivariance keeps rules with different options in one list; configure validates its input at runtime.
41
- configure: Bivariant<(options: Input | undefined) => Promise<Configured>>;
41
+ configure: Bivariant<(options: TInput | undefined) => Promise<Configured>>;
42
42
  readonly description: string;
43
43
  readonly family?: boolean;
44
- readonly id: Id;
44
+ readonly id: TId;
45
45
  readonly registrable?: boolean;
46
46
  }
47
47
 
48
- interface RuleDefinition<Id extends string, Options> {
49
- readonly check: (context: RuleContext<Options>) => Findings | PromiseLike<Findings>;
48
+ interface RuleDefinition<TId extends string, TOptions> {
49
+ readonly check: (context: RuleContext<TOptions>) => Findings | PromiseLike<Findings>;
50
50
  readonly description: string;
51
- readonly id: Id;
51
+ readonly id: TId;
52
52
  readonly registrable?: false;
53
53
  }
54
54
 
55
- interface RuleDefinitionWithOptions<Id extends string, Input, Options> extends RuleDefinition<Id, Options> {
56
- readonly options: StandardSchemaV1<Input, Options>;
55
+ interface RuleDefinitionWithOptions<TId extends string, TInput, TOptions> extends RuleDefinition<TId, TOptions> {
56
+ readonly options: StandardSchemaV1<TInput, TOptions>;
57
57
  }
58
58
 
59
- interface RuleDefinitionWithoutOptions<Id extends string> extends RuleDefinition<Id, undefined> {
59
+ interface RuleDefinitionWithoutOptions<TId extends string> extends RuleDefinition<TId, undefined> {
60
60
  readonly options?: undefined;
61
61
  }
62
62
 
@@ -68,9 +68,12 @@ const noOptions: StandardSchemaV1<undefined, undefined> = {
68
68
  },
69
69
  };
70
70
 
71
- const configureWith =
72
- <Input, Options>(schema: StandardSchemaV1<Input, Options>, check: RuleDefinition<string, Options>["check"], absent: unknown) =>
73
- async (options: unknown): Promise<Configured> => {
71
+ function configureWith<TInput, TOptions>(
72
+ schema: StandardSchemaV1<TInput, TOptions>,
73
+ check: RuleDefinition<string, TOptions>["check"],
74
+ absent: unknown,
75
+ ) {
76
+ return async (options: unknown): Promise<Configured> => {
74
77
  const result = await schema["~standard"].validate(options ?? absent);
75
78
  if (result.issues !== undefined) {
76
79
  return { _tag: "Invalid", issues: result.issues.map(describeIssue) };
@@ -78,12 +81,15 @@ const configureWith =
78
81
  const value = result.value;
79
82
  return { _tag: "Ready", check: async (inputs) => check({ ...inputs, options: value }) };
80
83
  };
84
+ }
81
85
 
82
- export function defineRule<const Id extends string, Input, Options>(definition: RuleDefinitionWithOptions<Id, Input, Options>): Rule<Id, Input>;
83
- export function defineRule<const Id extends string>(definition: RuleDefinitionWithoutOptions<Id>): Rule<Id, undefined>;
84
- export function defineRule<const Id extends string, Input, Options>(
85
- definition: RuleDefinitionWithOptions<Id, Input, Options> | RuleDefinitionWithoutOptions<Id>,
86
- ): Rule<Id, Input> | Rule<Id, undefined> {
86
+ export function defineRule<const TId extends string, TInput, TOptions>(
87
+ definition: RuleDefinitionWithOptions<TId, TInput, TOptions>,
88
+ ): Rule<TId, TInput>;
89
+ export function defineRule<const TId extends string>(definition: RuleDefinitionWithoutOptions<TId>): Rule<TId, undefined>;
90
+ export function defineRule<const TId extends string, TInput, TOptions>(
91
+ definition: RuleDefinitionWithOptions<TId, TInput, TOptions> | RuleDefinitionWithoutOptions<TId>,
92
+ ): Rule<TId, TInput> | Rule<TId, undefined> {
87
93
  const configure =
88
94
  definition.options === undefined
89
95
  ? configureWith(noOptions, definition.check, undefined)
@@ -1,10 +1,10 @@
1
- import { maxPerFile } from "#rules/comments/max-per-file.ts";
2
- import { noBanner } from "#rules/comments/no-banner.ts";
3
- import { noEnvironmentPragma } from "#rules/comments/no-environment-pragma.ts";
4
- import { noJsdoc } from "#rules/comments/no-jsdoc.ts";
5
- import { noLineReference } from "#rules/comments/no-line-reference.ts";
6
- import { noPrReference } from "#rules/comments/no-pr-reference.ts";
7
- import { noTodo } from "#rules/comments/no-todo.ts";
1
+ import { maxPerFile } from "#rules/comments/maxPerFile.ts";
2
+ import { noBanner } from "#rules/comments/noBanner.ts";
3
+ import { noEnvironmentPragma } from "#rules/comments/noEnvironmentPragma.ts";
4
+ import { noJsdoc } from "#rules/comments/noJsdoc.ts";
5
+ import { noLineReference } from "#rules/comments/noLineReference.ts";
6
+ import { noPrReference } from "#rules/comments/noPrReference.ts";
7
+ import { noTodo } from "#rules/comments/noTodo.ts";
8
8
  import { distinctCase } from "#rules/files/distinctCase.ts";
9
9
  import { folderNames } from "#rules/files/folderNames.ts";
10
10
  import { namedAfterExport } from "#rules/files/namedAfterExport.ts";
@@ -17,17 +17,19 @@ import { noTestCode } from "#rules/imports/noTestCode.ts";
17
17
  import { importsResolvable } from "#rules/imports/resolvable.ts";
18
18
  import { importsRuntime } from "#rules/imports/runtime.ts";
19
19
  import { storySetup } from "#rules/stories/setup.ts";
20
- import { biomeOverrides } from "#rules/suppressions/biome-overrides.ts";
20
+ import { storySpecs } from "#rules/stories/specs.ts";
21
+ import { biomeOverrides } from "#rules/suppressions/biomeOverrides.ts";
21
22
  import { biomeRecommended } from "#rules/suppressions/biomeRecommended.ts";
22
- import { noDoubleCast } from "#rules/suppressions/no-double-cast.ts";
23
- import { noInline } from "#rules/suppressions/no-inline.ts";
23
+ import { noDoubleCast } from "#rules/suppressions/noDoubleCast.ts";
24
24
  import { noIgnoreDeprecations } from "#rules/suppressions/noIgnoreDeprecations.ts";
25
+ import { noInline } from "#rules/suppressions/noInline.ts";
25
26
  import { testsColocated } from "#rules/test-names/colocated.ts";
26
27
  import { testsFollow } from "#rules/test-names/follow.ts";
27
28
  import { biome } from "./biome.ts";
28
- import { manifestsSorted } from "./manifests-sorted.ts";
29
- import { maxLines } from "./max-lines.ts";
29
+ import { manifestsSorted } from "./manifestsSorted.ts";
30
+ import { manifestsSourceFirst } from "./manifestsSourceFirst.ts";
30
31
  import { maxDepth } from "./maxDepth.ts";
32
+ import { maxLines } from "./maxLines.ts";
31
33
  import { noBarrel } from "./noBarrel.ts";
32
34
 
33
35
  export const builtInRules = [
@@ -55,6 +57,7 @@ export const builtInRules = [
55
57
  importsAliased,
56
58
  noTestCode,
57
59
  manifestsSorted,
60
+ manifestsSourceFirst,
58
61
  namedAfterExport,
59
62
  folderNames,
60
63
  distinctCase,
@@ -62,4 +65,5 @@ export const builtInRules = [
62
65
  testsFollow,
63
66
  testsColocated,
64
67
  storySetup,
68
+ storySpecs,
65
69
  ] as const;
@@ -9,13 +9,13 @@ interface PatternRule<Id extends string> {
9
9
  readonly patterns: readonly RegExp[];
10
10
  }
11
11
 
12
- const firstMatch = (comment: SourceComment, patterns: readonly RegExp[]): string | undefined => {
12
+ function firstMatch(comment: SourceComment, patterns: readonly RegExp[]): string | undefined {
13
13
  const text = textOf(comment);
14
14
  return patterns.map((pattern) => pattern.exec(text)?.[0]).find((match) => match !== undefined);
15
- };
15
+ }
16
16
 
17
- export const definePatternRule = <const Id extends string>({ description, id, message, patterns }: PatternRule<Id>): Rule<Id, undefined> =>
18
- defineRule({
17
+ export function definePatternRule<const Id extends string>({ description, id, message, patterns }: PatternRule<Id>): Rule<Id, undefined> {
18
+ return defineRule({
19
19
  check: ({ sources }) =>
20
20
  sources.flatMap((file) =>
21
21
  commentsOf(file).flatMap((comment): readonly Finding[] => {
@@ -26,3 +26,4 @@ export const definePatternRule = <const Id extends string>({ description, id, me
26
26
  description,
27
27
  id,
28
28
  });
29
+ }
@@ -15,21 +15,28 @@ const DIRECTIVES: readonly RegExp[] = [
15
15
 
16
16
  const TRIPLE_SLASH = /^\/\/\/\s*</u;
17
17
 
18
- const contentOf = (comment: SourceComment): readonly string[] => comment.body.filter((line) => line !== "");
18
+ function contentOf(comment: SourceComment): readonly string[] {
19
+ return comment.body.filter((line) => line !== "");
20
+ }
19
21
 
20
- const startsWithTag = (line: string, tags: readonly string[]): boolean =>
21
- tags.some((tag) => line === tag || line.startsWith(`${tag} `) || line.startsWith(`${tag}\t`));
22
+ function startsWithTag(line: string, tags: readonly string[]): boolean {
23
+ return tags.some((tag) => line === tag || line.startsWith(`${tag} `) || line.startsWith(`${tag}\t`));
24
+ }
22
25
 
23
- export const textOf = (comment: SourceComment): string => comment.body.join("\n");
26
+ export function textOf(comment: SourceComment): string {
27
+ return comment.body.join("\n");
28
+ }
24
29
 
25
- export const isJsdoc = (comment: SourceComment): boolean => comment.kind === "block" && comment.text.startsWith("/**") && comment.text !== "/**/";
30
+ export function isJsdoc(comment: SourceComment): boolean {
31
+ return comment.kind === "block" && comment.text.startsWith("/**") && comment.text !== "/**/";
32
+ }
26
33
 
27
- export const isPragma = (comment: SourceComment, pragmas: readonly string[]): boolean => {
34
+ export function isPragma(comment: SourceComment, pragmas: readonly string[]): boolean {
28
35
  const content = contentOf(comment);
29
36
  return content.length > 0 && content.every((line) => startsWithTag(line, pragmas));
30
- };
37
+ }
31
38
 
32
- export const isDirective = (comment: SourceComment): boolean => {
39
+ export function isDirective(comment: SourceComment): boolean {
33
40
  const first = contentOf(comment)[0] ?? "";
34
41
  return TRIPLE_SLASH.test(comment.text) || DIRECTIVES.some((directive) => directive.test(first));
35
- };
42
+ }
@@ -9,18 +9,22 @@ const MaxPerFileOptions = Schema.Struct({
9
9
  max: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0), Schema.isLessThanOrEqualTo(2)).pipe(Schema.withDecodingDefaultKey(Effect.succeed(2))),
10
10
  });
11
11
 
12
- const continues = (previous: SourceComment | undefined, comment: SourceComment): boolean =>
13
- previous !== undefined
14
- && previous.kind === "line"
15
- && comment.kind === "line"
16
- && previous.ownLine
17
- && comment.ownLine
18
- && comment.line === previous.endLine + 1;
12
+ function continues(previous: SourceComment | undefined, comment: SourceComment): boolean {
13
+ return (
14
+ previous !== undefined
15
+ && previous.kind === "line"
16
+ && comment.kind === "line"
17
+ && previous.ownLine
18
+ && comment.ownLine
19
+ && comment.line === previous.endLine + 1
20
+ );
21
+ }
19
22
 
20
- const commentBlocks = (comments: readonly SourceComment[]): readonly SourceComment[] =>
21
- comments.filter((comment, index) => !continues(comments[index - 1], comment));
23
+ function commentBlocks(comments: readonly SourceComment[]): readonly SourceComment[] {
24
+ return comments.filter((comment, index) => !continues(comments[index - 1], comment));
25
+ }
22
26
 
23
- const overCap = (file: SourceFile, max: number, allow: readonly string[]): readonly Finding[] => {
27
+ function overCap(file: SourceFile, max: number, allow: readonly string[]): readonly Finding[] {
24
28
  const counted = commentsOf(file).filter((comment) => !(isDirective(comment) || isPragma(comment, allow)));
25
29
  const blocks = commentBlocks(counted);
26
30
  const first = blocks[max];
@@ -35,7 +39,7 @@ const overCap = (file: SourceFile, max: number, allow: readonly string[]): reado
35
39
  threshold: max,
36
40
  },
37
41
  ];
38
- };
42
+ }
39
43
 
40
44
  export const maxPerFile = defineRule({
41
45
  check: ({ options, sources }) => sources.flatMap((file) => overCap(file, options.max, options.allow)),
@@ -1,4 +1,4 @@
1
- import { definePatternRule } from "./pattern-rule.ts";
1
+ import { definePatternRule } from "./definePattern.ts";
2
2
 
3
3
  const DIVIDER = "[-=*#~_+/\\\\─━═┄┈]{3,}";
4
4
 
@@ -1,4 +1,4 @@
1
- import { definePatternRule } from "./pattern-rule.ts";
1
+ import { definePatternRule } from "./definePattern.ts";
2
2
 
3
3
  export const noEnvironmentPragma = definePatternRule({
4
4
  description:
@@ -1,4 +1,4 @@
1
- import { definePatternRule } from "./pattern-rule.ts";
1
+ import { definePatternRule } from "./definePattern.ts";
2
2
 
3
3
  const SOURCE_EXTENSION =
4
4
  "(?:[cm]?[jt]sx?|json5?|mdx?|css|scss|less|html?|vue|svelte|astro|ya?ml|toml|sql|prisma|graphql|gql|go|rs|py|rb|java|kt|swift|sh)";
@@ -1,4 +1,4 @@
1
- import { definePatternRule } from "./pattern-rule.ts";
1
+ import { definePatternRule } from "./definePattern.ts";
2
2
 
3
3
  export const noPrReference = definePatternRule({
4
4
  description: "History belongs in version control, not in the code. State the reason itself, so the comment holds without the link.",
@@ -1,4 +1,4 @@
1
- import { definePatternRule } from "./pattern-rule.ts";
1
+ import { definePatternRule } from "./definePattern.ts";
2
2
 
3
3
  export const noTodo = definePatternRule({
4
4
  description: "A TODO in the code is never done. Do the work now, or track it where the repository tracks work.",
@@ -13,15 +13,15 @@ export interface SourceComment {
13
13
 
14
14
  const DECLARATION = /\.d\.[cm]?ts$/u;
15
15
 
16
- const commentRanges = (source: ts.SourceFile): readonly ts.CommentRange[] => {
16
+ function commentRanges(source: ts.SourceFile): readonly ts.CommentRange[] {
17
17
  const ranges = new Map<number, ts.CommentRange>();
18
18
  const jsxText: ts.Node[] = [];
19
- const add = (found: readonly ts.CommentRange[] | undefined) => {
19
+ function add(found: readonly ts.CommentRange[] | undefined) {
20
20
  for (const range of found ?? []) {
21
21
  ranges.set(range.pos, range);
22
22
  }
23
- };
24
- const visit = (node: ts.Node) => {
23
+ }
24
+ function visit(node: ts.Node) {
25
25
  if (node.kind === ts.SyntaxKind.JsxText) {
26
26
  jsxText.push(node);
27
27
  }
@@ -30,22 +30,23 @@ const commentRanges = (source: ts.SourceFile): readonly ts.CommentRange[] => {
30
30
  for (const child of node.getChildren(source)) {
31
31
  visit(child);
32
32
  }
33
- };
33
+ }
34
34
  visit(source);
35
35
  return [...ranges.values()]
36
36
  .filter((range) => !jsxText.some((text) => range.pos >= text.pos && range.pos < text.end))
37
37
  .sort((left, right) => left.pos - right.pos);
38
- };
38
+ }
39
39
 
40
- export const bodyOf = (kind: SourceComment["kind"], text: string): readonly string[] =>
41
- kind === "line"
40
+ export function bodyOf(kind: SourceComment["kind"], text: string): readonly string[] {
41
+ return kind === "line"
42
42
  ? [text.slice(2).trim()]
43
43
  : text
44
44
  .slice(2, -2)
45
45
  .split("\n")
46
46
  .map((line) => line.trim().replace(/^\*(?=\s|$)\s*/u, ""));
47
+ }
47
48
 
48
- const toComment = (source: ts.SourceFile, range: ts.CommentRange): SourceComment => {
49
+ function toComment(source: ts.SourceFile, range: ts.CommentRange): SourceComment {
49
50
  const kind = range.kind === ts.SyntaxKind.SingleLineCommentTrivia ? "line" : "block";
50
51
  const text = source.text.slice(range.pos, range.end);
51
52
  const start = source.getLineAndCharacterOfPosition(range.pos);
@@ -58,16 +59,16 @@ const toComment = (source: ts.SourceFile, range: ts.CommentRange): SourceComment
58
59
  ownLine: source.text.slice(lineStart, range.pos).trim() === "",
59
60
  text,
60
61
  };
61
- };
62
+ }
62
63
 
63
64
  const scanned = new WeakMap<SourceFile, readonly SourceComment[]>();
64
65
 
65
- const scan = (file: SourceFile): readonly SourceComment[] => {
66
+ function scan(file: SourceFile): readonly SourceComment[] {
66
67
  const source = parse(file);
67
68
  return source === undefined ? [] : commentRanges(source).map((range) => toComment(source, range));
68
- };
69
+ }
69
70
 
70
- export const scanComments = (file: SourceFile): readonly SourceComment[] => {
71
+ export function scanComments(file: SourceFile): readonly SourceComment[] {
71
72
  const cached = scanned.get(file);
72
73
  if (cached !== undefined) {
73
74
  return cached;
@@ -75,6 +76,8 @@ export const scanComments = (file: SourceFile): readonly SourceComment[] => {
75
76
  const comments = scan(file);
76
77
  scanned.set(file, comments);
77
78
  return comments;
78
- };
79
+ }
79
80
 
80
- export const commentsOf = (file: SourceFile): readonly SourceComment[] => (DECLARATION.test(file.path) ? [] : scanComments(file));
81
+ export function commentsOf(file: SourceFile): readonly SourceComment[] {
82
+ return DECLARATION.test(file.path) ? [] : scanComments(file);
83
+ }
@@ -0,0 +1,48 @@
1
+ import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
2
+ import { Effect } from "effect";
3
+ import { manifestsIn } from "#manifests/sorted.ts";
4
+ import { defineRule, type Finding } from "#rule.ts";
5
+ import { entriesOf, itemsOf, type Json, member, parseJsonc } from "#rules/suppressions/biome/json.ts";
6
+
7
+ const CONDITION = "source";
8
+
9
+ function misplaced(path: string, field: string, node: Json): readonly Finding[] {
10
+ const conditions = entriesOf(node);
11
+ const first = conditions[0]?.[0];
12
+ const own: readonly Finding[] =
13
+ first === CONDITION || !conditions.some(([key]) => key === CONDITION)
14
+ ? []
15
+ : [
16
+ {
17
+ file: path,
18
+ line: node.line,
19
+ message: `Lists the "${CONDITION}" condition after "${first}" in ${field}. TypeScript takes the first condition that matches, so once built output exists it reads that instead of the sources the "${CONDITION}" condition names. Move "${CONDITION}" to the top of this condition block.`,
20
+ subject: field,
21
+ },
22
+ ];
23
+ const nested = [...conditions.map(([, value]) => value), ...itemsOf(node)];
24
+ return [...own, ...nested.flatMap((value) => misplaced(path, field, value))];
25
+ }
26
+
27
+ function findingsIn(path: string, text: string): readonly Finding[] {
28
+ const parsed = parseJsonc(path, text);
29
+ if (parsed._tag !== "Parsed") {
30
+ return [];
31
+ }
32
+ return ["exports", "imports"].flatMap((field) => {
33
+ const map = member(parsed.json, field);
34
+ return map === undefined ? [] : misplaced(path, field, map);
35
+ });
36
+ }
37
+
38
+ export const manifestsSourceFirst = defineRule({
39
+ check: async ({ readText, root }) => {
40
+ const manifests = await Effect.runPromise(manifestsIn(root).pipe(Effect.provide(NodeFileSystem.layer)));
41
+ const texts = await Promise.all(manifests.map(async (path) => ({ path, text: (await readText(path)) ?? "" })));
42
+ return texts.filter(({ text }) => text.includes(`"${CONDITION}"`)).flatMap(({ path, text }) => findingsIn(path, text));
43
+ },
44
+ description:
45
+ 'Every package.json condition block that names the "source" condition lists it first in exports and imports, so TypeScript reads the sources whether or not built output exists.',
46
+ id: "manifests/source-first",
47
+ registrable: false,
48
+ });
@@ -11,8 +11,8 @@ const MaxLinesOptions = Schema.Struct({
11
11
  test: Limit.check(Schema.isLessThanOrEqualTo(300)).pipe(Schema.withDecodingDefaultKey(Effect.succeed(300))),
12
12
  });
13
13
 
14
- const oversized = (file: SourceFile, limit: number): readonly Finding[] =>
15
- file.lines.length <= limit
14
+ function oversized(file: SourceFile, limit: number): readonly Finding[] {
15
+ return file.lines.length <= limit
16
16
  ? []
17
17
  : [
18
18
  {
@@ -22,6 +22,7 @@ const oversized = (file: SourceFile, limit: number): readonly Finding[] =>
22
22
  threshold: limit,
23
23
  },
24
24
  ];
25
+ }
25
26
 
26
27
  export const maxLines = defineRule({
27
28
  check: ({ options, sources }) =>
@@ -0,0 +1,147 @@
1
+ import { join } from "node:path";
2
+ import ts from "typescript";
3
+ import type { ImportGraph } from "#imports/graph.ts";
4
+ import type { RuleInputs } from "#rule.ts";
5
+ import { storyProgram } from "./program.ts";
6
+
7
+ type Origin = "factory" | "kit" | "story" | "plain" | "suite" | "vitest" | "stories" | "function" | undefined;
8
+
9
+ function importFrom(node: ts.Node): string | undefined {
10
+ for (let current: ts.Node | undefined = node; current !== undefined; current = current.parent) {
11
+ if (ts.isImportDeclaration(current) && ts.isStringLiteral(current.moduleSpecifier)) {
12
+ return current.moduleSpecifier.text;
13
+ }
14
+ }
15
+ return undefined;
16
+ }
17
+
18
+ function moduleOrigin(from: string | undefined): Origin {
19
+ if (from === "vitest" || from === "@effect/vitest") {
20
+ return "vitest";
21
+ }
22
+ return from === "@shivaedev/test-story" || from === "@shivaedev/test-story/storyKit.ts" ? "stories" : undefined;
23
+ }
24
+
25
+ function imported(name: string, from: string | undefined): Origin {
26
+ const namespace = moduleOrigin(from);
27
+ if (name === "storyKit" && namespace === "stories") {
28
+ return "factory";
29
+ }
30
+ if ((name === "describe" || name === "suite") && namespace === "vitest") {
31
+ return "suite";
32
+ }
33
+ return (name === "it" || name === "test") && namespace === "vitest" ? "plain" : undefined;
34
+ }
35
+
36
+ export function storyOrigins(inputs: RuleInputs, graph: ImportGraph) {
37
+ const program = storyProgram(inputs, graph);
38
+ const checker = program.getTypeChecker();
39
+ const visiting = new Set<ts.Node>();
40
+ const known = new Map<ts.Node, Origin>();
41
+
42
+ function binding(node: ts.BindingElement): Origin {
43
+ if (!ts.isVariableDeclaration(node.parent.parent)) {
44
+ return undefined;
45
+ }
46
+ const value = node.parent.parent.initializer;
47
+ const name = node.propertyName ?? node.name;
48
+ return value !== undefined && (ts.isIdentifier(name) || ts.isStringLiteral(name)) ? member(value, name.text) : undefined;
49
+ }
50
+
51
+ function declaration(node: ts.Node): Origin {
52
+ if (ts.isImportSpecifier(node)) {
53
+ return imported((node.propertyName ?? node.name).text, importFrom(node));
54
+ }
55
+ if (ts.isNamespaceImport(node)) {
56
+ return moduleOrigin(importFrom(node));
57
+ }
58
+ if (ts.isVariableDeclaration(node) || ts.isPropertyAssignment(node)) {
59
+ return node.initializer === undefined ? undefined : origin(node.initializer);
60
+ }
61
+ return localBinding(node);
62
+ }
63
+
64
+ function localBinding(node: ts.Node): Origin {
65
+ if (ts.isShorthandPropertyAssignment(node)) {
66
+ return symbol(checker.getShorthandAssignmentValueSymbol(node));
67
+ }
68
+ return ts.isBindingElement(node) ? binding(node) : undefined;
69
+ }
70
+
71
+ function symbol(value: ts.Symbol | undefined): Origin {
72
+ if (value === undefined) {
73
+ return undefined;
74
+ }
75
+ const direct = value.declarations?.map(visit).find((result) => result !== undefined);
76
+ return direct ?? (value.flags & ts.SymbolFlags.Alias ? symbol(checker.getAliasedSymbol(value)) : undefined);
77
+ }
78
+
79
+ function object(node: ts.ObjectLiteralExpression): Origin {
80
+ for (const property of [...node.properties].reverse()) {
81
+ if (property.name?.getText() === "it") {
82
+ return visit(property) === "story" ? "kit" : undefined;
83
+ }
84
+ if (ts.isSpreadAssignment(property) && visit(property.expression) === "kit") {
85
+ return "kit";
86
+ }
87
+ }
88
+ return undefined;
89
+ }
90
+
91
+ function member(value: ts.Expression, name: string): Origin {
92
+ const receiver = visit(value);
93
+ if (receiver === "kit" && name === "it") {
94
+ return "story";
95
+ }
96
+ if (receiver === "story" || receiver === "plain" || receiver === "suite") {
97
+ return receiver;
98
+ }
99
+ const from = receiver === "vitest" ? "vitest" : undefined;
100
+ return imported(name, receiver === "stories" ? "@shivaedev/test-story/storyKit.ts" : from);
101
+ }
102
+
103
+ function identifier(node: ts.Identifier): Origin {
104
+ if (node.text === "describe" || node.text === "suite") {
105
+ return symbol(checker.getSymbolAtLocation(node)) ?? "suite";
106
+ }
107
+ const value = checker.getSymbolAtLocation(node);
108
+ return symbol(value) ?? (value === undefined && (node.text === "it" || node.text === "test") ? "plain" : undefined);
109
+ }
110
+
111
+ function origin(node: ts.Node): Origin {
112
+ if (ts.isFunctionLike(node)) {
113
+ return "function";
114
+ }
115
+ if (ts.isObjectLiteralExpression(node)) {
116
+ return object(node);
117
+ }
118
+ return expression(node);
119
+ }
120
+
121
+ function expression(node: ts.Node): Origin {
122
+ if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isSatisfiesExpression(node)) {
123
+ return visit(node.expression);
124
+ }
125
+ if (ts.isCallExpression(node)) {
126
+ const callee = visit(node.expression);
127
+ return callee === "factory" ? "kit" : callee;
128
+ }
129
+ if (ts.isPropertyAccessExpression(node)) {
130
+ return member(node.expression, node.name.text) ?? symbol(checker.getSymbolAtLocation(node.name));
131
+ }
132
+ return ts.isIdentifier(node) ? identifier(node) : declaration(node);
133
+ }
134
+
135
+ function visit(node: ts.Node): Origin {
136
+ if (visiting.has(node) || known.has(node)) {
137
+ return known.get(node);
138
+ }
139
+ visiting.add(node);
140
+ const result = origin(node);
141
+ visiting.delete(node);
142
+ known.set(node, result);
143
+ return result;
144
+ }
145
+
146
+ return { origin: visit, source: (path: string) => program.getSourceFile(join(inputs.root, path)) };
147
+ }