@nielspeter/eess-ts 0.1.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 (371) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +364 -0
  3. package/dist/builders/call-rule-builder.d.ts +199 -0
  4. package/dist/builders/call-rule-builder.d.ts.map +1 -0
  5. package/dist/builders/call-rule-builder.js +254 -0
  6. package/dist/builders/call-rule-builder.js.map +1 -0
  7. package/dist/builders/class-rule-builder.d.ts +136 -0
  8. package/dist/builders/class-rule-builder.d.ts.map +1 -0
  9. package/dist/builders/class-rule-builder.js +253 -0
  10. package/dist/builders/class-rule-builder.js.map +1 -0
  11. package/dist/builders/cross-layer-builder.d.ts +96 -0
  12. package/dist/builders/cross-layer-builder.d.ts.map +1 -0
  13. package/dist/builders/cross-layer-builder.js +172 -0
  14. package/dist/builders/cross-layer-builder.js.map +1 -0
  15. package/dist/builders/function-rule-builder.d.ts +198 -0
  16. package/dist/builders/function-rule-builder.d.ts.map +1 -0
  17. package/dist/builders/function-rule-builder.js +287 -0
  18. package/dist/builders/function-rule-builder.js.map +1 -0
  19. package/dist/builders/jsx-rule-builder.d.ts +113 -0
  20. package/dist/builders/jsx-rule-builder.d.ts.map +1 -0
  21. package/dist/builders/jsx-rule-builder.js +150 -0
  22. package/dist/builders/jsx-rule-builder.js.map +1 -0
  23. package/dist/builders/module-rule-builder.d.ts +159 -0
  24. package/dist/builders/module-rule-builder.d.ts.map +1 -0
  25. package/dist/builders/module-rule-builder.js +237 -0
  26. package/dist/builders/module-rule-builder.js.map +1 -0
  27. package/dist/builders/scoped-function-rule-builder.d.ts +21 -0
  28. package/dist/builders/scoped-function-rule-builder.d.ts.map +1 -0
  29. package/dist/builders/scoped-function-rule-builder.js +26 -0
  30. package/dist/builders/scoped-function-rule-builder.js.map +1 -0
  31. package/dist/builders/slice-rule-builder.d.ts +85 -0
  32. package/dist/builders/slice-rule-builder.d.ts.map +1 -0
  33. package/dist/builders/slice-rule-builder.js +134 -0
  34. package/dist/builders/slice-rule-builder.js.map +1 -0
  35. package/dist/builders/type-rule-builder.d.ts +103 -0
  36. package/dist/builders/type-rule-builder.d.ts.map +1 -0
  37. package/dist/builders/type-rule-builder.js +170 -0
  38. package/dist/builders/type-rule-builder.js.map +1 -0
  39. package/dist/cli/bin.d.ts +3 -0
  40. package/dist/cli/bin.d.ts.map +1 -0
  41. package/dist/cli/bin.js +4 -0
  42. package/dist/cli/bin.js.map +1 -0
  43. package/dist/cli/commands/baseline.d.ts +11 -0
  44. package/dist/cli/commands/baseline.d.ts.map +1 -0
  45. package/dist/cli/commands/baseline.js +16 -0
  46. package/dist/cli/commands/baseline.js.map +1 -0
  47. package/dist/cli/commands/check.d.ts +21 -0
  48. package/dist/cli/commands/check.d.ts.map +1 -0
  49. package/dist/cli/commands/check.js +94 -0
  50. package/dist/cli/commands/check.js.map +1 -0
  51. package/dist/cli/commands/explain.d.ts +9 -0
  52. package/dist/cli/commands/explain.d.ts.map +1 -0
  53. package/dist/cli/commands/explain.js +49 -0
  54. package/dist/cli/commands/explain.js.map +1 -0
  55. package/dist/cli/config.d.ts +31 -0
  56. package/dist/cli/config.d.ts.map +1 -0
  57. package/dist/cli/config.js +20 -0
  58. package/dist/cli/config.js.map +1 -0
  59. package/dist/cli/index.d.ts +21 -0
  60. package/dist/cli/index.d.ts.map +1 -0
  61. package/dist/cli/index.js +168 -0
  62. package/dist/cli/index.js.map +1 -0
  63. package/dist/cli/load-rules.d.ts +20 -0
  64. package/dist/cli/load-rules.d.ts.map +1 -0
  65. package/dist/cli/load-rules.js +97 -0
  66. package/dist/cli/load-rules.js.map +1 -0
  67. package/dist/cli/resolve-config.d.ts +11 -0
  68. package/dist/cli/resolve-config.d.ts.map +1 -0
  69. package/dist/cli/resolve-config.js +61 -0
  70. package/dist/cli/resolve-config.js.map +1 -0
  71. package/dist/cli/watch.d.ts +49 -0
  72. package/dist/cli/watch.d.ts.map +1 -0
  73. package/dist/cli/watch.js +138 -0
  74. package/dist/cli/watch.js.map +1 -0
  75. package/dist/conditions/body-analysis-function.d.ts +23 -0
  76. package/dist/conditions/body-analysis-function.d.ts.map +1 -0
  77. package/dist/conditions/body-analysis-function.js +103 -0
  78. package/dist/conditions/body-analysis-function.js.map +1 -0
  79. package/dist/conditions/body-analysis-module.d.ts +27 -0
  80. package/dist/conditions/body-analysis-module.d.ts.map +1 -0
  81. package/dist/conditions/body-analysis-module.js +99 -0
  82. package/dist/conditions/body-analysis-module.js.map +1 -0
  83. package/dist/conditions/body-analysis.d.ts +34 -0
  84. package/dist/conditions/body-analysis.d.ts.map +1 -0
  85. package/dist/conditions/body-analysis.js +95 -0
  86. package/dist/conditions/body-analysis.js.map +1 -0
  87. package/dist/conditions/call.d.ts +62 -0
  88. package/dist/conditions/call.d.ts.map +1 -0
  89. package/dist/conditions/call.js +267 -0
  90. package/dist/conditions/call.js.map +1 -0
  91. package/dist/conditions/catch-analysis.d.ts +31 -0
  92. package/dist/conditions/catch-analysis.d.ts.map +1 -0
  93. package/dist/conditions/catch-analysis.js +78 -0
  94. package/dist/conditions/catch-analysis.js.map +1 -0
  95. package/dist/conditions/class.d.ts +33 -0
  96. package/dist/conditions/class.d.ts.map +1 -0
  97. package/dist/conditions/class.js +110 -0
  98. package/dist/conditions/class.js.map +1 -0
  99. package/dist/conditions/cross-layer.d.ts +28 -0
  100. package/dist/conditions/cross-layer.d.ts.map +1 -0
  101. package/dist/conditions/cross-layer.js +105 -0
  102. package/dist/conditions/cross-layer.js.map +1 -0
  103. package/dist/conditions/dependency.d.ts +73 -0
  104. package/dist/conditions/dependency.d.ts.map +1 -0
  105. package/dist/conditions/dependency.js +177 -0
  106. package/dist/conditions/dependency.js.map +1 -0
  107. package/dist/conditions/exports.d.ts +33 -0
  108. package/dist/conditions/exports.d.ts.map +1 -0
  109. package/dist/conditions/exports.js +101 -0
  110. package/dist/conditions/exports.js.map +1 -0
  111. package/dist/conditions/function.d.ts +79 -0
  112. package/dist/conditions/function.d.ts.map +1 -0
  113. package/dist/conditions/function.js +167 -0
  114. package/dist/conditions/function.js.map +1 -0
  115. package/dist/conditions/helpers.d.ts +14 -0
  116. package/dist/conditions/helpers.d.ts.map +1 -0
  117. package/dist/conditions/helpers.js +26 -0
  118. package/dist/conditions/helpers.js.map +1 -0
  119. package/dist/conditions/jsx.d.ts +40 -0
  120. package/dist/conditions/jsx.d.ts.map +1 -0
  121. package/dist/conditions/jsx.js +133 -0
  122. package/dist/conditions/jsx.js.map +1 -0
  123. package/dist/conditions/members.d.ts +74 -0
  124. package/dist/conditions/members.d.ts.map +1 -0
  125. package/dist/conditions/members.js +224 -0
  126. package/dist/conditions/members.js.map +1 -0
  127. package/dist/conditions/pattern.d.ts +20 -0
  128. package/dist/conditions/pattern.d.ts.map +1 -0
  129. package/dist/conditions/pattern.js +108 -0
  130. package/dist/conditions/pattern.js.map +1 -0
  131. package/dist/conditions/reverse-dependency.d.ts +28 -0
  132. package/dist/conditions/reverse-dependency.d.ts.map +1 -0
  133. package/dist/conditions/reverse-dependency.js +275 -0
  134. package/dist/conditions/reverse-dependency.js.map +1 -0
  135. package/dist/conditions/slice.d.ts +49 -0
  136. package/dist/conditions/slice.d.ts.map +1 -0
  137. package/dist/conditions/slice.js +153 -0
  138. package/dist/conditions/slice.js.map +1 -0
  139. package/dist/conditions/structural.d.ts +58 -0
  140. package/dist/conditions/structural.d.ts.map +1 -0
  141. package/dist/conditions/structural.js +102 -0
  142. package/dist/conditions/structural.js.map +1 -0
  143. package/dist/conditions/type-level.d.ts +29 -0
  144. package/dist/conditions/type-level.d.ts.map +1 -0
  145. package/dist/conditions/type-level.js +48 -0
  146. package/dist/conditions/type-level.js.map +1 -0
  147. package/dist/core/import-options.d.ts +32 -0
  148. package/dist/core/import-options.d.ts.map +1 -0
  149. package/dist/core/import-options.js +29 -0
  150. package/dist/core/import-options.js.map +1 -0
  151. package/dist/core/module-dependencies.d.ts +28 -0
  152. package/dist/core/module-dependencies.d.ts.map +1 -0
  153. package/dist/core/module-dependencies.js +35 -0
  154. package/dist/core/module-dependencies.js.map +1 -0
  155. package/dist/core/pair-condition.d.ts +10 -0
  156. package/dist/core/pair-condition.d.ts.map +1 -0
  157. package/dist/core/pair-condition.js +2 -0
  158. package/dist/core/pair-condition.js.map +1 -0
  159. package/dist/core/project.d.ts +68 -0
  160. package/dist/core/project.d.ts.map +1 -0
  161. package/dist/core/project.js +127 -0
  162. package/dist/core/project.js.map +1 -0
  163. package/dist/core/type-matcher.d.ts +10 -0
  164. package/dist/core/type-matcher.d.ts.map +1 -0
  165. package/dist/core/type-matcher.js +2 -0
  166. package/dist/core/type-matcher.js.map +1 -0
  167. package/dist/core/violation.d.ts +38 -0
  168. package/dist/core/violation.d.ts.map +1 -0
  169. package/dist/core/violation.js +130 -0
  170. package/dist/core/violation.js.map +1 -0
  171. package/dist/graphql/index.d.ts +76 -0
  172. package/dist/graphql/index.d.ts.map +1 -0
  173. package/dist/graphql/index.js +108 -0
  174. package/dist/graphql/index.js.map +1 -0
  175. package/dist/graphql/resolver-rule-builder.d.ts +75 -0
  176. package/dist/graphql/resolver-rule-builder.d.ts.map +1 -0
  177. package/dist/graphql/resolver-rule-builder.js +146 -0
  178. package/dist/graphql/resolver-rule-builder.js.map +1 -0
  179. package/dist/graphql/schema-conditions.d.ts +31 -0
  180. package/dist/graphql/schema-conditions.d.ts.map +1 -0
  181. package/dist/graphql/schema-conditions.js +119 -0
  182. package/dist/graphql/schema-conditions.js.map +1 -0
  183. package/dist/graphql/schema-loader.d.ts +75 -0
  184. package/dist/graphql/schema-loader.d.ts.map +1 -0
  185. package/dist/graphql/schema-loader.js +113 -0
  186. package/dist/graphql/schema-loader.js.map +1 -0
  187. package/dist/graphql/schema-predicates.d.ts +42 -0
  188. package/dist/graphql/schema-predicates.d.ts.map +1 -0
  189. package/dist/graphql/schema-predicates.js +74 -0
  190. package/dist/graphql/schema-predicates.js.map +1 -0
  191. package/dist/graphql/schema-rule-builder.d.ts +74 -0
  192. package/dist/graphql/schema-rule-builder.d.ts.map +1 -0
  193. package/dist/graphql/schema-rule-builder.js +188 -0
  194. package/dist/graphql/schema-rule-builder.js.map +1 -0
  195. package/dist/helpers/body-traversal.d.ts +58 -0
  196. package/dist/helpers/body-traversal.d.ts.map +1 -0
  197. package/dist/helpers/body-traversal.js +180 -0
  198. package/dist/helpers/body-traversal.js.map +1 -0
  199. package/dist/helpers/callback-extractor.d.ts +28 -0
  200. package/dist/helpers/callback-extractor.d.ts.map +1 -0
  201. package/dist/helpers/callback-extractor.js +159 -0
  202. package/dist/helpers/callback-extractor.js.map +1 -0
  203. package/dist/helpers/complexity.d.ts +26 -0
  204. package/dist/helpers/complexity.d.ts.map +1 -0
  205. package/dist/helpers/complexity.js +64 -0
  206. package/dist/helpers/complexity.js.map +1 -0
  207. package/dist/helpers/matchers.d.ts +206 -0
  208. package/dist/helpers/matchers.d.ts.map +1 -0
  209. package/dist/helpers/matchers.js +470 -0
  210. package/dist/helpers/matchers.js.map +1 -0
  211. package/dist/helpers/pattern.d.ts +37 -0
  212. package/dist/helpers/pattern.d.ts.map +1 -0
  213. package/dist/helpers/pattern.js +22 -0
  214. package/dist/helpers/pattern.js.map +1 -0
  215. package/dist/helpers/slice-graph.d.ts +32 -0
  216. package/dist/helpers/slice-graph.d.ts.map +1 -0
  217. package/dist/helpers/slice-graph.js +89 -0
  218. package/dist/helpers/slice-graph.js.map +1 -0
  219. package/dist/helpers/tarjan.d.ts +21 -0
  220. package/dist/helpers/tarjan.d.ts.map +1 -0
  221. package/dist/helpers/tarjan.js +71 -0
  222. package/dist/helpers/tarjan.js.map +1 -0
  223. package/dist/helpers/type-matchers.d.ts +67 -0
  224. package/dist/helpers/type-matchers.d.ts.map +1 -0
  225. package/dist/helpers/type-matchers.js +108 -0
  226. package/dist/helpers/type-matchers.js.map +1 -0
  227. package/dist/helpers/within.d.ts +41 -0
  228. package/dist/helpers/within.d.ts.map +1 -0
  229. package/dist/helpers/within.js +32 -0
  230. package/dist/helpers/within.js.map +1 -0
  231. package/dist/index.d.ts +101 -0
  232. package/dist/index.d.ts.map +1 -0
  233. package/dist/index.js +116 -0
  234. package/dist/index.js.map +1 -0
  235. package/dist/models/arch-call.d.ts +83 -0
  236. package/dist/models/arch-call.d.ts.map +1 -0
  237. package/dist/models/arch-call.js +90 -0
  238. package/dist/models/arch-call.js.map +1 -0
  239. package/dist/models/arch-function.d.ts +76 -0
  240. package/dist/models/arch-function.d.ts.map +1 -0
  241. package/dist/models/arch-function.js +122 -0
  242. package/dist/models/arch-function.js.map +1 -0
  243. package/dist/models/arch-jsx-element.d.ts +75 -0
  244. package/dist/models/arch-jsx-element.d.ts.map +1 -0
  245. package/dist/models/arch-jsx-element.js +231 -0
  246. package/dist/models/arch-jsx-element.js.map +1 -0
  247. package/dist/models/cross-layer.d.ts +15 -0
  248. package/dist/models/cross-layer.d.ts.map +1 -0
  249. package/dist/models/cross-layer.js +2 -0
  250. package/dist/models/cross-layer.js.map +1 -0
  251. package/dist/models/slice.d.ts +60 -0
  252. package/dist/models/slice.d.ts.map +1 -0
  253. package/dist/models/slice.js +89 -0
  254. package/dist/models/slice.js.map +1 -0
  255. package/dist/predicates/call.d.ts +49 -0
  256. package/dist/predicates/call.d.ts.map +1 -0
  257. package/dist/predicates/call.js +115 -0
  258. package/dist/predicates/call.js.map +1 -0
  259. package/dist/predicates/class.d.ts +42 -0
  260. package/dist/predicates/class.d.ts.map +1 -0
  261. package/dist/predicates/class.js +83 -0
  262. package/dist/predicates/class.js.map +1 -0
  263. package/dist/predicates/function.d.ts +81 -0
  264. package/dist/predicates/function.d.ts.map +1 -0
  265. package/dist/predicates/function.js +157 -0
  266. package/dist/predicates/function.js.map +1 -0
  267. package/dist/predicates/identity.d.ts +55 -0
  268. package/dist/predicates/identity.d.ts.map +1 -0
  269. package/dist/predicates/identity.js +96 -0
  270. package/dist/predicates/identity.js.map +1 -0
  271. package/dist/predicates/index.d.ts +4 -0
  272. package/dist/predicates/index.d.ts.map +1 -0
  273. package/dist/predicates/index.js +3 -0
  274. package/dist/predicates/index.js.map +1 -0
  275. package/dist/predicates/jsx.d.ts +41 -0
  276. package/dist/predicates/jsx.d.ts.map +1 -0
  277. package/dist/predicates/jsx.js +84 -0
  278. package/dist/predicates/jsx.js.map +1 -0
  279. package/dist/predicates/metrics.d.ts +30 -0
  280. package/dist/predicates/metrics.d.ts.map +1 -0
  281. package/dist/predicates/metrics.js +65 -0
  282. package/dist/predicates/metrics.js.map +1 -0
  283. package/dist/predicates/module.d.ts +44 -0
  284. package/dist/predicates/module.d.ts.map +1 -0
  285. package/dist/predicates/module.js +70 -0
  286. package/dist/predicates/module.js.map +1 -0
  287. package/dist/predicates/type.d.ts +48 -0
  288. package/dist/predicates/type.d.ts.map +1 -0
  289. package/dist/predicates/type.js +107 -0
  290. package/dist/predicates/type.js.map +1 -0
  291. package/dist/presets/boundaries.d.ts +19 -0
  292. package/dist/presets/boundaries.d.ts.map +1 -0
  293. package/dist/presets/boundaries.js +93 -0
  294. package/dist/presets/boundaries.js.map +1 -0
  295. package/dist/presets/data-layer.d.ts +20 -0
  296. package/dist/presets/data-layer.d.ts.map +1 -0
  297. package/dist/presets/data-layer.js +30 -0
  298. package/dist/presets/data-layer.js.map +1 -0
  299. package/dist/presets/index.d.ts +9 -0
  300. package/dist/presets/index.d.ts.map +1 -0
  301. package/dist/presets/index.js +5 -0
  302. package/dist/presets/index.js.map +1 -0
  303. package/dist/presets/layered.d.ts +21 -0
  304. package/dist/presets/layered.d.ts.map +1 -0
  305. package/dist/presets/layered.js +103 -0
  306. package/dist/presets/layered.js.map +1 -0
  307. package/dist/presets/shared.d.ts +3 -0
  308. package/dist/presets/shared.d.ts.map +1 -0
  309. package/dist/presets/shared.js +5 -0
  310. package/dist/presets/shared.js.map +1 -0
  311. package/dist/rules/architecture.d.ts +10 -0
  312. package/dist/rules/architecture.d.ts.map +1 -0
  313. package/dist/rules/architecture.js +14 -0
  314. package/dist/rules/architecture.js.map +1 -0
  315. package/dist/rules/code-quality.d.ts +55 -0
  316. package/dist/rules/code-quality.d.ts.map +1 -0
  317. package/dist/rules/code-quality.js +116 -0
  318. package/dist/rules/code-quality.js.map +1 -0
  319. package/dist/rules/dependencies.d.ts +32 -0
  320. package/dist/rules/dependencies.d.ts.map +1 -0
  321. package/dist/rules/dependencies.js +37 -0
  322. package/dist/rules/dependencies.js.map +1 -0
  323. package/dist/rules/errors.d.ts +31 -0
  324. package/dist/rules/errors.d.ts.map +1 -0
  325. package/dist/rules/errors.js +97 -0
  326. package/dist/rules/errors.js.map +1 -0
  327. package/dist/rules/hygiene.d.ts +54 -0
  328. package/dist/rules/hygiene.d.ts.map +1 -0
  329. package/dist/rules/hygiene.js +62 -0
  330. package/dist/rules/hygiene.js.map +1 -0
  331. package/dist/rules/metrics-function.d.ts +45 -0
  332. package/dist/rules/metrics-function.d.ts.map +1 -0
  333. package/dist/rules/metrics-function.js +87 -0
  334. package/dist/rules/metrics-function.js.map +1 -0
  335. package/dist/rules/metrics.d.ts +64 -0
  336. package/dist/rules/metrics.d.ts.map +1 -0
  337. package/dist/rules/metrics.js +157 -0
  338. package/dist/rules/metrics.js.map +1 -0
  339. package/dist/rules/naming.d.ts +24 -0
  340. package/dist/rules/naming.d.ts.map +1 -0
  341. package/dist/rules/naming.js +51 -0
  342. package/dist/rules/naming.js.map +1 -0
  343. package/dist/rules/security.d.ts +59 -0
  344. package/dist/rules/security.d.ts.map +1 -0
  345. package/dist/rules/security.js +92 -0
  346. package/dist/rules/security.js.map +1 -0
  347. package/dist/rules/typescript.d.ts +73 -0
  348. package/dist/rules/typescript.d.ts.map +1 -0
  349. package/dist/rules/typescript.js +105 -0
  350. package/dist/rules/typescript.js.map +1 -0
  351. package/dist/smells/duplicate-bodies.d.ts +24 -0
  352. package/dist/smells/duplicate-bodies.d.ts.map +1 -0
  353. package/dist/smells/duplicate-bodies.js +131 -0
  354. package/dist/smells/duplicate-bodies.js.map +1 -0
  355. package/dist/smells/fingerprint.d.ts +27 -0
  356. package/dist/smells/fingerprint.d.ts.map +1 -0
  357. package/dist/smells/fingerprint.js +50 -0
  358. package/dist/smells/fingerprint.js.map +1 -0
  359. package/dist/smells/inconsistent-siblings.d.ts +21 -0
  360. package/dist/smells/inconsistent-siblings.d.ts.map +1 -0
  361. package/dist/smells/inconsistent-siblings.js +135 -0
  362. package/dist/smells/inconsistent-siblings.js.map +1 -0
  363. package/dist/smells/index.d.ts +12 -0
  364. package/dist/smells/index.d.ts.map +1 -0
  365. package/dist/smells/index.js +15 -0
  366. package/dist/smells/index.js.map +1 -0
  367. package/dist/smells/smell-builder.d.ts +37 -0
  368. package/dist/smells/smell-builder.d.ts.map +1 -0
  369. package/dist/smells/smell-builder.js +51 -0
  370. package/dist/smells/smell-builder.js.map +1 -0
  371. package/package.json +121 -0
@@ -0,0 +1,275 @@
1
+ import picomatch from 'picomatch';
2
+ import nodePath from 'node:path';
3
+ import { Node, SyntaxKind } from 'ts-morph';
4
+ const graphCache = new WeakMap();
5
+ /**
6
+ * Build or retrieve the reverse import graph for a project.
7
+ *
8
+ * Maps each file path to the list of files that import it.
9
+ * Cached per ts-morph Project instance — multiple rules in the same
10
+ * test suite share the same graph. Cache is cleared automatically
11
+ * when resetProjectCache() creates new ArchProject instances (WeakMap GC).
12
+ */
13
+ /**
14
+ * Add an edge to the reverse import graph: targetPath is imported by sf.
15
+ * Deduplicates entries when `deduplicate` is true (used for re-exports
16
+ * where the same file may appear via both import and export declarations).
17
+ */
18
+ function addToGraph(graph, targetPath, sf, deduplicate) {
19
+ const existing = graph.get(targetPath);
20
+ if (existing) {
21
+ if (!deduplicate || !existing.includes(sf)) {
22
+ existing.push(sf);
23
+ }
24
+ }
25
+ else {
26
+ graph.set(targetPath, [sf]);
27
+ }
28
+ }
29
+ /**
30
+ * Resolve a dynamic import specifier to a project source file.
31
+ *
32
+ * Handles relative specifiers with ESM extension mappings (.js→.ts, .jsx→.tsx,
33
+ * .mjs→.mts) and directory index imports. Non-relative specifiers (bare packages)
34
+ * are skipped — they resolve to node_modules, which are outside the project.
35
+ */
36
+ function resolveDynamicImport(fromFile, specifier) {
37
+ if (!specifier.startsWith('.'))
38
+ return undefined;
39
+ const dirPath = fromFile.getDirectory().getPath();
40
+ const project = fromFile.getProject();
41
+ // Try the specifier as-is, with ESM extension mappings, and as a directory index
42
+ const candidates = [
43
+ specifier,
44
+ specifier.replace(/\.js$/, '.ts'),
45
+ specifier.replace(/\.jsx$/, '.tsx'),
46
+ specifier.replace(/\.mjs$/, '.mts'),
47
+ specifier + '.ts',
48
+ specifier + '.tsx',
49
+ specifier + '/index.ts',
50
+ specifier + '/index.tsx',
51
+ ];
52
+ for (const candidate of candidates) {
53
+ const absolutePath = nodePath.resolve(dirPath, candidate);
54
+ const resolved = project.getSourceFile(absolutePath);
55
+ if (resolved)
56
+ return resolved;
57
+ }
58
+ return undefined;
59
+ }
60
+ /** Index static import declarations from a source file into the reverse graph. */
61
+ function indexStaticImports(graph, sf) {
62
+ for (const decl of sf.getImportDeclarations()) {
63
+ const resolved = decl.getModuleSpecifierSourceFile();
64
+ if (!resolved)
65
+ continue;
66
+ addToGraph(graph, resolved.getFilePath(), sf, false);
67
+ }
68
+ }
69
+ /** Index re-export declarations from a source file into the reverse graph. */
70
+ function indexReExports(graph, sf) {
71
+ for (const decl of sf.getExportDeclarations()) {
72
+ const resolved = decl.getModuleSpecifierSourceFile();
73
+ if (!resolved)
74
+ continue;
75
+ addToGraph(graph, resolved.getFilePath(), sf, true);
76
+ }
77
+ }
78
+ /** Index dynamic import() expressions from a source file into the reverse graph. */
79
+ function indexDynamicImports(graph, sf) {
80
+ for (const callExpr of sf.getDescendantsOfKind(SyntaxKind.CallExpression)) {
81
+ if (callExpr.getExpression().getKind() !== SyntaxKind.ImportKeyword)
82
+ continue;
83
+ const args = callExpr.getArguments();
84
+ if (args.length === 0)
85
+ continue;
86
+ const arg = args[0];
87
+ if (!arg)
88
+ continue;
89
+ let specifier;
90
+ if (Node.isStringLiteral(arg) || Node.isNoSubstitutionTemplateLiteral(arg)) {
91
+ specifier = arg.getLiteralValue();
92
+ }
93
+ if (specifier === undefined)
94
+ continue;
95
+ const resolved = resolveDynamicImport(sf, specifier);
96
+ if (resolved) {
97
+ addToGraph(graph, resolved.getFilePath(), sf, true);
98
+ }
99
+ }
100
+ }
101
+ function getReverseImportGraph(sourceFiles) {
102
+ if (sourceFiles.length === 0)
103
+ return new Map();
104
+ const firstFile = sourceFiles[0];
105
+ if (!firstFile)
106
+ return new Map();
107
+ const project = firstFile.getProject();
108
+ const cached = graphCache.get(project);
109
+ if (cached)
110
+ return cached;
111
+ const graph = new Map();
112
+ for (const sf of sourceFiles) {
113
+ indexStaticImports(graph, sf);
114
+ indexReExports(graph, sf);
115
+ indexDynamicImports(graph, sf);
116
+ }
117
+ graphCache.set(project, graph);
118
+ return graph;
119
+ }
120
+ // ─── Conditions ──────────────────────────────────────────────────
121
+ /**
122
+ * Every file that imports this module must match at least one of the globs.
123
+ *
124
+ * Enforces barrel/facade patterns: internal modules should only be
125
+ * imported through their public API (e.g., index.ts).
126
+ *
127
+ * Modules with zero importers pass vacuously. Chain `.andShould().beImported()`
128
+ * if you also want to catch orphaned files.
129
+ *
130
+ * Both static `import` declarations and dynamic `import()` expressions with
131
+ * string-literal specifiers are resolved. `require()` calls are not resolved.
132
+ */
133
+ export function onlyBeImportedVia(...globs) {
134
+ const matchers = globs.map((g) => picomatch(g));
135
+ const quotedGlobs = globs.map((g) => `"${g}"`).join(', ');
136
+ return {
137
+ description: `only be imported via ${quotedGlobs}`,
138
+ evaluate(elements, context) {
139
+ // Build graph from ALL project files, not just the filtered elements
140
+ const first = elements[0];
141
+ const allFiles = first ? first.getProject().getSourceFiles() : [];
142
+ const graph = getReverseImportGraph(allFiles);
143
+ const violations = [];
144
+ for (const sf of elements) {
145
+ const importers = graph.get(sf.getFilePath()) ?? [];
146
+ for (const importer of importers) {
147
+ const importerPath = importer.getFilePath();
148
+ if (!matchers.some((m) => m(importerPath))) {
149
+ violations.push({
150
+ rule: context.rule,
151
+ element: sf.getBaseName(),
152
+ file: sf.getFilePath(),
153
+ line: 1,
154
+ message: `${sf.getBaseName()} is imported by ${importer.getBaseName()} which does not match [${globs.join(', ')}]`,
155
+ because: context.because,
156
+ });
157
+ }
158
+ }
159
+ }
160
+ return violations;
161
+ },
162
+ };
163
+ }
164
+ /**
165
+ * Module must be imported by at least one other file in the project.
166
+ *
167
+ * Detects dead/orphaned modules that nobody references.
168
+ * Use `.excluding('index.ts', 'main.ts')` to skip entry points.
169
+ *
170
+ * Both static `import` declarations and dynamic `import()` expressions with
171
+ * string-literal specifiers are resolved. Modules loaded via `require()` or
172
+ * dynamic imports with computed specifiers will still be falsely reported.
173
+ */
174
+ export function beImported() {
175
+ return {
176
+ description: 'be imported by at least one other module',
177
+ evaluate(elements, context) {
178
+ const first = elements[0];
179
+ const allFiles = first ? first.getProject().getSourceFiles() : [];
180
+ const graph = getReverseImportGraph(allFiles);
181
+ const violations = [];
182
+ for (const sf of elements) {
183
+ const importers = graph.get(sf.getFilePath()) ?? [];
184
+ if (importers.length === 0) {
185
+ violations.push({
186
+ rule: context.rule,
187
+ element: sf.getBaseName(),
188
+ file: sf.getFilePath(),
189
+ line: 1,
190
+ message: `${sf.getBaseName()} is not imported by any other module`,
191
+ because: context.because,
192
+ });
193
+ }
194
+ }
195
+ return violations;
196
+ },
197
+ };
198
+ }
199
+ /**
200
+ * Every named export in the module must be referenced by at least one other file.
201
+ *
202
+ * Uses ts-morph's findReferencesAsNodes() per export symbol.
203
+ * Short-circuits after first external reference is found.
204
+ *
205
+ * More expensive than file-level checks — scope with `.that().resideInFolder()`
206
+ * to limit the search space.
207
+ *
208
+ * **Note:** Default exports are excluded from this check. Use `beImported()` for
209
+ * file-level dead code detection. Only named exports are analyzed.
210
+ */
211
+ /**
212
+ * Scan a single source file for unused named exports, returning violations.
213
+ */
214
+ function findUnusedExportsInFile(sf, context) {
215
+ const violations = [];
216
+ const exportMap = sf.getExportedDeclarations();
217
+ for (const [name, declarations] of exportMap) {
218
+ if (name === 'default')
219
+ continue;
220
+ const firstDecl = declarations[0];
221
+ if (firstDecl === undefined)
222
+ continue;
223
+ if (!hasExternalReference(firstDecl, sf)) {
224
+ const line = Node.isNode(firstDecl) ? firstDecl.getStartLineNumber() : 1;
225
+ violations.push({
226
+ rule: context.rule,
227
+ element: sf.getBaseName(),
228
+ file: sf.getFilePath(),
229
+ line,
230
+ message: `${sf.getBaseName()} exports "${name}" which is not referenced by any other file`,
231
+ because: context.because,
232
+ });
233
+ }
234
+ }
235
+ return violations;
236
+ }
237
+ export function haveNoUnusedExports() {
238
+ return {
239
+ description: 'have no unused exports',
240
+ evaluate(elements, context) {
241
+ const violations = [];
242
+ for (const sf of elements) {
243
+ violations.push(...findUnusedExportsInFile(sf, context));
244
+ }
245
+ return violations;
246
+ },
247
+ };
248
+ }
249
+ /**
250
+ * Check if a declaration has at least one reference from a different file.
251
+ * Short-circuits on first external reference found.
252
+ *
253
+ * Uses the project's LanguageService.findReferencesAsNodes() which accepts
254
+ * any Node (unlike the ReferenceFindableNode mixin which only some types have).
255
+ */
256
+ function hasExternalReference(declaration, sourceFile) {
257
+ const selfPath = sourceFile.getFilePath();
258
+ const languageService = sourceFile.getProject().getLanguageService();
259
+ try {
260
+ const refs = languageService.findReferencesAsNodes(declaration);
261
+ for (const ref of refs) {
262
+ if (ref.getSourceFile().getFilePath() !== selfPath) {
263
+ return true; // short-circuit
264
+ }
265
+ }
266
+ }
267
+ catch (err) {
268
+ void err; // deliberate: fall through to the safe answer below
269
+ // Some nodes (e.g., shorthand property assignments in re-exports) may fail
270
+ // Treat as "referenced" to avoid false positives
271
+ return true;
272
+ }
273
+ return false;
274
+ }
275
+ //# sourceMappingURL=reverse-dependency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reverse-dependency.js","sourceRoot":"","sources":["../../src/conditions/reverse-dependency.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import type { Condition } from '@nielspeter/eess';
2
+ import type { Slice } from '../models/slice.js';
3
+ /**
4
+ * Assert that no circular dependencies exist between slices.
5
+ *
6
+ * Builds a directed dependency graph from import declarations,
7
+ * then runs Tarjan's SCC algorithm to detect cycles.
8
+ * Each cycle produces a violation listing the cycle path.
9
+ *
10
+ * @example
11
+ * slices(project)
12
+ * .matching('src/features/*\/')
13
+ * .should().beFreeOfCycles()
14
+ * .check()
15
+ */
16
+ export declare function beFreeOfCycles(): Condition<Slice>;
17
+ /**
18
+ * Assert that slices respect a layered dependency order.
19
+ *
20
+ * Given layers ['presentation', 'application', 'persistence', 'domain'],
21
+ * layer N may depend on layers N+1, N+2, ... but NOT on layers N-1, N-2, ...
22
+ * That is, dependencies must flow downward (toward higher indices) only.
23
+ *
24
+ * A layer not present in the slice set is silently skipped.
25
+ *
26
+ * @param layers - Ordered layer names, from highest (e.g., UI) to lowest (e.g., domain)
27
+ *
28
+ * @example
29
+ * slices(project)
30
+ * .assignedFrom(layers)
31
+ * .should().respectLayerOrder('presentation', 'application', 'persistence', 'domain')
32
+ * .check()
33
+ */
34
+ export declare function respectLayerOrder(...layers: string[]): Condition<Slice>;
35
+ /**
36
+ * Assert that no slice depends on any of the listed slices.
37
+ *
38
+ * Use for explicit isolation rules, e.g., "no slice may depend on legacy".
39
+ *
40
+ * @param forbiddenSlices - Names of slices that must not be depended upon
41
+ *
42
+ * @example
43
+ * slices(project)
44
+ * .matching('src/features/*\/')
45
+ * .should().notDependOn('legacy', 'deprecated')
46
+ * .check()
47
+ */
48
+ export declare function notDependOn(...forbiddenSlices: string[]): Condition<Slice>;
49
+ //# sourceMappingURL=slice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slice.d.ts","sourceRoot":"","sources":["../../src/conditions/slice.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAoB,MAAM,kBAAkB,CAAA;AAEnE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAQ/C;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,IAAI,SAAS,CAAC,KAAK,CAAC,CAmDjD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,KAAK,CAAC,CAsCvE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,GAAG,eAAe,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,KAAK,CAAC,CA6B1E"}
@@ -0,0 +1,153 @@
1
+ import { buildSliceDependencyGraph, buildFileToSliceMap, findSliceDependencyDetails, } from '../helpers/slice-graph.js';
2
+ import { tarjanSCC } from '../helpers/tarjan.js';
3
+ /**
4
+ * Assert that no circular dependencies exist between slices.
5
+ *
6
+ * Builds a directed dependency graph from import declarations,
7
+ * then runs Tarjan's SCC algorithm to detect cycles.
8
+ * Each cycle produces a violation listing the cycle path.
9
+ *
10
+ * @example
11
+ * slices(project)
12
+ * .matching('src/features/*\/')
13
+ * .should().beFreeOfCycles()
14
+ * .check()
15
+ */
16
+ export function beFreeOfCycles() {
17
+ return {
18
+ description: 'be free of cycles',
19
+ evaluate(slices, context) {
20
+ const fileToSlice = buildFileToSliceMap(slices);
21
+ const edges = buildSliceDependencyGraph(slices, fileToSlice);
22
+ // Map slice names to indices for Tarjan's
23
+ const sliceNames = slices.map((s) => s.name);
24
+ const nameToIndex = new Map(sliceNames.map((name, i) => [name, i]));
25
+ const adjacency = new Map();
26
+ for (const edge of edges) {
27
+ const fromIdx = nameToIndex.get(edge.from);
28
+ const toIdx = nameToIndex.get(edge.to);
29
+ if (fromIdx === undefined || toIdx === undefined)
30
+ continue;
31
+ const existing = adjacency.get(fromIdx);
32
+ if (existing) {
33
+ existing.push(toIdx);
34
+ }
35
+ else {
36
+ adjacency.set(fromIdx, [toIdx]);
37
+ }
38
+ }
39
+ const sccs = tarjanSCC(slices.length, adjacency);
40
+ const violations = [];
41
+ for (const scc of sccs) {
42
+ const cycleNames = scc.map((i) => sliceNames[i]);
43
+ const cyclePath = [...cycleNames, cycleNames[0]].join(' -> ');
44
+ // Find one concrete file causing the cycle for the violation location
45
+ const fromSlice = cycleNames[0] ?? '';
46
+ const toSlice = cycleNames[1] ?? fromSlice;
47
+ const details = findSliceDependencyDetails(slices, fromSlice, toSlice, fileToSlice);
48
+ const firstDetail = details[0];
49
+ violations.push({
50
+ rule: context.rule,
51
+ element: `[${cycleNames.join(', ')}]`,
52
+ file: firstDetail ? firstDetail.sourceFile.getFilePath() : 'unknown',
53
+ line: firstDetail ? firstDetail.importLine : 0,
54
+ message: `Cycle detected: ${cyclePath}`,
55
+ because: context.because,
56
+ });
57
+ }
58
+ return violations;
59
+ },
60
+ };
61
+ }
62
+ /**
63
+ * Assert that slices respect a layered dependency order.
64
+ *
65
+ * Given layers ['presentation', 'application', 'persistence', 'domain'],
66
+ * layer N may depend on layers N+1, N+2, ... but NOT on layers N-1, N-2, ...
67
+ * That is, dependencies must flow downward (toward higher indices) only.
68
+ *
69
+ * A layer not present in the slice set is silently skipped.
70
+ *
71
+ * @param layers - Ordered layer names, from highest (e.g., UI) to lowest (e.g., domain)
72
+ *
73
+ * @example
74
+ * slices(project)
75
+ * .assignedFrom(layers)
76
+ * .should().respectLayerOrder('presentation', 'application', 'persistence', 'domain')
77
+ * .check()
78
+ */
79
+ export function respectLayerOrder(...layers) {
80
+ return {
81
+ description: `respect layer order [${layers.join(' -> ')}]`,
82
+ evaluate(slices, context) {
83
+ const fileToSlice = buildFileToSliceMap(slices);
84
+ const edges = buildSliceDependencyGraph(slices, fileToSlice);
85
+ // Map layer names to their position (lower index = higher layer)
86
+ const layerIndex = new Map(layers.map((name, i) => [name, i]));
87
+ const violations = [];
88
+ for (const edge of edges) {
89
+ const fromIdx = layerIndex.get(edge.from);
90
+ const toIdx = layerIndex.get(edge.to);
91
+ // Skip edges involving non-layer slices
92
+ if (fromIdx === undefined || toIdx === undefined)
93
+ continue;
94
+ // Violation: depending on a higher layer (lower index)
95
+ if (toIdx < fromIdx) {
96
+ const details = findSliceDependencyDetails(slices, edge.from, edge.to, fileToSlice);
97
+ for (const detail of details) {
98
+ violations.push({
99
+ rule: context.rule,
100
+ element: detail.sourceFile.getBaseName(),
101
+ file: detail.sourceFile.getFilePath(),
102
+ line: detail.importLine,
103
+ message: `Layer "${edge.from}" depends on higher layer "${edge.to}" (allowed: ${layers.slice(fromIdx + 1).join(', ') || 'none'})`,
104
+ because: context.because,
105
+ });
106
+ }
107
+ }
108
+ }
109
+ return violations;
110
+ },
111
+ };
112
+ }
113
+ /**
114
+ * Assert that no slice depends on any of the listed slices.
115
+ *
116
+ * Use for explicit isolation rules, e.g., "no slice may depend on legacy".
117
+ *
118
+ * @param forbiddenSlices - Names of slices that must not be depended upon
119
+ *
120
+ * @example
121
+ * slices(project)
122
+ * .matching('src/features/*\/')
123
+ * .should().notDependOn('legacy', 'deprecated')
124
+ * .check()
125
+ */
126
+ export function notDependOn(...forbiddenSlices) {
127
+ const forbiddenSet = new Set(forbiddenSlices);
128
+ return {
129
+ description: `not depend on [${forbiddenSlices.join(', ')}]`,
130
+ evaluate(slices, context) {
131
+ const fileToSlice = buildFileToSliceMap(slices);
132
+ const edges = buildSliceDependencyGraph(slices, fileToSlice);
133
+ const violations = [];
134
+ for (const edge of edges) {
135
+ if (forbiddenSet.has(edge.to)) {
136
+ const details = findSliceDependencyDetails(slices, edge.from, edge.to, fileToSlice);
137
+ for (const detail of details) {
138
+ violations.push({
139
+ rule: context.rule,
140
+ element: detail.sourceFile.getBaseName(),
141
+ file: detail.sourceFile.getFilePath(),
142
+ line: detail.importLine,
143
+ message: `Slice "${edge.from}" depends on forbidden slice "${edge.to}"`,
144
+ because: context.because,
145
+ });
146
+ }
147
+ }
148
+ }
149
+ return violations;
150
+ },
151
+ };
152
+ }
153
+ //# sourceMappingURL=slice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slice.js","sourceRoot":"","sources":["../../src/conditions/slice.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ import { Node } from 'ts-morph';
2
+ import type { Condition } from '@nielspeter/eess';
3
+ /**
4
+ * Elements must reside in a file matching the glob pattern.
5
+ *
6
+ * Uses picomatch for glob matching against the absolute file path.
7
+ *
8
+ * @example
9
+ * // Assert all matched elements are in repository files
10
+ * .should(resideInFile('** /repositories/*.ts'))
11
+ */
12
+ export declare function resideInFile<T extends Node>(glob: string): Condition<T>;
13
+ /**
14
+ * Elements must reside in a folder matching the glob pattern.
15
+ *
16
+ * Matches against the directory portion of the file path (everything
17
+ * before the last path separator).
18
+ *
19
+ * @example
20
+ * // Assert all matched elements are in the services folder
21
+ * .should(resideInFolder('** /services'))
22
+ */
23
+ export declare function resideInFolder<T extends Node>(glob: string): Condition<T>;
24
+ /**
25
+ * Elements must have a name matching the regex pattern.
26
+ *
27
+ * @example
28
+ * // Assert all matched elements follow the naming convention
29
+ * .should(haveNameMatching(/Service$/))
30
+ */
31
+ export declare function haveNameMatching<T extends Node>(regex: RegExp): Condition<T>;
32
+ /**
33
+ * Elements must be exported from their module.
34
+ *
35
+ * Checks for the `export` keyword on the node. For variable declarations,
36
+ * checks the parent variable statement.
37
+ *
38
+ * @example
39
+ * // Assert all matched services are exported
40
+ * .should(beExported())
41
+ */
42
+ export declare function beExported<T extends Node>(): Condition<T>;
43
+ /**
44
+ * The predicate set must be empty — no elements should match.
45
+ *
46
+ * This is a set-level condition, not an element-level condition.
47
+ * If ANY elements exist after predicate filtering, each one becomes
48
+ * a violation with the message "X should not exist".
49
+ *
50
+ * @example
51
+ * // Assert no parse*Order functions exist
52
+ * functions(project)
53
+ * .that(haveNameMatching(/^parse\w+Order$/))
54
+ * .should(notExist())
55
+ * .because('use shared parseOrder() utility instead')
56
+ */
57
+ export declare function notExist<T extends Node>(): Condition<T>;
58
+ //# sourceMappingURL=structural.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structural.d.ts","sourceRoot":"","sources":["../../src/conditions/structural.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAA;AAE/B,OAAO,KAAK,EAAE,SAAS,EAAoB,MAAM,kBAAkB,CAAA;AAKnE;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,CAQvE;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,CAezE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,CAM5E;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAyBzD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CASvD"}
@@ -0,0 +1,102 @@
1
+ import { Node } from 'ts-morph';
2
+ import picomatch from 'picomatch';
3
+ import { createViolation, getElementFile, getElementName } from '../core/violation.js';
4
+ import { elementCondition } from './helpers.js';
5
+ /**
6
+ * Elements must reside in a file matching the glob pattern.
7
+ *
8
+ * Uses picomatch for glob matching against the absolute file path.
9
+ *
10
+ * @example
11
+ * // Assert all matched elements are in repository files
12
+ * .should(resideInFile('** /repositories/*.ts'))
13
+ */
14
+ export function resideInFile(glob) {
15
+ const isMatch = picomatch(glob);
16
+ return elementCondition(`reside in file matching '${glob}'`, (element) => isMatch(getElementFile(element)), (element) => `${getElementName(element)} resides in '${getElementFile(element)}' which does not match '${glob}'`);
17
+ }
18
+ /**
19
+ * Elements must reside in a folder matching the glob pattern.
20
+ *
21
+ * Matches against the directory portion of the file path (everything
22
+ * before the last path separator).
23
+ *
24
+ * @example
25
+ * // Assert all matched elements are in the services folder
26
+ * .should(resideInFolder('** /services'))
27
+ */
28
+ export function resideInFolder(glob) {
29
+ const isMatch = picomatch(glob);
30
+ return elementCondition(`reside in folder matching '${glob}'`, (element) => {
31
+ const filePath = getElementFile(element);
32
+ const folder = filePath.substring(0, filePath.lastIndexOf('/'));
33
+ return isMatch(folder);
34
+ }, (element) => {
35
+ const filePath = getElementFile(element);
36
+ const folder = filePath.substring(0, filePath.lastIndexOf('/'));
37
+ return `${getElementName(element)} resides in folder '${folder}' which does not match '${glob}'`;
38
+ });
39
+ }
40
+ /**
41
+ * Elements must have a name matching the regex pattern.
42
+ *
43
+ * @example
44
+ * // Assert all matched elements follow the naming convention
45
+ * .should(haveNameMatching(/Service$/))
46
+ */
47
+ export function haveNameMatching(regex) {
48
+ return elementCondition(`have name matching ${String(regex)}`, (element) => regex.test(getElementName(element)), (element) => `${getElementName(element)} does not have a name matching ${String(regex)}`);
49
+ }
50
+ /**
51
+ * Elements must be exported from their module.
52
+ *
53
+ * Checks for the `export` keyword on the node. For variable declarations,
54
+ * checks the parent variable statement.
55
+ *
56
+ * @example
57
+ * // Assert all matched services are exported
58
+ * .should(beExported())
59
+ */
60
+ export function beExported() {
61
+ return elementCondition('be exported', (element) => {
62
+ // ts-morph type guards for nodes with isExported()
63
+ if (Node.isClassDeclaration(element) ||
64
+ Node.isFunctionDeclaration(element) ||
65
+ Node.isInterfaceDeclaration(element) ||
66
+ Node.isTypeAliasDeclaration(element) ||
67
+ Node.isEnumDeclaration(element)) {
68
+ return element.isExported();
69
+ }
70
+ // VariableDeclaration — check parent VariableStatement
71
+ if (Node.isVariableDeclaration(element)) {
72
+ const varStatement = element.getVariableStatement();
73
+ if (varStatement) {
74
+ return varStatement.isExported();
75
+ }
76
+ }
77
+ return false;
78
+ }, (element) => `${getElementName(element)} is not exported`);
79
+ }
80
+ /**
81
+ * The predicate set must be empty — no elements should match.
82
+ *
83
+ * This is a set-level condition, not an element-level condition.
84
+ * If ANY elements exist after predicate filtering, each one becomes
85
+ * a violation with the message "X should not exist".
86
+ *
87
+ * @example
88
+ * // Assert no parse*Order functions exist
89
+ * functions(project)
90
+ * .that(haveNameMatching(/^parse\w+Order$/))
91
+ * .should(notExist())
92
+ * .because('use shared parseOrder() utility instead')
93
+ */
94
+ export function notExist() {
95
+ return {
96
+ description: 'not exist',
97
+ evaluate(elements, context) {
98
+ return elements.map((element) => createViolation(element, `${getElementName(element)} should not exist`, context));
99
+ },
100
+ };
101
+ }
102
+ //# sourceMappingURL=structural.js.map