@nielspeter/eess-ts 0.2.0 → 0.4.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 (433) hide show
  1. package/README.md +87 -34
  2. package/dist/builders/call-rule-builder.d.ts +66 -26
  3. package/dist/builders/call-rule-builder.d.ts.map +1 -1
  4. package/dist/builders/call-rule-builder.js +73 -31
  5. package/dist/builders/call-rule-builder.js.map +1 -1
  6. package/dist/builders/class-rule-builder.d.ts +125 -26
  7. package/dist/builders/class-rule-builder.d.ts.map +1 -1
  8. package/dist/builders/class-rule-builder.js +134 -30
  9. package/dist/builders/class-rule-builder.js.map +1 -1
  10. package/dist/builders/correspondence-builder.d.ts +287 -0
  11. package/dist/builders/correspondence-builder.d.ts.map +1 -0
  12. package/dist/builders/correspondence-builder.js +393 -0
  13. package/dist/builders/correspondence-builder.js.map +1 -0
  14. package/dist/builders/correspondence-findings.d.ts +104 -0
  15. package/dist/builders/correspondence-findings.d.ts.map +1 -0
  16. package/dist/builders/correspondence-findings.js +301 -0
  17. package/dist/builders/correspondence-findings.js.map +1 -0
  18. package/dist/builders/cross-layer-builder.d.ts +99 -16
  19. package/dist/builders/cross-layer-builder.d.ts.map +1 -1
  20. package/dist/builders/cross-layer-builder.js +181 -35
  21. package/dist/builders/cross-layer-builder.js.map +1 -1
  22. package/dist/builders/function-rule-builder.d.ts +144 -25
  23. package/dist/builders/function-rule-builder.d.ts.map +1 -1
  24. package/dist/builders/function-rule-builder.js +173 -26
  25. package/dist/builders/function-rule-builder.js.map +1 -1
  26. package/dist/builders/jsx-rule-builder.d.ts +70 -14
  27. package/dist/builders/jsx-rule-builder.d.ts.map +1 -1
  28. package/dist/builders/jsx-rule-builder.js +73 -14
  29. package/dist/builders/jsx-rule-builder.js.map +1 -1
  30. package/dist/builders/module-rule-builder.d.ts +2 -2
  31. package/dist/builders/module-rule-builder.d.ts.map +1 -1
  32. package/dist/builders/module-rule-builder.js +12 -3
  33. package/dist/builders/module-rule-builder.js.map +1 -1
  34. package/dist/builders/scoped-function-rule-builder.d.ts +11 -0
  35. package/dist/builders/scoped-function-rule-builder.d.ts.map +1 -1
  36. package/dist/builders/scoped-function-rule-builder.js +12 -0
  37. package/dist/builders/scoped-function-rule-builder.js.map +1 -1
  38. package/dist/builders/slice-discovery-message.d.ts +36 -0
  39. package/dist/builders/slice-discovery-message.d.ts.map +1 -0
  40. package/dist/builders/slice-discovery-message.js +181 -0
  41. package/dist/builders/slice-discovery-message.js.map +1 -0
  42. package/dist/builders/slice-rule-builder.d.ts +151 -12
  43. package/dist/builders/slice-rule-builder.d.ts.map +1 -1
  44. package/dist/builders/slice-rule-builder.js +242 -35
  45. package/dist/builders/slice-rule-builder.js.map +1 -1
  46. package/dist/builders/type-rule-builder.d.ts +95 -24
  47. package/dist/builders/type-rule-builder.d.ts.map +1 -1
  48. package/dist/builders/type-rule-builder.js +105 -29
  49. package/dist/builders/type-rule-builder.js.map +1 -1
  50. package/dist/{helpers → builders}/within.d.ts +2 -2
  51. package/dist/builders/within.d.ts.map +1 -0
  52. package/dist/{helpers → builders}/within.js +1 -1
  53. package/dist/builders/within.js.map +1 -0
  54. package/dist/cli/commands/baseline.d.ts +3 -2
  55. package/dist/cli/commands/baseline.d.ts.map +1 -1
  56. package/dist/cli/commands/baseline.js +68 -6
  57. package/dist/cli/commands/baseline.js.map +1 -1
  58. package/dist/cli/commands/check.d.ts +7 -2
  59. package/dist/cli/commands/check.d.ts.map +1 -1
  60. package/dist/cli/commands/check.js +241 -45
  61. package/dist/cli/commands/check.js.map +1 -1
  62. package/dist/cli/commands/doctor.d.ts +29 -0
  63. package/dist/cli/commands/doctor.d.ts.map +1 -0
  64. package/dist/cli/commands/doctor.js +224 -0
  65. package/dist/cli/commands/doctor.js.map +1 -0
  66. package/dist/cli/commands/explain.d.ts +2 -1
  67. package/dist/cli/commands/explain.d.ts.map +1 -1
  68. package/dist/cli/commands/explain.js +19 -10
  69. package/dist/cli/commands/explain.js.map +1 -1
  70. package/dist/cli/commands/init.d.ts +9 -3
  71. package/dist/cli/commands/init.d.ts.map +1 -1
  72. package/dist/cli/commands/init.js +162 -137
  73. package/dist/cli/commands/init.js.map +1 -1
  74. package/dist/cli/import-rule-module.d.ts +29 -0
  75. package/dist/cli/import-rule-module.d.ts.map +1 -0
  76. package/dist/cli/import-rule-module.js +60 -0
  77. package/dist/cli/import-rule-module.js.map +1 -0
  78. package/dist/cli/index.d.ts +2 -2
  79. package/dist/cli/index.d.ts.map +1 -1
  80. package/dist/cli/index.js +98 -33
  81. package/dist/cli/index.js.map +1 -1
  82. package/dist/cli/load-rules.d.ts +6 -11
  83. package/dist/cli/load-rules.d.ts.map +1 -1
  84. package/dist/cli/load-rules.js +21 -29
  85. package/dist/cli/load-rules.js.map +1 -1
  86. package/dist/cli/resolve-config.d.ts.map +1 -1
  87. package/dist/cli/resolve-config.js +24 -14
  88. package/dist/cli/resolve-config.js.map +1 -1
  89. package/dist/cli/rule-file-findings.d.ts +178 -0
  90. package/dist/cli/rule-file-findings.d.ts.map +1 -0
  91. package/dist/cli/rule-file-findings.js +293 -0
  92. package/dist/cli/rule-file-findings.js.map +1 -0
  93. package/dist/cli/watch.d.ts +20 -7
  94. package/dist/cli/watch.d.ts.map +1 -1
  95. package/dist/cli/watch.js +39 -14
  96. package/dist/cli/watch.js.map +1 -1
  97. package/dist/conditions/body-analysis-function.d.ts.map +1 -1
  98. package/dist/conditions/body-analysis-function.js +16 -7
  99. package/dist/conditions/body-analysis-function.js.map +1 -1
  100. package/dist/conditions/body-analysis-module.d.ts.map +1 -1
  101. package/dist/conditions/body-analysis-module.js +14 -9
  102. package/dist/conditions/body-analysis-module.js.map +1 -1
  103. package/dist/conditions/body-analysis.d.ts.map +1 -1
  104. package/dist/conditions/body-analysis.js +16 -7
  105. package/dist/conditions/body-analysis.js.map +1 -1
  106. package/dist/conditions/call.d.ts.map +1 -1
  107. package/dist/conditions/call.js +38 -17
  108. package/dist/conditions/call.js.map +1 -1
  109. package/dist/conditions/catch-analysis.d.ts +2 -1
  110. package/dist/conditions/catch-analysis.d.ts.map +1 -1
  111. package/dist/conditions/catch-analysis.js.map +1 -1
  112. package/dist/conditions/cross-layer.d.ts +2 -10
  113. package/dist/conditions/cross-layer.d.ts.map +1 -1
  114. package/dist/conditions/cross-layer.js +227 -23
  115. package/dist/conditions/cross-layer.js.map +1 -1
  116. package/dist/conditions/dependency.d.ts +35 -6
  117. package/dist/conditions/dependency.d.ts.map +1 -1
  118. package/dist/conditions/dependency.js +299 -45
  119. package/dist/conditions/dependency.js.map +1 -1
  120. package/dist/conditions/exports.d.ts.map +1 -1
  121. package/dist/conditions/exports.js +10 -0
  122. package/dist/conditions/exports.js.map +1 -1
  123. package/dist/conditions/function.d.ts.map +1 -1
  124. package/dist/conditions/function.js +19 -5
  125. package/dist/conditions/function.js.map +1 -1
  126. package/dist/conditions/helpers.d.ts +2 -1
  127. package/dist/conditions/helpers.d.ts.map +1 -1
  128. package/dist/conditions/helpers.js +4 -1
  129. package/dist/conditions/helpers.js.map +1 -1
  130. package/dist/conditions/jsx.d.ts.map +1 -1
  131. package/dist/conditions/jsx.js +7 -2
  132. package/dist/conditions/jsx.js.map +1 -1
  133. package/dist/conditions/match-identity.d.ts +43 -0
  134. package/dist/conditions/match-identity.d.ts.map +1 -0
  135. package/dist/conditions/match-identity.js +43 -0
  136. package/dist/conditions/match-identity.js.map +1 -0
  137. package/dist/conditions/members.d.ts.map +1 -1
  138. package/dist/conditions/members.js +17 -4
  139. package/dist/conditions/members.js.map +1 -1
  140. package/dist/conditions/reverse-dependency.d.ts +18 -5
  141. package/dist/conditions/reverse-dependency.d.ts.map +1 -1
  142. package/dist/conditions/reverse-dependency.js +109 -92
  143. package/dist/conditions/reverse-dependency.js.map +1 -1
  144. package/dist/conditions/slice.d.ts +86 -2
  145. package/dist/conditions/slice.d.ts.map +1 -1
  146. package/dist/conditions/slice.js +234 -53
  147. package/dist/conditions/slice.js.map +1 -1
  148. package/dist/conditions/structural.d.ts.map +1 -1
  149. package/dist/conditions/structural.js +16 -4
  150. package/dist/conditions/structural.js.map +1 -1
  151. package/dist/core/check-all.d.ts +19 -0
  152. package/dist/core/check-all.d.ts.map +1 -0
  153. package/dist/core/check-all.js +96 -0
  154. package/dist/core/check-all.js.map +1 -0
  155. package/dist/core/combinators.d.ts +48 -0
  156. package/dist/core/combinators.d.ts.map +1 -0
  157. package/dist/core/combinators.js +56 -0
  158. package/dist/core/combinators.js.map +1 -0
  159. package/dist/core/descendant-cache.d.ts +36 -0
  160. package/dist/core/descendant-cache.d.ts.map +1 -0
  161. package/dist/core/descendant-cache.js +119 -0
  162. package/dist/core/descendant-cache.js.map +1 -0
  163. package/dist/core/diagnose.d.ts +234 -0
  164. package/dist/core/diagnose.d.ts.map +1 -0
  165. package/dist/core/diagnose.js +382 -0
  166. package/dist/core/diagnose.js.map +1 -0
  167. package/dist/core/disk-set.d.ts +48 -0
  168. package/dist/core/disk-set.d.ts.map +1 -0
  169. package/dist/core/disk-set.js +218 -0
  170. package/dist/core/disk-set.js.map +1 -0
  171. package/dist/core/element-cache.d.ts +75 -0
  172. package/dist/core/element-cache.d.ts.map +1 -0
  173. package/dist/core/element-cache.js +32 -0
  174. package/dist/core/element-cache.js.map +1 -0
  175. package/dist/core/empty-project-advice.d.ts +75 -0
  176. package/dist/core/empty-project-advice.d.ts.map +1 -0
  177. package/dist/core/empty-project-advice.js +61 -0
  178. package/dist/core/empty-project-advice.js.map +1 -0
  179. package/dist/core/exclusion-comments.d.ts +54 -0
  180. package/dist/core/exclusion-comments.d.ts.map +1 -0
  181. package/dist/core/exclusion-comments.js +310 -0
  182. package/dist/core/exclusion-comments.js.map +1 -0
  183. package/dist/core/execute-rule.d.ts +150 -0
  184. package/dist/core/execute-rule.d.ts.map +1 -0
  185. package/dist/core/execute-rule.js +580 -0
  186. package/dist/core/execute-rule.js.map +1 -0
  187. package/dist/core/glob-diagnosis.d.ts +78 -0
  188. package/dist/core/glob-diagnosis.d.ts.map +1 -0
  189. package/dist/core/glob-diagnosis.js +134 -0
  190. package/dist/core/glob-diagnosis.js.map +1 -0
  191. package/dist/core/glob-evaluator.d.ts +48 -0
  192. package/dist/core/glob-evaluator.d.ts.map +1 -0
  193. package/dist/core/glob-evaluator.js +89 -0
  194. package/dist/core/glob-evaluator.js.map +1 -0
  195. package/dist/core/import-candidates.d.ts +60 -0
  196. package/dist/core/import-candidates.d.ts.map +1 -0
  197. package/dist/core/import-candidates.js +93 -0
  198. package/dist/core/import-candidates.js.map +1 -0
  199. package/dist/core/import-options.d.ts +55 -2
  200. package/dist/core/import-options.d.ts.map +1 -1
  201. package/dist/core/import-options.js +64 -0
  202. package/dist/core/import-options.js.map +1 -1
  203. package/dist/core/line-index.d.ts +3 -0
  204. package/dist/core/line-index.d.ts.map +1 -0
  205. package/dist/core/line-index.js +229 -0
  206. package/dist/core/line-index.js.map +1 -0
  207. package/dist/core/metric-violation.d.ts +86 -0
  208. package/dist/core/metric-violation.d.ts.map +1 -0
  209. package/dist/core/metric-violation.js +132 -0
  210. package/dist/core/metric-violation.js.map +1 -0
  211. package/dist/core/module-edges.d.ts +361 -0
  212. package/dist/core/module-edges.d.ts.map +1 -0
  213. package/dist/core/module-edges.js +592 -0
  214. package/dist/core/module-edges.js.map +1 -0
  215. package/dist/core/object-literal-functions.d.ts +42 -0
  216. package/dist/core/object-literal-functions.d.ts.map +1 -0
  217. package/dist/core/object-literal-functions.js +75 -0
  218. package/dist/core/object-literal-functions.js.map +1 -0
  219. package/dist/core/orphan-exclusions.d.ts +44 -0
  220. package/dist/core/orphan-exclusions.d.ts.map +1 -0
  221. package/dist/core/orphan-exclusions.js +214 -0
  222. package/dist/core/orphan-exclusions.js.map +1 -0
  223. package/dist/core/pair-condition.d.ts +32 -3
  224. package/dist/core/pair-condition.d.ts.map +1 -1
  225. package/dist/core/path-universe.d.ts +54 -0
  226. package/dist/core/path-universe.d.ts.map +1 -0
  227. package/dist/core/path-universe.js +65 -0
  228. package/dist/core/path-universe.js.map +1 -0
  229. package/dist/core/per-root-compiler-options.d.ts +20 -0
  230. package/dist/core/per-root-compiler-options.d.ts.map +1 -0
  231. package/dist/core/per-root-compiler-options.js +57 -0
  232. package/dist/core/per-root-compiler-options.js.map +1 -0
  233. package/dist/core/project-relative.d.ts +98 -0
  234. package/dist/core/project-relative.d.ts.map +1 -0
  235. package/dist/core/project-relative.js +185 -0
  236. package/dist/core/project-relative.js.map +1 -0
  237. package/dist/core/project.d.ts +7 -1
  238. package/dist/core/project.d.ts.map +1 -1
  239. package/dist/core/project.js +24 -0
  240. package/dist/core/project.js.map +1 -1
  241. package/dist/core/rule-builder.d.ts +282 -0
  242. package/dist/core/rule-builder.d.ts.map +1 -0
  243. package/dist/core/rule-builder.js +445 -0
  244. package/dist/core/rule-builder.js.map +1 -0
  245. package/dist/core/rule-declaration.d.ts +32 -0
  246. package/dist/core/rule-declaration.d.ts.map +1 -0
  247. package/dist/core/rule-declaration.js +126 -0
  248. package/dist/core/rule-declaration.js.map +1 -0
  249. package/dist/core/terminal-builder.d.ts +625 -0
  250. package/dist/core/terminal-builder.d.ts.map +1 -0
  251. package/dist/core/terminal-builder.js +787 -0
  252. package/dist/core/terminal-builder.js.map +1 -0
  253. package/dist/core/terminal-execution.d.ts +23 -0
  254. package/dist/core/terminal-execution.d.ts.map +1 -0
  255. package/dist/core/terminal-execution.js +71 -0
  256. package/dist/core/terminal-execution.js.map +1 -0
  257. package/dist/core/vacuity-diagnosis.d.ts +103 -0
  258. package/dist/core/vacuity-diagnosis.d.ts.map +1 -0
  259. package/dist/core/vacuity-diagnosis.js +415 -0
  260. package/dist/core/vacuity-diagnosis.js.map +1 -0
  261. package/dist/core/violation.d.ts +16 -11
  262. package/dist/core/violation.d.ts.map +1 -1
  263. package/dist/core/violation.js +33 -13
  264. package/dist/core/violation.js.map +1 -1
  265. package/dist/graphql/index.js +1 -1
  266. package/dist/graphql/index.js.map +1 -1
  267. package/dist/graphql/resolver-rule-builder.d.ts +98 -23
  268. package/dist/graphql/resolver-rule-builder.d.ts.map +1 -1
  269. package/dist/graphql/resolver-rule-builder.js +151 -21
  270. package/dist/graphql/resolver-rule-builder.js.map +1 -1
  271. package/dist/graphql/schema-loader.d.ts +21 -0
  272. package/dist/graphql/schema-loader.d.ts.map +1 -1
  273. package/dist/graphql/schema-loader.js +81 -12
  274. package/dist/graphql/schema-loader.js.map +1 -1
  275. package/dist/graphql/schema-rule-builder.d.ts +57 -20
  276. package/dist/graphql/schema-rule-builder.d.ts.map +1 -1
  277. package/dist/graphql/schema-rule-builder.js +101 -25
  278. package/dist/graphql/schema-rule-builder.js.map +1 -1
  279. package/dist/helpers/baseline-diagnostics.d.ts +45 -0
  280. package/dist/helpers/baseline-diagnostics.d.ts.map +1 -0
  281. package/dist/helpers/baseline-diagnostics.js +168 -0
  282. package/dist/helpers/baseline-diagnostics.js.map +1 -0
  283. package/dist/helpers/baseline-generator.d.ts +19 -0
  284. package/dist/helpers/baseline-generator.d.ts.map +1 -0
  285. package/dist/helpers/baseline-generator.js +18 -0
  286. package/dist/helpers/baseline-generator.js.map +1 -0
  287. package/dist/helpers/baseline.d.ts +336 -0
  288. package/dist/helpers/baseline.d.ts.map +1 -0
  289. package/dist/helpers/baseline.js +664 -0
  290. package/dist/helpers/baseline.js.map +1 -0
  291. package/dist/helpers/body-traversal.d.ts +27 -2
  292. package/dist/helpers/body-traversal.d.ts.map +1 -1
  293. package/dist/helpers/body-traversal.js +122 -16
  294. package/dist/helpers/body-traversal.js.map +1 -1
  295. package/dist/helpers/callback-extractor.d.ts.map +1 -1
  296. package/dist/helpers/callback-extractor.js +48 -47
  297. package/dist/helpers/callback-extractor.js.map +1 -1
  298. package/dist/helpers/complexity.d.ts +13 -5
  299. package/dist/helpers/complexity.d.ts.map +1 -1
  300. package/dist/helpers/complexity.js +15 -6
  301. package/dist/helpers/complexity.js.map +1 -1
  302. package/dist/helpers/diff-aware.d.ts +44 -0
  303. package/dist/helpers/diff-aware.d.ts.map +1 -0
  304. package/dist/helpers/diff-aware.js +84 -0
  305. package/dist/helpers/diff-aware.js.map +1 -0
  306. package/dist/helpers/matchers.d.ts +68 -7
  307. package/dist/helpers/matchers.d.ts.map +1 -1
  308. package/dist/helpers/matchers.js +114 -20
  309. package/dist/helpers/matchers.js.map +1 -1
  310. package/dist/helpers/slice-graph.d.ts +100 -8
  311. package/dist/helpers/slice-graph.d.ts.map +1 -1
  312. package/dist/helpers/slice-graph.js +126 -34
  313. package/dist/helpers/slice-graph.js.map +1 -1
  314. package/dist/helpers/tarjan.d.ts +0 -14
  315. package/dist/helpers/tarjan.d.ts.map +1 -1
  316. package/dist/helpers/tarjan.js +29 -16
  317. package/dist/helpers/tarjan.js.map +1 -1
  318. package/dist/index.d.ts +56 -17
  319. package/dist/index.d.ts.map +1 -1
  320. package/dist/index.js +57 -13
  321. package/dist/index.js.map +1 -1
  322. package/dist/models/arch-call.d.ts +3 -2
  323. package/dist/models/arch-call.d.ts.map +1 -1
  324. package/dist/models/arch-call.js.map +1 -1
  325. package/dist/models/arch-function.d.ts +28 -6
  326. package/dist/models/arch-function.d.ts.map +1 -1
  327. package/dist/models/arch-function.js +106 -3
  328. package/dist/models/arch-function.js.map +1 -1
  329. package/dist/models/arch-jsx-element.js +2 -2
  330. package/dist/models/arch-jsx-element.js.map +1 -1
  331. package/dist/models/slice.d.ts +33 -9
  332. package/dist/models/slice.d.ts.map +1 -1
  333. package/dist/models/slice.js +129 -19
  334. package/dist/models/slice.js.map +1 -1
  335. package/dist/predicates/call.d.ts.map +1 -1
  336. package/dist/predicates/call.js +5 -0
  337. package/dist/predicates/call.js.map +1 -1
  338. package/dist/predicates/identity.d.ts +21 -0
  339. package/dist/predicates/identity.d.ts.map +1 -1
  340. package/dist/predicates/identity.js +75 -3
  341. package/dist/predicates/identity.js.map +1 -1
  342. package/dist/predicates/index.d.ts +1 -1
  343. package/dist/predicates/index.d.ts.map +1 -1
  344. package/dist/predicates/index.js +1 -1
  345. package/dist/predicates/index.js.map +1 -1
  346. package/dist/predicates/metrics.d.ts +4 -2
  347. package/dist/predicates/metrics.d.ts.map +1 -1
  348. package/dist/predicates/metrics.js +4 -2
  349. package/dist/predicates/metrics.js.map +1 -1
  350. package/dist/predicates/module.d.ts +4 -12
  351. package/dist/predicates/module.d.ts.map +1 -1
  352. package/dist/predicates/module.js +57 -37
  353. package/dist/predicates/module.js.map +1 -1
  354. package/dist/presets/agent-guardrails.d.ts +22 -15
  355. package/dist/presets/agent-guardrails.d.ts.map +1 -1
  356. package/dist/presets/agent-guardrails.js +124 -19
  357. package/dist/presets/agent-guardrails.js.map +1 -1
  358. package/dist/presets/boundaries.d.ts +45 -2
  359. package/dist/presets/boundaries.d.ts.map +1 -1
  360. package/dist/presets/boundaries.js +132 -23
  361. package/dist/presets/boundaries.js.map +1 -1
  362. package/dist/presets/data-layer.d.ts +22 -2
  363. package/dist/presets/data-layer.d.ts.map +1 -1
  364. package/dist/presets/data-layer.js +58 -16
  365. package/dist/presets/data-layer.js.map +1 -1
  366. package/dist/presets/index.d.ts +7 -5
  367. package/dist/presets/index.d.ts.map +1 -1
  368. package/dist/presets/index.js +9 -3
  369. package/dist/presets/index.js.map +1 -1
  370. package/dist/presets/layered.d.ts +46 -2
  371. package/dist/presets/layered.d.ts.map +1 -1
  372. package/dist/presets/layered.js +66 -29
  373. package/dist/presets/layered.js.map +1 -1
  374. package/dist/presets/recommended.d.ts +80 -9
  375. package/dist/presets/recommended.d.ts.map +1 -1
  376. package/dist/presets/recommended.js +25 -23
  377. package/dist/presets/recommended.js.map +1 -1
  378. package/dist/presets/shared.d.ts +270 -2
  379. package/dist/presets/shared.d.ts.map +1 -1
  380. package/dist/presets/shared.js +338 -4
  381. package/dist/presets/shared.js.map +1 -1
  382. package/dist/rules/code-quality.d.ts.map +1 -1
  383. package/dist/rules/code-quality.js +15 -6
  384. package/dist/rules/code-quality.js.map +1 -1
  385. package/dist/rules/hygiene.d.ts +1 -1
  386. package/dist/rules/hygiene.js +1 -1
  387. package/dist/rules/metrics-function.d.ts +2 -1
  388. package/dist/rules/metrics-function.d.ts.map +1 -1
  389. package/dist/rules/metrics-function.js +33 -6
  390. package/dist/rules/metrics-function.js.map +1 -1
  391. package/dist/rules/metrics.d.ts +2 -1
  392. package/dist/rules/metrics.d.ts.map +1 -1
  393. package/dist/rules/metrics.js +39 -9
  394. package/dist/rules/metrics.js.map +1 -1
  395. package/dist/smells/duplicate-bodies.d.ts +58 -5
  396. package/dist/smells/duplicate-bodies.d.ts.map +1 -1
  397. package/dist/smells/duplicate-bodies.js +137 -56
  398. package/dist/smells/duplicate-bodies.js.map +1 -1
  399. package/dist/smells/fingerprint.d.ts +8 -1
  400. package/dist/smells/fingerprint.d.ts.map +1 -1
  401. package/dist/smells/fingerprint.js +21 -3
  402. package/dist/smells/fingerprint.js.map +1 -1
  403. package/dist/smells/inconsistent-siblings.d.ts +128 -7
  404. package/dist/smells/inconsistent-siblings.d.ts.map +1 -1
  405. package/dist/smells/inconsistent-siblings.js +288 -86
  406. package/dist/smells/inconsistent-siblings.js.map +1 -1
  407. package/dist/smells/sibling-files.d.ts +47 -0
  408. package/dist/smells/sibling-files.d.ts.map +1 -0
  409. package/dist/smells/sibling-files.js +104 -0
  410. package/dist/smells/sibling-files.js.map +1 -0
  411. package/dist/smells/similar-pairs.d.ts +27 -0
  412. package/dist/smells/similar-pairs.d.ts.map +1 -0
  413. package/dist/smells/similar-pairs.js +52 -0
  414. package/dist/smells/similar-pairs.js.map +1 -0
  415. package/dist/smells/smell-builder.d.ts +57 -12
  416. package/dist/smells/smell-builder.d.ts.map +1 -1
  417. package/dist/smells/smell-builder.js +111 -14
  418. package/dist/smells/smell-builder.js.map +1 -1
  419. package/dist/tsconfig/index.d.ts +19 -0
  420. package/dist/tsconfig/index.d.ts.map +1 -0
  421. package/dist/tsconfig/index.js +20 -0
  422. package/dist/tsconfig/index.js.map +1 -0
  423. package/dist/tsconfig/tsconfig-builder.d.ts +42 -9
  424. package/dist/tsconfig/tsconfig-builder.d.ts.map +1 -1
  425. package/dist/tsconfig/tsconfig-builder.js +64 -16
  426. package/dist/tsconfig/tsconfig-builder.js.map +1 -1
  427. package/package.json +3 -2
  428. package/dist/core/module-dependencies.d.ts +0 -28
  429. package/dist/core/module-dependencies.d.ts.map +0 -1
  430. package/dist/core/module-dependencies.js +0 -35
  431. package/dist/core/module-dependencies.js.map +0 -1
  432. package/dist/helpers/within.d.ts.map +0 -1
  433. package/dist/helpers/within.js.map +0 -1
@@ -0,0 +1,178 @@
1
+ import type { ArchViolation } from '@nielspeter/eess';
2
+ /**
3
+ * Attribute findings that have no source location of their own to the rule file
4
+ * they came from.
5
+ *
6
+ * [ts-archunit Bug 0026](https://github.com/nielspeter/ts-archunit/blob/main/bugs/fixed/0026-a-location-less-finding-does-not-say-which-rule-file-it-came-from.md):
7
+ * a configuration finding carries `file: ''` because it reports a fault in the
8
+ * rule rather than in the code, so two identical vacuous rules in two rule files
9
+ * rendered as two identical paragraphs with nothing saying which to open. In a
10
+ * test the frame comes free from vitest; in the CLI — the golden-path default —
11
+ * nothing supplied it, even though this loop knows the file and was discarding it.
12
+ *
13
+ * `line: 1`, following `tsconfig()`'s precedent for a fault that belongs to a
14
+ * file rather than to a position in it (`docs/config-rules.md` documents that
15
+ * choice). The builders cannot know the line: a rule with no glob has no
16
+ * position anywhere, and the assertion-gate findings are exactly the rules that
17
+ * may have none. Not `line: 0` — `::error file=x,line=0` is not a valid GitHub
18
+ * annotation and gets dropped or misplaced.
19
+ *
20
+ * **Safe against a `eess-exclude` comment only because these findings carry
21
+ * `bypassFilters`.** `execute-rule.ts` filters comment exclusions with
22
+ * `v.bypassFilters === true || !isExcludedByComment(...)`, and that first clause
23
+ * is what stops a real path here from making the finding suppressible by a
24
+ * comment in the rule file. It was written for this exact temptation, and until
25
+ * this change nothing tested it, because no such finding had a readable path.
26
+ * `tests/helpers/exclusion-comments.test.ts` now pins it.
27
+ */
28
+ export declare function attributeToRuleFile(violations: readonly ArchViolation[], file: string): ArchViolation[];
29
+ /**
30
+ * A rule file, or one rule in it, that could not be evaluated at all.
31
+ *
32
+ * [ts-archunit Bug 0025](https://github.com/nielspeter/ts-archunit/blob/main/bugs/fixed/0025-a-non-archruleerror-from-one-rule-file-drops-every-other-finding.md):
33
+ * `runCheck` and `runBaseline` caught `ArchRuleError` and rethrew everything
34
+ * else, so any other error escaped the per-file loop and terminated the process
35
+ * — no report written, no exit code returned, and every finding already
36
+ * collected discarded. Measured: one malformed `crossProject()` in one file
37
+ * silenced a second file's four real violations, and printed a raw Node stack
38
+ * trace with `node_modules` paths in their place.
39
+ *
40
+ * Reported as a **configuration finding** (`bypassFilters`): a rule that could
41
+ * not run enforced nothing, which is not a violation of the rule and is not
42
+ * something to accept into a baseline. Same reasoning as the assertion gate,
43
+ * and it inherits the same treatment — `error` severity whatever the rule
44
+ * asked for, refused by `.excluding()`, skipped by diff and baseline.
45
+ *
46
+ * `line: 1` with the rule file as `file`, following `tsconfig()`'s precedent for
47
+ * a fault that belongs to a file rather than to a position in it. Not `line: 0`:
48
+ * `::error file=x,line=0` is not a valid GitHub annotation and gets dropped or
49
+ * misplaced (the v0.22.0 defect), and only a `file` of `''` takes the run-level
50
+ * branch that avoids it.
51
+ */
52
+ export declare function ruleFileFailure(file: string, error: unknown, ruleFiles: number): ArchViolation;
53
+ /**
54
+ * What to collect when evaluating a rule file, or one rule in it, threw.
55
+ *
56
+ * An `ArchRuleError` already carries findings — those ARE the report, and this
57
+ * is the pre-existing path for a rule file that self-executes a failing
58
+ * `.check()` at import. Anything else becomes one configuration finding naming
59
+ * the file. Never a rethrow: that discarded every finding already collected in
60
+ * the run, including other files' (bug 0025).
61
+ *
62
+ * One definition, imported by both `runCheck` and `runBaseline`. They had a copy
63
+ * each for the ArchRuleError half and the two had already diverged in what they
64
+ * did with everything else.
65
+ */
66
+ export declare function failureOrViolations(file: string, error: unknown, ruleFiles: number): ArchViolation[];
67
+ /**
68
+ * A rule file stopped evaluating partway, so the rules after that point never ran.
69
+ *
70
+ * [ts-archunit Bug 0029](https://github.com/nielspeter/ts-archunit/blob/main/bugs/fixed/0029-a-throwing-warn-truncates-the-rest-of-the-rule-file.md),
71
+ * and the half [ts-archunit plan 0069](https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0069-no-rule-may-certify-nothing.md)'s R3a
72
+ * specified and did not build: *"R3a states the semantics, and the CLI **reports the
73
+ * truncation rather than absorbing it**."* The semantics shipped in v0.23.0; this is
74
+ * the reporting.
75
+ *
76
+ * In a **self-executing** rule file — the shape `init` scaffolds and `docs/cli.md`
77
+ * documents — every rule calls its own terminal at module scope. A throw there aborts
78
+ * the module, so every rule declared after it is never evaluated. The CLI then folds
79
+ * the thrown finding into the run and the output looks entirely ordinary. Measured on
80
+ * v0.28.0, the same two rules in each style:
81
+ *
82
+ * | rule file shape | findings reported |
83
+ * | -------------------------------- | ----------------- |
84
+ * | `export default [rule1, rule2]` | **5** — the configuration finding and all four violations |
85
+ * | self-executing | **1** — the four violations silently absent |
86
+ *
87
+ * Worse than a crash, because the run is **red for the other finding**: someone fixes
88
+ * that, moves on, and never learns the rest of the file was skipped — or takes the
89
+ * sanctioned remedy for a dead glob, which is to delete the rule, and still never
90
+ * learns it.
91
+ *
92
+ * **What this can and cannot say.** The module never finished, so the CLI cannot know
93
+ * *what* was lost — only that anything after the throw did not run. Naming a count or
94
+ * a rule would be invention. It says the file stopped and what to do, which is all it
95
+ * has.
96
+ *
97
+ * `bypassFilters` because it reports missing coverage: nothing to grade, exclude or
98
+ * accept into a baseline.
99
+ */
100
+ export declare function ruleFileTruncated(file: string, ruleFiles: number): ArchViolation;
101
+ /**
102
+ * `--baseline` was passed, but the rule file printed findings the baseline never
103
+ * filtered. Bug 0199.
104
+ *
105
+ * **The mechanism, corrected TWICE. This is the third version and the measured one.**
106
+ *
107
+ * 1. *"The preset throws before baseline filtering runs."* False. `runCheck` collects
108
+ * a thrown terminal's violations off the error (`failureOrViolations`) and filters
109
+ * them; measured, its collection came back empty against a matching baseline.
110
+ * 2. *"`setCallerAggregatesReports` is module state that does not cross jiti's
111
+ * registry."* Also false, and it was the premise of a whole record. The flag is
112
+ * read at exactly ONE site — `core/execute-rule.ts`, inside `executeWarn`.
113
+ * And jiti is not even the default loader: `cli/import-rule-module.ts` imports
114
+ * natively first and reaches jiti only on a module-format refusal.
115
+ * (Line numbers deliberately omitted: the fix for (3) moved every one of them,
116
+ * and the first version of this docblock cited three that had already drifted.)
117
+ *
118
+ * 3. **What is actually true:** `executeCheck` calls `writeReport(...)`
119
+ * **unconditionally** at `core/execute-rule.ts`, one line before it throws.
120
+ * A `.check()` at module scope therefore prints its findings always — same
121
+ * registry, no jiti, no flag involved. The CLI cannot un-print them.
122
+ *
123
+ * The disproof of (2) is one line: a fixture importing the same module graph as the
124
+ * test's `runCheck`, so the flag was fully visible, printed all four violations
125
+ * anyway.
126
+ *
127
+ * **Why a notice and not the repair.** Making `executeCheck` honour the flag is a
128
+ * change to when a terminal emits — ADR-008 territory, and it would alter behaviour
129
+ * for every caller that relies on `.check()` printing, including test files. That is
130
+ * bug 0201. Until it is decided the run must not be silent about output it could not
131
+ * filter.
132
+ *
133
+ * **Fires only when something was actually printed.** `executeWarn` throws the same
134
+ * error type, and when the CLI aggregates it writes only non-`bypassFilters` entries
135
+ * — for a configuration finding, nothing at all. The first version fired on the bare
136
+ * `isArchRuleError` condition and so claimed a leak that never happened, pointing at
137
+ * a finding whose own text says a baseline can never suppress it. Asserting a cause
138
+ * the run cannot verify is the ADR-009 rule 2 defect `ruleFileFailure` names above.
139
+ *
140
+ * **What this can and cannot say.** The leaked lines were written before the CLI saw
141
+ * them, so it cannot know how many there were. Naming a count would be invention.
142
+ *
143
+ * `bypassFilters` because it reports a gap in filtering — accepting it into a
144
+ * baseline would suppress the notice that the baseline is not being applied.
145
+ */
146
+ export declare function baselineNotApplied(file: string, filters: {
147
+ baseline?: string;
148
+ changed: boolean;
149
+ }): ArchViolation;
150
+ /**
151
+ * A rule file loaded cleanly and contributed **zero** rules.
152
+ *
153
+ * `check` used to print `✓ eess-ts — 0 rules across 1 file · 0 failing` and exit 0
154
+ * over exactly this, while `doctor` on the same file already refused it with "no
155
+ * rules found in the given files". Two commands in one CLI disagreeing about
156
+ * whether "no rules" is an error, and the one wired into CI was the one blessing
157
+ * it.
158
+ *
159
+ * **The shape that makes it urgent** is a migration from `@nielspeter/ts-archunit`,
160
+ * whose `recommended()` returned builders unconditionally and had no `report`
161
+ * option. eess-ts's runs and throws by default, so the line ts-archunit's own
162
+ * `init` scaffolded — `export default [...recommended(p)]` — spreads a *violations*
163
+ * array. On a codebase with findings that fails loudly (the loader rejects a
164
+ * non-builder entry). **On a clean codebase the array is empty**, so the file
165
+ * exports `[]`, the CLI loads nothing, and the run is green with every rule gone.
166
+ * `tsc --noEmit` passes; nothing reaches stderr.
167
+ *
168
+ * The adopter this hits is the one who did the baseline work and cleaned up.
169
+ *
170
+ * `CLAUDE.md` already tells agents that a zero denominator "means the gate matched
171
+ * little or nothing — treat that as a red flag". This makes the CLI agree with its
172
+ * own instruction instead of printing `✓` over its alarm value.
173
+ *
174
+ * `bypassFilters` because it reports absent coverage: there is nothing to grade,
175
+ * exclude, or accept into a baseline.
176
+ */
177
+ export declare function ruleFileContributedNoRules(file: string): ArchViolation;
178
+ //# sourceMappingURL=rule-file-findings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-file-findings.d.ts","sourceRoot":"","sources":["../../src/cli/rule-file-findings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAIrD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,SAAS,aAAa,EAAE,EACpC,IAAI,EAAE,MAAM,GACX,aAAa,EAAE,CAEjB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,aAAa,CA0B9F;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,OAAO,EACd,SAAS,EAAE,MAAM,GAChB,aAAa,EAAE,CAEjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,aAAa,CAqBhF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC/C,aAAa,CAkDf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CAsBtE"}
@@ -0,0 +1,293 @@
1
+ import { isArchRuleError } from '@nielspeter/eess';
2
+ import { basename } from 'node:path';
3
+ /**
4
+ * Attribute findings that have no source location of their own to the rule file
5
+ * they came from.
6
+ *
7
+ * [ts-archunit Bug 0026](https://github.com/nielspeter/ts-archunit/blob/main/bugs/fixed/0026-a-location-less-finding-does-not-say-which-rule-file-it-came-from.md):
8
+ * a configuration finding carries `file: ''` because it reports a fault in the
9
+ * rule rather than in the code, so two identical vacuous rules in two rule files
10
+ * rendered as two identical paragraphs with nothing saying which to open. In a
11
+ * test the frame comes free from vitest; in the CLI — the golden-path default —
12
+ * nothing supplied it, even though this loop knows the file and was discarding it.
13
+ *
14
+ * `line: 1`, following `tsconfig()`'s precedent for a fault that belongs to a
15
+ * file rather than to a position in it (`docs/config-rules.md` documents that
16
+ * choice). The builders cannot know the line: a rule with no glob has no
17
+ * position anywhere, and the assertion-gate findings are exactly the rules that
18
+ * may have none. Not `line: 0` — `::error file=x,line=0` is not a valid GitHub
19
+ * annotation and gets dropped or misplaced.
20
+ *
21
+ * **Safe against a `eess-exclude` comment only because these findings carry
22
+ * `bypassFilters`.** `execute-rule.ts` filters comment exclusions with
23
+ * `v.bypassFilters === true || !isExcludedByComment(...)`, and that first clause
24
+ * is what stops a real path here from making the finding suppressible by a
25
+ * comment in the rule file. It was written for this exact temptation, and until
26
+ * this change nothing tested it, because no such finding had a readable path.
27
+ * `tests/helpers/exclusion-comments.test.ts` now pins it.
28
+ */
29
+ export function attributeToRuleFile(violations, file) {
30
+ return violations.map((v) => (v.file === '' ? { ...v, file, line: 1 } : v));
31
+ }
32
+ /**
33
+ * A rule file, or one rule in it, that could not be evaluated at all.
34
+ *
35
+ * [ts-archunit Bug 0025](https://github.com/nielspeter/ts-archunit/blob/main/bugs/fixed/0025-a-non-archruleerror-from-one-rule-file-drops-every-other-finding.md):
36
+ * `runCheck` and `runBaseline` caught `ArchRuleError` and rethrew everything
37
+ * else, so any other error escaped the per-file loop and terminated the process
38
+ * — no report written, no exit code returned, and every finding already
39
+ * collected discarded. Measured: one malformed `crossProject()` in one file
40
+ * silenced a second file's four real violations, and printed a raw Node stack
41
+ * trace with `node_modules` paths in their place.
42
+ *
43
+ * Reported as a **configuration finding** (`bypassFilters`): a rule that could
44
+ * not run enforced nothing, which is not a violation of the rule and is not
45
+ * something to accept into a baseline. Same reasoning as the assertion gate,
46
+ * and it inherits the same treatment — `error` severity whatever the rule
47
+ * asked for, refused by `.excluding()`, skipped by diff and baseline.
48
+ *
49
+ * `line: 1` with the rule file as `file`, following `tsconfig()`'s precedent for
50
+ * a fault that belongs to a file rather than to a position in it. Not `line: 0`:
51
+ * `::error file=x,line=0` is not a valid GitHub annotation and gets dropped or
52
+ * misplaced (the v0.22.0 defect), and only a `file` of `''` takes the run-level
53
+ * branch that avoids it.
54
+ */
55
+ // eess-exclude eess/no-unused-exports: consumed by the test suite; the build tsconfig this gate reads excludes tests, so `src` is the only usage it can see
56
+ export function ruleFileFailure(file, error, ruleFiles) {
57
+ const raw = error instanceof Error ? error.message : String(error);
58
+ // An error message may or may not end in punctuation, and the sentence after
59
+ // it ran straight on: "…(reading 'config') The other rule files…". Measured on
60
+ // the real CLI, which is the only place the two strings meet.
61
+ const detail = /[.!?]$/.test(raw.trimEnd()) ? raw.trimEnd() : `${raw.trimEnd()}.`;
62
+ const others = ruleFiles > 1 ? ' The other rule files in this run were still checked.' : '';
63
+ return {
64
+ // The path goes in `file` and nowhere else it would be re-rendered. It used
65
+ // to appear four times in one finding — in `rule`, in `element`, in the
66
+ // location line, and in the remedy — and the location line renders it
67
+ // through `path.relative(cwd, …)`, so a rule file outside the cwd printed as
68
+ // `../../../../../../private/tmp/…`. Measured on the real CLI.
69
+ rule: 'eess-ts: rule file',
70
+ element: basename(file),
71
+ file,
72
+ line: 1,
73
+ message: `This rule file could not be evaluated, so its rules enforced nothing in this run: ${detail}${others}`,
74
+ // Conditional, never asserted — this fires for any error a rule file or a
75
+ // builder can raise (a syntax error, a missing dependency, a misconfigured
76
+ // builder), and naming one cause for all of them is the ADR-009 rule 2
77
+ // defect. The error message above is the evidence; the builder sentence is
78
+ // offered as the common case that is fixable without touching source code.
79
+ suggestion: `Fix the error named above in this rule file. If it names a builder method — for example a crossProject() with the wrong number of .side(...) calls — the rule is misconfigured rather than violated, so the fix is in the rule file and not in the code it checks.`,
80
+ bypassFilters: true,
81
+ };
82
+ }
83
+ /**
84
+ * What to collect when evaluating a rule file, or one rule in it, threw.
85
+ *
86
+ * An `ArchRuleError` already carries findings — those ARE the report, and this
87
+ * is the pre-existing path for a rule file that self-executes a failing
88
+ * `.check()` at import. Anything else becomes one configuration finding naming
89
+ * the file. Never a rethrow: that discarded every finding already collected in
90
+ * the run, including other files' (bug 0025).
91
+ *
92
+ * One definition, imported by both `runCheck` and `runBaseline`. They had a copy
93
+ * each for the ArchRuleError half and the two had already diverged in what they
94
+ * did with everything else.
95
+ */
96
+ export function failureOrViolations(file, error, ruleFiles) {
97
+ return isArchRuleError(error) ? error.violations : [ruleFileFailure(file, error, ruleFiles)];
98
+ }
99
+ /**
100
+ * A rule file stopped evaluating partway, so the rules after that point never ran.
101
+ *
102
+ * [ts-archunit Bug 0029](https://github.com/nielspeter/ts-archunit/blob/main/bugs/fixed/0029-a-throwing-warn-truncates-the-rest-of-the-rule-file.md),
103
+ * and the half [ts-archunit plan 0069](https://github.com/nielspeter/ts-archunit/blob/main/plans/completed/0069-no-rule-may-certify-nothing.md)'s R3a
104
+ * specified and did not build: *"R3a states the semantics, and the CLI **reports the
105
+ * truncation rather than absorbing it**."* The semantics shipped in v0.23.0; this is
106
+ * the reporting.
107
+ *
108
+ * In a **self-executing** rule file — the shape `init` scaffolds and `docs/cli.md`
109
+ * documents — every rule calls its own terminal at module scope. A throw there aborts
110
+ * the module, so every rule declared after it is never evaluated. The CLI then folds
111
+ * the thrown finding into the run and the output looks entirely ordinary. Measured on
112
+ * v0.28.0, the same two rules in each style:
113
+ *
114
+ * | rule file shape | findings reported |
115
+ * | -------------------------------- | ----------------- |
116
+ * | `export default [rule1, rule2]` | **5** — the configuration finding and all four violations |
117
+ * | self-executing | **1** — the four violations silently absent |
118
+ *
119
+ * Worse than a crash, because the run is **red for the other finding**: someone fixes
120
+ * that, moves on, and never learns the rest of the file was skipped — or takes the
121
+ * sanctioned remedy for a dead glob, which is to delete the rule, and still never
122
+ * learns it.
123
+ *
124
+ * **What this can and cannot say.** The module never finished, so the CLI cannot know
125
+ * *what* was lost — only that anything after the throw did not run. Naming a count or
126
+ * a rule would be invention. It says the file stopped and what to do, which is all it
127
+ * has.
128
+ *
129
+ * `bypassFilters` because it reports missing coverage: nothing to grade, exclude or
130
+ * accept into a baseline.
131
+ */
132
+ export function ruleFileTruncated(file, ruleFiles) {
133
+ const others = ruleFiles > 1 ? ' The other rule files in this run were still checked.' : '';
134
+ return {
135
+ rule: 'eess-ts: rule file',
136
+ element: basename(file),
137
+ file,
138
+ line: 1,
139
+ message: `This rule file stopped evaluating at the finding above, so any rule declared after ` +
140
+ `that point never ran and its violations are not in this report. Rules that had already ` +
141
+ `run are reported normally.${others}`,
142
+ suggestion: `Fix the finding above, then re-run to see the rest of this file. To make the file ` +
143
+ `always evaluate in full: if its rules come from a preset, pass \`report: 'builders'\` — ` +
144
+ `e.g. \`recommended(p, { report: 'builders' })\` — which returns the builders instead of ` +
145
+ `running them at module scope. A spread like \`export default [...recommended(p)]\` does ` +
146
+ `NOT avoid this on its own, because the spread evaluates before the array exists. ` +
147
+ `Otherwise move the rules into \`export default [rule1, rule2]\` and drop the terminal ` +
148
+ `calls, so every rule is built before any of them runs.`,
149
+ bypassFilters: true,
150
+ };
151
+ }
152
+ /**
153
+ * `--baseline` was passed, but the rule file printed findings the baseline never
154
+ * filtered. Bug 0199.
155
+ *
156
+ * **The mechanism, corrected TWICE. This is the third version and the measured one.**
157
+ *
158
+ * 1. *"The preset throws before baseline filtering runs."* False. `runCheck` collects
159
+ * a thrown terminal's violations off the error (`failureOrViolations`) and filters
160
+ * them; measured, its collection came back empty against a matching baseline.
161
+ * 2. *"`setCallerAggregatesReports` is module state that does not cross jiti's
162
+ * registry."* Also false, and it was the premise of a whole record. The flag is
163
+ * read at exactly ONE site — `core/execute-rule.ts`, inside `executeWarn`.
164
+ * And jiti is not even the default loader: `cli/import-rule-module.ts` imports
165
+ * natively first and reaches jiti only on a module-format refusal.
166
+ * (Line numbers deliberately omitted: the fix for (3) moved every one of them,
167
+ * and the first version of this docblock cited three that had already drifted.)
168
+ *
169
+ * 3. **What is actually true:** `executeCheck` calls `writeReport(...)`
170
+ * **unconditionally** at `core/execute-rule.ts`, one line before it throws.
171
+ * A `.check()` at module scope therefore prints its findings always — same
172
+ * registry, no jiti, no flag involved. The CLI cannot un-print them.
173
+ *
174
+ * The disproof of (2) is one line: a fixture importing the same module graph as the
175
+ * test's `runCheck`, so the flag was fully visible, printed all four violations
176
+ * anyway.
177
+ *
178
+ * **Why a notice and not the repair.** Making `executeCheck` honour the flag is a
179
+ * change to when a terminal emits — ADR-008 territory, and it would alter behaviour
180
+ * for every caller that relies on `.check()` printing, including test files. That is
181
+ * bug 0201. Until it is decided the run must not be silent about output it could not
182
+ * filter.
183
+ *
184
+ * **Fires only when something was actually printed.** `executeWarn` throws the same
185
+ * error type, and when the CLI aggregates it writes only non-`bypassFilters` entries
186
+ * — for a configuration finding, nothing at all. The first version fired on the bare
187
+ * `isArchRuleError` condition and so claimed a leak that never happened, pointing at
188
+ * a finding whose own text says a baseline can never suppress it. Asserting a cause
189
+ * the run cannot verify is the ADR-009 rule 2 defect `ruleFileFailure` names above.
190
+ *
191
+ * **What this can and cannot say.** The leaked lines were written before the CLI saw
192
+ * them, so it cannot know how many there were. Naming a count would be invention.
193
+ *
194
+ * `bypassFilters` because it reports a gap in filtering — accepting it into a
195
+ * baseline would suppress the notice that the baseline is not being applied.
196
+ */
197
+ export function baselineNotApplied(file, filters) {
198
+ // Name the filters that were actually in play. `--baseline` is not the only one,
199
+ // and it is not always a FLAG — a config file can set it, and telling someone
200
+ // "`--baseline` was not applied" when they never typed it sends them looking for
201
+ // an argument they did not pass (adopter review of PR #74).
202
+ const names = [];
203
+ if (filters.baseline !== undefined)
204
+ names.push(`the baseline \`${basename(filters.baseline)}\``);
205
+ if (filters.changed)
206
+ names.push('`--changed` (diff-aware mode)');
207
+ const list = names.join(' and ');
208
+ return {
209
+ // NOT `eess-ts: rule file` — `dedupeConfigFindings` keys on
210
+ // `file + rule + element`, so sharing that label merges this into
211
+ // `ruleFileTruncated()` and the notice disappears. Measured: it did.
212
+ //
213
+ // NOT `eess-ts: baseline` either: it fires for `--changed` too, and three
214
+ // existing findings already use that label for baseline diagnostics.
215
+ //
216
+ // And NOT `eess-ts: filtering`, which it briefly was — across `docs/`
217
+ // "filtering" is the `.that()` predicate phase (`docs/core-concepts.md:91`,
218
+ // repeated on every dialect page), so a reader seeing it on a red build would
219
+ // most reasonably conclude their predicate was wrong. `reporting` is ADR-008's
220
+ // own noun for what this finding is actually about: who emitted, and when.
221
+ rule: 'eess-ts: reporting',
222
+ element: basename(file),
223
+ file,
224
+ line: 1,
225
+ message: `This rule file reported findings itself, so ${list} ` +
226
+ `${names.length > 1 ? 'were' : 'was'} NOT applied to them — anything it printed ` +
227
+ `reached you unfiltered, and findings you have already accepted or excluded can ` +
228
+ `appear as failures. The findings the CLI collected were filtered normally, which ` +
229
+ `is why the counts in this run disagree.`,
230
+ because: `A CLI-side filter can only act on findings the CLI collects. A rule file that ` +
231
+ `calls its own terminal at module scope prints them first, so the run you are ` +
232
+ `reading is not the run the filter describes.`,
233
+ // The remedy is ordered generic-first ON PURPOSE. This finding fires for ANY
234
+ // terminal throwing at module scope — a hand-written `.check()` with no preset
235
+ // in sight reaches it too — so naming the preset fix alone would be the ADR-009
236
+ // rule 2 defect its neighbour `ruleFileFailure` calls out by name.
237
+ suggestion: `Move this file's rules into \`export default [rule1, rule2]\` and drop the ` +
238
+ `terminal calls — an array export hands every rule to the CLI, which then owns ` +
239
+ `reporting and applies every filter to all of it. If the rules come from a ` +
240
+ `preset, pass \`report: 'builders'\` instead — e.g. ` +
241
+ `\`recommended(p, { report: 'builders' })\` — which returns the builders rather ` +
242
+ `than running them. \`eess-ts init\` scaffolds both forms correctly.`,
243
+ bypassFilters: true,
244
+ };
245
+ }
246
+ /**
247
+ * A rule file loaded cleanly and contributed **zero** rules.
248
+ *
249
+ * `check` used to print `✓ eess-ts — 0 rules across 1 file · 0 failing` and exit 0
250
+ * over exactly this, while `doctor` on the same file already refused it with "no
251
+ * rules found in the given files". Two commands in one CLI disagreeing about
252
+ * whether "no rules" is an error, and the one wired into CI was the one blessing
253
+ * it.
254
+ *
255
+ * **The shape that makes it urgent** is a migration from `@nielspeter/ts-archunit`,
256
+ * whose `recommended()` returned builders unconditionally and had no `report`
257
+ * option. eess-ts's runs and throws by default, so the line ts-archunit's own
258
+ * `init` scaffolded — `export default [...recommended(p)]` — spreads a *violations*
259
+ * array. On a codebase with findings that fails loudly (the loader rejects a
260
+ * non-builder entry). **On a clean codebase the array is empty**, so the file
261
+ * exports `[]`, the CLI loads nothing, and the run is green with every rule gone.
262
+ * `tsc --noEmit` passes; nothing reaches stderr.
263
+ *
264
+ * The adopter this hits is the one who did the baseline work and cleaned up.
265
+ *
266
+ * `CLAUDE.md` already tells agents that a zero denominator "means the gate matched
267
+ * little or nothing — treat that as a red flag". This makes the CLI agree with its
268
+ * own instruction instead of printing `✓` over its alarm value.
269
+ *
270
+ * `bypassFilters` because it reports absent coverage: there is nothing to grade,
271
+ * exclude, or accept into a baseline.
272
+ */
273
+ export function ruleFileContributedNoRules(file) {
274
+ return {
275
+ rule: 'eess-ts: rule file',
276
+ element: basename(file),
277
+ file,
278
+ line: 1,
279
+ message: `This rule file loaded without error but contributed no rules, so it enforced ` +
280
+ `nothing in this run. A green result here would mean "every rule passed" when ` +
281
+ `what happened is "there were no rules".`,
282
+ because: `A gate that checks nothing cannot fail, and a passing build is only evidence ` +
283
+ `if something was actually examined.`,
284
+ suggestion: `Check what this file's default export actually contains. The usual cause is a ` +
285
+ `preset spread without \`report: 'builders'\` — \`export default ` +
286
+ `[...recommended(p)]\` spreads the preset's RESULT, which on a codebase with no ` +
287
+ `violations is an empty array. Use \`recommended(p, { report: 'builders' })\`. ` +
288
+ `If the file is deliberately empty, delete it rather than leaving a rule file ` +
289
+ `that enforces nothing.`,
290
+ bypassFilters: true,
291
+ };
292
+ }
293
+ //# sourceMappingURL=rule-file-findings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-file-findings.js","sourceRoot":"","sources":["../../src/cli/rule-file-findings.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- export interface WatchOptions {
1
+ interface WatchOptions {
2
2
  /** Directories to watch for changes */
3
3
  watchDirs: string[];
4
4
  /** Additional files to watch (e.g., rule files) */
@@ -22,13 +22,17 @@ export declare class RunScheduler {
22
22
  private _pendingRerun;
23
23
  private readonly debounceMs;
24
24
  private readonly onRun;
25
- constructor(onRun: (trigger: string) => Promise<void>, debounceMs?: number);
25
+ /** Completed runs. */
26
26
  get runCount(): number;
27
+ constructor(onRun: (trigger: string) => Promise<void>, debounceMs?: number);
27
28
  get pendingRerun(): boolean;
28
29
  get isRunning(): boolean;
29
30
  /**
30
- * Queue a run for the given trigger, resetting the debounce window.
31
- * If a run is already in progress, marks a pending re-run instead.
31
+ * Queue a re-run, debounced.
32
+ *
33
+ * A run already in flight is not interrupted: the request is remembered and
34
+ * replayed when it finishes, so a save during a run is never dropped and
35
+ * never starts a second concurrent run.
32
36
  */
33
37
  schedule(trigger: string): void;
34
38
  private executeRun;
@@ -41,9 +45,18 @@ export declare class RunScheduler {
41
45
  * a run is in progress, it is queued and executed after the
42
46
  * current run completes.
43
47
  *
44
- * Known limitation: each re-run loads rule files with a fresh (cache-busting)
45
- * jiti instance. Over long sessions, memory grows — restart the watcher
46
- * periodically for long sessions.
48
+ * Known limitation: each re-run uses `importFresh` which creates
49
+ * a new ESM module cache entry. Over long sessions, memory grows.
50
+ * Restart the watcher periodically for long sessions.
47
51
  */
48
52
  export declare function watchAndRerun(options: WatchOptions): void;
53
+ /**
54
+ * Import a module with cache-busting for watch mode.
55
+ *
56
+ * Node ESM has no cache eviction API. Each call creates a new
57
+ * module entry via a unique query string. Over long sessions
58
+ * this leaks memory — see watchAndRerun JSDoc.
59
+ */
60
+ export declare function importFresh(filePath: string): Promise<unknown>;
61
+ export {};
49
62
  //# sourceMappingURL=watch.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/cli/watch.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,mDAAmD;IACnD,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,0CAA0C;IAC1C,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAGD,eAAO,MAAM,UAAU,QAAiB,CAAA;AAExC;;;;;GAKG;AAEH,qBAAa,YAAY;;IACvB,OAAO,CAAC,aAAa,CAA2C;IAChE,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoC;gBAG9C,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,SAAM;IAKvE,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAW/B,OAAO,CAAC,UAAU;CAwBnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAqDzD"}
1
+ {"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/cli/watch.ts"],"names":[],"mappings":"AAKA,UAAU,YAAY;IACpB,uCAAuC;IACvC,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,mDAAmD;IACnD,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,0CAA0C;IAC1C,gBAAgB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IACrC,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAGD,eAAO,MAAM,UAAU,QAAiB,CAAA;AAExC;;;;;GAKG;AAEH,qBAAa,YAAY;;IACvB,OAAO,CAAC,aAAa,CAA2C;IAChE,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoC;IAU1D,sBAAsB;IACtB,IAAI,QAAQ,IAAI,MAAM,CAErB;gBAEW,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,SAAM;IAKvE,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAW/B,OAAO,CAAC,UAAU;CAwBnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAsDzD;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAIpE"}
package/dist/cli/watch.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { watch } from 'node:fs/promises';
2
+ import { isNullaryCallable } from '@nielspeter/eess';
2
3
  import path from 'node:path';
3
- import { ArchRuleError } from '@nielspeter/eess';
4
- // eess-exclude eess/no-unused-exports: consumed by the test suite (tests are outside the build project)
4
+ import { isArchRuleError } from '@nielspeter/eess';
5
+ // eess-exclude eess/no-unused-exports: consumed by the test suite; the build tsconfig this gate reads excludes tests, so `src` is the only usage it can see
5
6
  export const TS_FILE_RE = /\.[cm]?tsx?$/;
6
7
  /**
7
8
  * Scheduling logic for watch mode — extracted for testability.
@@ -9,21 +10,29 @@ export const TS_FILE_RE = /\.[cm]?tsx?$/;
9
10
  * Debounces rapid triggers. If a trigger arrives while the callback
10
11
  * is running, it queues a re-run after the current run completes.
11
12
  */
12
- // eess-exclude eess/no-unused-exports: consumed by the test suite (tests are outside the build project)
13
+ // eess-exclude eess/no-unused-exports: consumed by the test suite; the build tsconfig this gate reads excludes tests, so `src` is the only usage it can see
13
14
  export class RunScheduler {
14
15
  debounceTimer;
15
16
  running = false;
16
17
  _pendingRerun = false;
17
18
  debounceMs;
18
19
  onRun;
20
+ /**
21
+ * How many runs have completed — read by the watch loop's own tests.
22
+ *
23
+ * Hard-private with a getter, matching `eess-mermaid`'s `RunScheduler`: the
24
+ * counter is this class's bookkeeping and a caller that could reassign it
25
+ * would make the number mean nothing.
26
+ */
19
27
  #runCount = 0;
28
+ /** Completed runs. */
29
+ get runCount() {
30
+ return this.#runCount;
31
+ }
20
32
  constructor(onRun, debounceMs = 250) {
21
33
  this.onRun = onRun;
22
34
  this.debounceMs = debounceMs;
23
35
  }
24
- get runCount() {
25
- return this.#runCount;
26
- }
27
36
  get pendingRerun() {
28
37
  return this._pendingRerun;
29
38
  }
@@ -31,8 +40,11 @@ export class RunScheduler {
31
40
  return this.running;
32
41
  }
33
42
  /**
34
- * Queue a run for the given trigger, resetting the debounce window.
35
- * If a run is already in progress, marks a pending re-run instead.
43
+ * Queue a re-run, debounced.
44
+ *
45
+ * A run already in flight is not interrupted: the request is remembered and
46
+ * replayed when it finishes, so a save during a run is never dropped and
47
+ * never starts a second concurrent run.
36
48
  */
37
49
  schedule(trigger) {
38
50
  if (this.debounceTimer)
@@ -54,7 +66,7 @@ export class RunScheduler {
54
66
  this.onRun(trigger)
55
67
  .catch((err) => {
56
68
  // Rule failures are expected — swallow ArchRuleError, print others
57
- if (!(err instanceof ArchRuleError)) {
69
+ if (!isArchRuleError(err)) {
58
70
  if (err instanceof Error) {
59
71
  console.error(err.message);
60
72
  }
@@ -79,9 +91,9 @@ export class RunScheduler {
79
91
  * a run is in progress, it is queued and executed after the
80
92
  * current run completes.
81
93
  *
82
- * Known limitation: each re-run loads rule files with a fresh (cache-busting)
83
- * jiti instance. Over long sessions, memory grows — restart the watcher
84
- * periodically for long sessions.
94
+ * Known limitation: each re-run uses `importFresh` which creates
95
+ * a new ESM module cache entry. Over long sessions, memory grows.
96
+ * Restart the watcher periodically for long sessions.
85
97
  */
86
98
  export function watchAndRerun(options) {
87
99
  const { watchDirs, watchFiles, onChangeDetected, debounceMs = 250 } = options;
@@ -129,10 +141,23 @@ export function watchAndRerun(options) {
129
141
  // Graceful shutdown — close all watchers on SIGINT
130
142
  process.on('SIGINT', () => {
131
143
  for (const w of watchers) {
132
- // Ask each async iterator to release its underlying fs.watch handle.
133
- void w[Symbol.asyncIterator]().return?.();
144
+ if ('return' in w && isNullaryCallable(w.return)) {
145
+ void w.return();
146
+ }
134
147
  }
135
148
  process.exit(0);
136
149
  });
137
150
  }
151
+ /**
152
+ * Import a module with cache-busting for watch mode.
153
+ *
154
+ * Node ESM has no cache eviction API. Each call creates a new
155
+ * module entry via a unique query string. Over long sessions
156
+ * this leaks memory — see watchAndRerun JSDoc.
157
+ */
158
+ export async function importFresh(filePath) {
159
+ const resolved = path.resolve(filePath);
160
+ const url = `file://${resolved}?t=${Date.now()}`;
161
+ return import(url);
162
+ }
138
163
  //# sourceMappingURL=watch.js.map