@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
@@ -26,4 +26,68 @@ export function isTypeOnlyImport(decl) {
26
26
  const specifiers = decl.getNamedImports();
27
27
  return specifiers.length > 0 && specifiers.every((s) => s.isTypeOnly());
28
28
  }
29
+ /**
30
+ * Check whether a re-export is purely type-only (no runtime dependency).
31
+ *
32
+ * The `export … from` analogue of {@link isTypeOnlyImport}, and **both halves of
33
+ * the disjunction are needed** — measured, the two spellings put the flag in
34
+ * different places:
35
+ *
36
+ * | form | `decl.isTypeOnly()` | specifiers |
37
+ * | ------------------------------- | ------------------- | ---------- |
38
+ * | `export type { X } from 's'` | **true** | false |
39
+ * | `export { type X } from 's'` | false | **true** |
40
+ * | `export type * from 's'` | **true** | (none) |
41
+ * | `export { X } from 's'` | false | false |
42
+ *
43
+ * No default or namespace analogue exists, verified: there is no way to write a
44
+ * re-export that binds a runtime value while every named specifier is
45
+ * type-only, so this needs none of {@link isTypeOnlyImport}'s guards. A bare
46
+ * `export * from 's'` has no named specifiers and is runtime, which is why the
47
+ * second half requires a non-empty list.
48
+ */
49
+ export function isTypeOnlyReExport(decl) {
50
+ if (decl.isTypeOnly())
51
+ return true;
52
+ const specifiers = decl.getNamedExports();
53
+ return specifiers.length > 0 && specifiers.every((s) => s.isTypeOnly());
54
+ }
55
+ /**
56
+ * Split a variadic glob API's arguments, without a type assertion.
57
+ *
58
+ * Five entry points take `...args: [string[], ImportOptions] | string[]` —
59
+ * `onlyImportFrom`, `notImportFrom`, `dependOn`, `importFrom`, `importFromAny` —
60
+ * so a caller can write either `f('a', 'b')` or `f(['a', 'b'], { … })`. TypeScript
61
+ * cannot narrow a tuple-union rest parameter from `Array.isArray(args[0])`, which
62
+ * is true and was the stated reason for **ten** `as` casts across three files:
63
+ *
64
+ * ```ts
65
+ * const globs: string[] = Array.isArray(args[0]) ? args[0] : (args as string[])
66
+ * const options = Array.isArray(args[0]) && args.length > 1 ? (args[1] as ImportOptions) : undefined
67
+ * ```
68
+ *
69
+ * [ADR-005](../../../../adr/005-no-any-no-type-assertions.md) prescribes a type guard,
70
+ * and a guard does narrow it: `.filter((a): a is string => …)` produces `string[]`
71
+ * with no assertion, and `typeof second === 'object'` narrows the options. The
72
+ * casts were avoidable; only the direct narrowing was not.
73
+ *
74
+ * One owner rather than five copies, which is the other half. The dispatch was
75
+ * duplicated five times, and a duplicated predicate in this repo has already
76
+ * caused a measurement error once —
77
+ * [ts-archunit bug 0044](https://github.com/nielspeter/ts-archunit/blob/main/bugs/fixed/0044-an-inline-exclusion-comment-has-no-feedback-channel.md).
78
+ */
79
+ export function splitGlobArgs(args) {
80
+ const [first, second] = args;
81
+ if (Array.isArray(first)) {
82
+ return {
83
+ globs: first,
84
+ options: typeof second === 'object' && second !== null && !Array.isArray(second)
85
+ ? second
86
+ : undefined,
87
+ };
88
+ }
89
+ // The variadic form: every argument is a glob string. The predicate is what
90
+ // makes this cast-free — `filter` alone would leave the union in place.
91
+ return { globs: args.filter((a) => typeof a === 'string'), options: undefined };
92
+ }
29
93
  //# sourceMappingURL=import-options.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"import-options.js","sourceRoot":"","sources":["../../src/core/import-options.ts"],"names":[],"mappings":"AAkBA;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAuB;IACtD,0DAA0D;IAC1D,IAAI,IAAI,CAAC,UAAU,EAAE;QAAE,OAAO,IAAI,CAAA;IAClC,2CAA2C;IAC3C,IAAI,IAAI,CAAC,gBAAgB,EAAE;QAAE,OAAO,KAAK,CAAA;IACzC,6CAA6C;IAC7C,IAAI,IAAI,CAAC,kBAAkB,EAAE;QAAE,OAAO,KAAK,CAAA;IAC3C,sDAAsD;IACtD,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe,EAAE,CAAA;IACzC,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAA;AACzE,CAAC"}
1
+ {"version":3,"file":"import-options.js","sourceRoot":"","sources":["../../src/core/import-options.ts"],"names":[],"mappings":"AAsBA;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAuB;IACtD,0DAA0D;IAC1D,IAAI,IAAI,CAAC,UAAU,EAAE;QAAE,OAAO,IAAI,CAAA;IAClC,2CAA2C;IAC3C,IAAI,IAAI,CAAC,gBAAgB,EAAE;QAAE,OAAO,KAAK,CAAA;IACzC,6CAA6C;IAC7C,IAAI,IAAI,CAAC,kBAAkB,EAAE;QAAE,OAAO,KAAK,CAAA;IAC3C,sDAAsD;IACtD,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe,EAAE,CAAA;IACzC,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAA;AACzE,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAuB;IACxD,IAAI,IAAI,CAAC,UAAU,EAAE;QAAE,OAAO,IAAI,CAAA;IAClC,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe,EAAE,CAAA;IACzC,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAA;AACzE,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,aAAa,CAAC,IAAoD;IAIhF,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,IAAI,CAAA;IAC5B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,OAAO,EACL,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;gBACrE,CAAC,CAAC,MAAM;gBACR,CAAC,CAAC,SAAS;SAChB,CAAA;IACH,CAAC;IACD,4EAA4E;IAC5E,wEAAwE;IACxE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAA;AAC9F,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Node } from 'ts-morph';
2
+ export declare function countCodeLines(node: Node): number;
3
+ //# sourceMappingURL=line-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"line-index.d.ts","sourceRoot":"","sources":["../../src/core/line-index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,UAAU,CAAA;AA8NhD,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAYjD"}
@@ -0,0 +1,229 @@
1
+ import { SyntaxKind } from 'ts-morph';
2
+ import { registerCacheReset } from '@nielspeter/eess';
3
+ /**
4
+ * The per-file line index behind `linesOfCode`.
5
+ *
6
+ * **Why this is in `core/` and not beside `linesOfCode` in `helpers/`.** It is a
7
+ * cache over ts-morph state, and this package already has three of those —
8
+ * `element-cache.ts`, `descendant-cache.ts`, `module-edges.ts` — each of which
9
+ * registers a reset and drops per-file entries on `onModified`. The first cut of
10
+ * this index lived in `helpers/`, followed neither convention, and reintroduced
11
+ * the exact defect the other three exist to document. Caches live here, next to
12
+ * their siblings, where the convention is visible.
13
+ */
14
+ /**
15
+ * Comment kinds ts-morph surfaces as ordinary leaves in `getChildren()`.
16
+ *
17
+ * The compiler treats these as trivia and omits them; ts-morph deliberately does
18
+ * not, which is useful everywhere else and is exactly what `linesOfCode` must
19
+ * undo. Measured: without this set a body of 4 code lines and 3 comment lines
20
+ * counted 7.
21
+ */
22
+ const NON_CODE_KINDS = new Set([
23
+ SyntaxKind.SingleLineCommentTrivia,
24
+ SyntaxKind.MultiLineCommentTrivia,
25
+ ]);
26
+ /**
27
+ * Count the lines of a node that actually carry code.
28
+ *
29
+ * Physical source lines, excluding comments and blank lines. A line holding only
30
+ * `}` still counts — it is a source line; this is not a statement count.
31
+ *
32
+ * **Why not the span.**
33
+ * [Bug 0170](../../../../work/bugs/0170-linesofcode-counts-comments-so-documentation-reads-as-size.md):
34
+ * this was `end - start + 1`, which measures documentation as size. In this repo
35
+ * that put two rules in direct conflict — `eess/jsdoc-on-public-methods` requires
36
+ * a doc block on every public method, and `eess/max-class-lines` then counted
37
+ * those blocks, so satisfying the first rule broke the second. Measured across
38
+ * this source, six of nine class findings and two of four method findings were
39
+ * comment lines alone.
40
+ *
41
+ * The old docstring justified the span by saying it "avoids the fragility of
42
+ * text-based comment stripping". That reason is sound and it is why this counts
43
+ * TOKENS rather than matching comment syntax in text: comments are trivia, so
44
+ * they are never tokens, and they drop out structurally. Nothing is stripped.
45
+ *
46
+ * Comments have to be skipped by hand even so: ts-morph does not hide them the
47
+ * way the compiler's own tree does — `getChildren()` hands back
48
+ * `SingleLineCommentTrivia` and `MultiLineCommentTrivia` as ordinary leaves, and
49
+ * JSDoc as a child node. JSDoc is the sharpest of the three, because
50
+ * `getStartLineNumber()` excludes it while `getChildren()` returns it, so
51
+ * counting it made an element measure LARGER than its own span —
52
+ * `complexity.test.ts` pins against that as a corpus-wide invariant.
53
+ */
54
+ /**
55
+ * Character offset of the first character of each line, ascending.
56
+ *
57
+ * Measured, this is the whole performance story. Walking every token in this
58
+ * repo's source costs 99ms and reading their POSITIONS costs 21ms — but asking
59
+ * ts-morph for their line NUMBERS costs 523ms, five times the walk. `getStart()`
60
+ * returns a number the AST already holds; `getStartLineNumber()` resolves it
61
+ * against the file's text on every call.
62
+ *
63
+ * So positions come from the AST and the offset-to-line mapping is done here,
64
+ * against a table built once per file. That mapping is pure arithmetic over
65
+ * newline offsets — it is not text-based comment detection, which would be
66
+ * genuinely fragile (a regex literal containing `//` cannot be told from a
67
+ * comment without parsing). Deciding what is a comment stays the AST's job.
68
+ */
69
+ let lineStartsByFile = new WeakMap();
70
+ function lineStartsOf(sourceFile) {
71
+ const cached = lineStartsByFile.get(sourceFile);
72
+ if (cached !== undefined)
73
+ return cached;
74
+ const text = sourceFile.getFullText();
75
+ const starts = [0];
76
+ for (let i = 0; i < text.length; i++) {
77
+ if (text.charCodeAt(i) === NEWLINE)
78
+ starts.push(i + 1);
79
+ }
80
+ lineStartsByFile.set(sourceFile, starts);
81
+ return starts;
82
+ }
83
+ const NEWLINE = 10;
84
+ /**
85
+ * The 1-based line holding a character offset.
86
+ *
87
+ * The count of line starts at or before the offset IS the line number, because
88
+ * the table begins with 0 for line 1.
89
+ */
90
+ function lineAt(lineStarts, pos) {
91
+ return countUpTo(lineStarts, pos);
92
+ }
93
+ /**
94
+ * Every line of a source file that carries at least one token, ascending.
95
+ *
96
+ * Computed once per file and cached. Per-subject walking re-visited a method's
97
+ * tokens for every enclosing rule — `maxClassLines` walks the class, then
98
+ * `maxMethodLines` walks each method inside it.
99
+ *
100
+ * Dropped when the file is modified, via the listener in {@link watchOnce}.
101
+ * **Object identity is not enough here and never was.** A `SourceFile`'s
102
+ * identity SURVIVES an edit — `module-edges.ts` measured it, `descendant-cache.ts`
103
+ * records the same trap one level down, and the first cut of this index asserted
104
+ * the opposite and shipped the third instance of it.
105
+ *
106
+ * The failure is not "returns the previous number". Positions come from the AST
107
+ * and stay fresh while the table goes stale, so the two are read against each
108
+ * other and the answer corresponds to nothing: measured, a class that grew from
109
+ * 5 code lines to 8 reported 6.
110
+ */
111
+ let codeLinesByFile = new WeakMap();
112
+ const watched = new WeakSet();
113
+ /**
114
+ * Join `resetProjectCache()`, the documented consumer escape hatch.
115
+ *
116
+ * **This registration is UNPROVEN, and saying so is the point.** Measured:
117
+ * emptying this callback leaves all 3,523 `packages/ts` tests green. That is not
118
+ * an oversight to fix with a cleverer test — it follows from what the
119
+ * registration is FOR. Correctness is already carried by the per-file
120
+ * `onModified` listener in {@link watchOnce}, which fires on every mutation path
121
+ * (and which IS proven — removing it reddens two rows in `complexity.test.ts`).
122
+ * What this adds is **memory reclamation on an explicit reset**: a consumer done
123
+ * with a large project can drop the tables without waiting for the `WeakMap` keys
124
+ * to become unreachable.
125
+ *
126
+ * A property with no observable correctness signature cannot have a break class,
127
+ * and this file's own rule two functions down — "a redundant guard is an
128
+ * unprovable one" — is why that is recorded here rather than left to look
129
+ * tested. Do not add a test that calls `resetProjectCache()` and asserts the next
130
+ * measurement is correct: it is correct either way, and such a row would pass
131
+ * over a deleted callback, which is worse than no row at all.
132
+ */
133
+ registerCacheReset(() => {
134
+ lineStartsByFile = new WeakMap();
135
+ codeLinesByFile = new WeakMap();
136
+ });
137
+ /**
138
+ * Register the per-file invalidation listener, once.
139
+ *
140
+ * Not once per cache miss: a watch session re-evaluating rules would otherwise
141
+ * accumulate a listener per rule execution. Both maps drop together, because the
142
+ * same edit invalidates both.
143
+ *
144
+ * The closure reads `lineStartsByFile` / `codeLinesByFile` as **live bindings**
145
+ * rather than capturing them, which is what keeps invalidation working after
146
+ * `registerCacheReset` replaces the maps — `descendant-cache.ts` records that
147
+ * capturing by value made a post-reset edit invisible and passed the suite.
148
+ *
149
+ * `onModified` fires on every mutation path that matters here — measured:
150
+ * `addMethod`, `replaceWithText`, and `createSourceFile(path, text,
151
+ * { overwrite: true })`, which is the fixture shape this repo's own guidance
152
+ * prescribes and the one that returned a wrong number for every case after the
153
+ * first.
154
+ */
155
+ function watchOnce(sourceFile) {
156
+ if (watched.has(sourceFile))
157
+ return;
158
+ watched.add(sourceFile);
159
+ sourceFile.onModified(() => {
160
+ lineStartsByFile.delete(sourceFile);
161
+ codeLinesByFile.delete(sourceFile);
162
+ });
163
+ }
164
+ function codeLinesOf(sourceFile) {
165
+ const cached = codeLinesByFile.get(sourceFile);
166
+ if (cached !== undefined)
167
+ return cached;
168
+ const lineStarts = lineStartsOf(sourceFile);
169
+ const lines = new Set();
170
+ const visit = (current) => {
171
+ if (NON_CODE_KINDS.has(current.getKind()))
172
+ return;
173
+ // JSDoc spans many kinds; the prefix is what they share.
174
+ if (current.getKindName().startsWith('JSDoc'))
175
+ return;
176
+ const children = current.getChildren();
177
+ if (children.length === 0) {
178
+ addTokenLines(lines, lineStarts, current);
179
+ return;
180
+ }
181
+ for (const child of children)
182
+ visit(child);
183
+ };
184
+ visit(sourceFile);
185
+ const sorted = [...lines].sort((a, b) => a - b);
186
+ codeLinesByFile.set(sourceFile, sorted);
187
+ return sorted;
188
+ }
189
+ /**
190
+ * Mark every line a leaf token occupies. Multi-line tokens exist — a template
191
+ * literal, a long string — and every line they cover is code.
192
+ */
193
+ function addTokenLines(lines, lineStarts, token) {
194
+ const first = lineAt(lineStarts, token.getStart());
195
+ // `getEnd()` is exclusive, so the last character is one before it. Clamped,
196
+ // because a zero-width node has `getEnd() === getStart()`.
197
+ const last = lineAt(lineStarts, Math.max(token.getStart(), token.getEnd() - 1));
198
+ for (let line = first; line <= last; line++)
199
+ lines.add(line);
200
+ }
201
+ /** How many entries of an ascending array are `<= value`. */
202
+ function countUpTo(sorted, value) {
203
+ let low = 0;
204
+ let high = sorted.length;
205
+ while (low < high) {
206
+ const mid = (low + high) >>> 1;
207
+ // `?? -Infinity` satisfies noUncheckedIndexedAccess without a non-null
208
+ // assertion (ADR-005); `mid` is always in range, so it never applies.
209
+ if ((sorted[mid] ?? -Infinity) <= value)
210
+ low = mid + 1;
211
+ else
212
+ high = mid;
213
+ }
214
+ return low;
215
+ }
216
+ export function countCodeLines(node) {
217
+ const sourceFile = node.getSourceFile();
218
+ // ONE registration, here rather than in each accessor. Both maps drop on the
219
+ // same event, so a call in either accessor made the other's redundant — and a
220
+ // redundant guard is an unprovable one: removing either alone left the suite
221
+ // green. At the single entry, removing it reddens the invalidation tests.
222
+ watchOnce(sourceFile);
223
+ const lineStarts = lineStartsOf(sourceFile);
224
+ const lines = codeLinesOf(sourceFile);
225
+ const first = lineAt(lineStarts, node.getStart());
226
+ const last = lineAt(lineStarts, Math.max(node.getStart(), node.getEnd() - 1));
227
+ return countUpTo(lines, last) - countUpTo(lines, first - 1);
228
+ }
229
+ //# sourceMappingURL=line-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"line-index.js","sourceRoot":"","sources":["../../src/core/line-index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import type { Node } from 'ts-morph';
2
+ import type { ArchViolation } from '@nielspeter/eess';
3
+ /**
4
+ * A finding whose message states a **measurement** — bug 0012.
5
+ *
6
+ * Metric conditions write the measured value into the message:
7
+ *
8
+ * Big has 10 methods (max: 5) — consider splitting into focused classes
9
+ *
10
+ * `hashViolation` identifies a violation by its message, so the identity moved
11
+ * whenever the measurement moved — **in either direction**. Baseline a class at
12
+ * 10 methods, delete two, and the finding was reported as new:
13
+ *
14
+ * 10 → 10 green ✓
15
+ * 10 → 12 red ✓ (worse)
16
+ * 10 → 8 RED ✗ (better — the defect)
17
+ * 10 → 5 green ✓ (under the threshold)
18
+ *
19
+ * Paying down the debt failed CI, and kept failing on every incremental step
20
+ * until the class dropped under the threshold entirely. A team that split four
21
+ * methods out of an 87-method service got a red build for its trouble. The
22
+ * external audit that motivated this recommends adopting these rules "behind a
23
+ * ratchet (accept today's god objects, block new regressions)" and records them
24
+ * at **zero uses** — the ratchet they were told to use did not work.
25
+ *
26
+ * ## Why identity alone cannot fix it
27
+ *
28
+ * Bug 0010 added `identity` for messages that state a derived population, and
29
+ * applying it naively here trades one failure for a worse one:
30
+ *
31
+ * | identity contains | improving to 8 | regressing to 12 |
32
+ * | ----------------- | -------------- | ---------------- |
33
+ * | the count | **red** ✗ | red ✓ |
34
+ * | no count | green ✓ | **green** ✗ |
35
+ *
36
+ * Dropping the count turns the baseline into a mute button. Identity answers
37
+ * "is this the same finding?", and a metric needs "is it **worse** than what we
38
+ * accepted?" — a comparison, not an equality. So this carries both: a stable
39
+ * identity that finds the entry, and `measured`, which the baseline stores and
40
+ * compares. See `Baseline.isKnown`.
41
+ *
42
+ * **Ten sites produce these, not the eight the bug enumerated.** That table was
43
+ * built by grepping the `has N <noun>` message shape, which misses the two
44
+ * complexity conditions — `has cyclomatic complexity N` puts the number last.
45
+ * Both were left on `createViolation` in the first cut of this fix while the
46
+ * docs already claimed complexity was ratcheted, so a reader would have removed
47
+ * their CI escape hatch for a gate that still fired on every improvement. Found
48
+ * by review; a mechanical enumeration by *shape* is not one by *behaviour*.
49
+ */
50
+ export declare function metricViolation(node: Node, options: {
51
+ /**
52
+ * What is being measured — `methods`, `lines`, `parameters`, `properties`,
53
+ * `named-exports`. Part of the identity, so one element can carry several
54
+ * metric findings without them colliding.
55
+ */
56
+ metric: string;
57
+ /** The measurement now. Compared against the baselined value, not equated. */
58
+ measured: number;
59
+ /**
60
+ * What `measured` COUNTS, when that is not simply the metric's name.
61
+ *
62
+ * The baseline persists this and refuses to compare across a change of unit
63
+ * ([bug 0171](../../../../work/bugs/0171-a-metric-unit-change-silently-loosens-every-baselined-ratchet.md)).
64
+ * `lines` is why it exists: the metric kept its name while `linesOfCode`
65
+ * changed from counting a span to counting code, so identity still matched
66
+ * and every baselined ceiling silently tripled. A metric whose name already
67
+ * says what it counts — `methods`, `parameters` — needs nothing here.
68
+ */
69
+ unit?: string;
70
+ message: string;
71
+ /**
72
+ * The element's qualified name, when `getElementName` would under-qualify it.
73
+ *
74
+ * Members need this: `getElementName(member)` returns the bare `save`, while
75
+ * the message already says `UserRepo.save`. Two classes with a `save` method
76
+ * would otherwise be one entry.
77
+ */
78
+ qualifiedName?: string;
79
+ }, context: {
80
+ rule: string;
81
+ because?: string;
82
+ suggestion?: string;
83
+ ruleId?: string;
84
+ docs?: string;
85
+ }): ArchViolation;
86
+ //# sourceMappingURL=metric-violation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metric-violation.d.ts","sourceRoot":"","sources":["../../src/core/metric-violation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAA;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAGrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,IAAI,EACV,OAAO,EAAE;IACP;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAA;IACd,8EAA8E;IAC9E,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,EACD,OAAO,EAAE;IACP,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,GACA,aAAa,CAkCf"}
@@ -0,0 +1,132 @@
1
+ import { createViolation, enclosingScopeName, getElementName } from './violation.js';
2
+ /**
3
+ * A finding whose message states a **measurement** — bug 0012.
4
+ *
5
+ * Metric conditions write the measured value into the message:
6
+ *
7
+ * Big has 10 methods (max: 5) — consider splitting into focused classes
8
+ *
9
+ * `hashViolation` identifies a violation by its message, so the identity moved
10
+ * whenever the measurement moved — **in either direction**. Baseline a class at
11
+ * 10 methods, delete two, and the finding was reported as new:
12
+ *
13
+ * 10 → 10 green ✓
14
+ * 10 → 12 red ✓ (worse)
15
+ * 10 → 8 RED ✗ (better — the defect)
16
+ * 10 → 5 green ✓ (under the threshold)
17
+ *
18
+ * Paying down the debt failed CI, and kept failing on every incremental step
19
+ * until the class dropped under the threshold entirely. A team that split four
20
+ * methods out of an 87-method service got a red build for its trouble. The
21
+ * external audit that motivated this recommends adopting these rules "behind a
22
+ * ratchet (accept today's god objects, block new regressions)" and records them
23
+ * at **zero uses** — the ratchet they were told to use did not work.
24
+ *
25
+ * ## Why identity alone cannot fix it
26
+ *
27
+ * Bug 0010 added `identity` for messages that state a derived population, and
28
+ * applying it naively here trades one failure for a worse one:
29
+ *
30
+ * | identity contains | improving to 8 | regressing to 12 |
31
+ * | ----------------- | -------------- | ---------------- |
32
+ * | the count | **red** ✗ | red ✓ |
33
+ * | no count | green ✓ | **green** ✗ |
34
+ *
35
+ * Dropping the count turns the baseline into a mute button. Identity answers
36
+ * "is this the same finding?", and a metric needs "is it **worse** than what we
37
+ * accepted?" — a comparison, not an equality. So this carries both: a stable
38
+ * identity that finds the entry, and `measured`, which the baseline stores and
39
+ * compares. See `Baseline.isKnown`.
40
+ *
41
+ * **Ten sites produce these, not the eight the bug enumerated.** That table was
42
+ * built by grepping the `has N <noun>` message shape, which misses the two
43
+ * complexity conditions — `has cyclomatic complexity N` puts the number last.
44
+ * Both were left on `createViolation` in the first cut of this fix while the
45
+ * docs already claimed complexity was ratcheted, so a reader would have removed
46
+ * their CI escape hatch for a gate that still fired on every improvement. Found
47
+ * by review; a mechanical enumeration by *shape* is not one by *behaviour*.
48
+ */
49
+ export function metricViolation(node, options, context) {
50
+ const base = createViolation(node, options.message, context);
51
+ return {
52
+ ...base,
53
+ // The qualified name reaches `element` too, not only `identity` — bug 0068's
54
+ // first consequence. `createViolation` derives it with `getElementName`,
55
+ // which resolves an unnamed node up to its nearest named ancestor, so a
56
+ // finding ABOUT an object-literal function was labelled with the ENCLOSING
57
+ // function's name while its own message named it correctly. `element` is
58
+ // what the terminal prints, what the JSON reports, and one of the three
59
+ // fields string-form `.excluding()` matches by exact membership
60
+ // (`execute-rule.ts`), so the disagreement also made an exclusion written
61
+ // against the printed name silently miss.
62
+ element: options.qualifiedName ?? base.element,
63
+ // File, element and metric — never the value. The value is the thing being
64
+ // ratcheted, so putting it here makes every change a new finding, which is
65
+ // the bug. Leaving the FILE out is the other half, and it was shipped once:
66
+ // two classes named `Big` in different files produced one identity, one
67
+ // hash, and `withBaseline`'s last-write-wins picked whichever ceiling came
68
+ // last — measured, a real 10 → 15 regression was silently accepted while
69
+ // the sibling sat at 20. That is bug 0028's shape recreated inside bug
70
+ // 0012's fix, and `ArchViolation.identity`'s own contract forbids it: "two
71
+ // distinct violations sharing one identity are one violation to the
72
+ // baseline, and accepting either accepts both."
73
+ //
74
+ // A path here is portable: `hashViolation` scrubs identity through
75
+ // `normalizeIdentityText(text, root)`, which is what that scrub is for.
76
+ identity: `${node.getSourceFile().getFilePath()}::${identityName(node, options.qualifiedName)}::${options.metric}`,
77
+ measured: options.measured,
78
+ // Defaults to the metric's own name: for `methods` or `parameters` the name
79
+ // IS the unit, and only a metric that can change what it counts under a
80
+ // stable name needs to say so explicitly.
81
+ measuredUnit: options.unit ?? options.metric,
82
+ };
83
+ }
84
+ /**
85
+ * The name segment of a metric identity: the subject's own name, **prefixed by the
86
+ * name of whatever encloses it**, so that two subjects sharing a name in one file
87
+ * are still two identities.
88
+ *
89
+ * Measured, across every function shape — neither name alone is sufficient:
90
+ *
91
+ * | shape | own name | enclosing scope | identity segment |
92
+ * | ------------------------------------ | ------------------- | --------------- | ------------------------- |
93
+ * | two factories returning `{build}` | `build`, `build` | `makeBeta`, `makeGamma` | `makeBeta.build`, … |
94
+ * | an arrow inside a named function | `errorResponseBuilder` | `makeAlpha` | `makeAlpha.errorResponseBuilder` |
95
+ * | a top-level `function takesFive` | `takesFive` | none | `takesFive` — unchanged |
96
+ * | a class method via `functions()` | `Repo.save` | `Repo` | `Repo.save` — already carries it |
97
+ *
98
+ * Two mistakes are recorded here because both shipped and both were found by
99
+ * review rather than by reasoning:
100
+ *
101
+ * 1. **The own name alone** (bug 0068's first fix) moved the collision instead of
102
+ * closing it: `owningBindingName` deliberately declines to prefix a literal
103
+ * returned from a factory, so both `build`s are just `build`.
104
+ * 2. **`getElementName` is not a scope.** It returns the node's OWN name when the
105
+ * node has one and only walks ancestors otherwise — so for method shorthand
106
+ * (`{ build() {} }`, a `MethodDeclaration`) it returned `build`, the prefix was
107
+ * skipped, and the fix worked for the arrow spelling of the same code and not
108
+ * for this one. The scope now comes from `enclosingScopeName`, which always
109
+ * walks from the parent.
110
+ *
111
+ * There is deliberately **no `own === scope` short-circuit**: a nested function
112
+ * whose name equals its enclosing function's (`function save() { return { save:
113
+ * … } }`) is exactly the case that needs the prefix, and equality of strings
114
+ * cannot tell "the scope is me" from "the scope happens to share my name".
115
+ *
116
+ * Known limit, stated rather than papered over: a literal in a **call argument**
117
+ * at module level (`register({ handler: … })`) has no enclosing named
118
+ * declaration, so two of them in one file still share an identity. Nothing stable
119
+ * distinguishes them — that is bug 0067's territory ("there is no right name
120
+ * yet"), not this one's.
121
+ */
122
+ function identityName(node, qualifiedName) {
123
+ const own = qualifiedName ?? getElementName(node);
124
+ const scope = enclosingScopeName(node);
125
+ if (scope === undefined)
126
+ return own;
127
+ // Already carries it — a class method's `Repo.save` under scope `Repo`.
128
+ if (own.startsWith(`${scope}.`) || own.endsWith(`.${scope}`))
129
+ return own;
130
+ return `${scope}.${own}`;
131
+ }
132
+ //# sourceMappingURL=metric-violation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metric-violation.js","sourceRoot":"","sources":["../../src/core/metric-violation.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEpF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAU,EACV,OA6BC,EACD,OAMC;IAED,MAAM,IAAI,GAAG,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAC5D,OAAO;QACL,GAAG,IAAI;QACP,6EAA6E;QAC7E,yEAAyE;QACzE,wEAAwE;QACxE,2EAA2E;QAC3E,yEAAyE;QACzE,wEAAwE;QACxE,gEAAgE;QAChE,0EAA0E;QAC1E,0CAA0C;QAC1C,OAAO,EAAE,OAAO,CAAC,aAAa,IAAI,IAAI,CAAC,OAAO;QAC9C,2EAA2E;QAC3E,2EAA2E;QAC3E,4EAA4E;QAC5E,wEAAwE;QACxE,2EAA2E;QAC3E,yEAAyE;QACzE,uEAAuE;QACvE,2EAA2E;QAC3E,oEAAoE;QACpE,gDAAgD;QAChD,EAAE;QACF,mEAAmE;QACnE,wEAAwE;QACxE,QAAQ,EAAE,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC,WAAW,EAAE,KAAK,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC,KAAK,OAAO,CAAC,MAAM,EAAE;QAClH,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,4EAA4E;QAC5E,wEAAwE;QACxE,0CAA0C;QAC1C,YAAY,EAAE,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,MAAM;KAC7C,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,SAAS,YAAY,CAAC,IAAU,EAAE,aAAiC;IACjE,MAAM,GAAG,GAAG,aAAa,IAAI,cAAc,CAAC,IAAI,CAAC,CAAA;IACjD,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAA;IACtC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,GAAG,CAAA;IACnC,wEAAwE;IACxE,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,KAAK,GAAG,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,KAAK,EAAE,CAAC;QAAE,OAAO,GAAG,CAAA;IACxE,OAAO,GAAG,KAAK,IAAI,GAAG,EAAE,CAAA;AAC1B,CAAC"}