@geonosis/oxlint-plugin-biological-architecture 3.1.0 → 3.3.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 (248) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +75 -6
  3. package/RULES.md +202 -101
  4. package/corpus/.oxlintrc.json +5 -0
  5. package/corpus/data/bad-dynamic-orm.ts +5 -0
  6. package/corpus/data/bad-type-only-orm.ts +3 -0
  7. package/corpus/e2e/bad-list-response.spec.ts +5 -0
  8. package/corpus/e2e/bad-timeout.spec.ts +5 -0
  9. package/corpus/e2e/good-detail-response.spec.ts +5 -0
  10. package/corpus/e2e/good-state.spec.ts +5 -0
  11. package/corpus/manifest.json +2 -0
  12. package/corpus/medusa/queries/bad-silent-cap.ts +11 -0
  13. package/corpus/medusa/queries/good-paged-through.ts +19 -0
  14. package/corpus/medusa/queries/integration-tests/any-row.ts +10 -0
  15. package/corpus/tissues/bad-awaits-data.tsx +8 -0
  16. package/corpus/tissues/bad-fetches-data.tsx +7 -0
  17. package/corpus/tissues/good-takes-products.tsx +5 -0
  18. package/dist/discover-atoms.d.ts +2 -37
  19. package/dist/discover-atoms.js +1 -104
  20. package/dist/index.d.ts +3 -12
  21. package/dist/index.js +21 -176
  22. package/dist/presets.d.ts +11 -19
  23. package/dist/presets.js +94 -4
  24. package/dist/registry.d.ts +2 -0
  25. package/dist/registry.js +172 -0
  26. package/dist/rules/atom-no-deps.d.ts +1 -0
  27. package/dist/rules/atom-no-deps.js +10 -11
  28. package/dist/rules/boundary-time-is-primitive.d.ts +0 -7
  29. package/dist/rules/cell-must-be-stateful.d.ts +2 -2
  30. package/dist/rules/cell-must-be-stateful.js +24 -23
  31. package/dist/rules/cell-must-not-compose-cell.d.ts +1 -0
  32. package/dist/rules/cell-must-not-compose-cell.js +10 -3
  33. package/dist/rules/cell-no-cross-feature-organelles.d.ts +1 -0
  34. package/dist/rules/cell-no-cross-feature-organelles.js +7 -0
  35. package/dist/rules/cell-no-tissues.d.ts +1 -0
  36. package/dist/rules/cell-no-tissues.js +11 -7
  37. package/dist/rules/cells-folder-index-is-barrel.d.ts +12 -10
  38. package/dist/rules/cells-folder-index-is-barrel.js +66 -34
  39. package/dist/rules/compound-must-be-stateless.d.ts +2 -0
  40. package/dist/rules/compound-must-be-stateless.js +24 -26
  41. package/dist/rules/compound-no-stores.d.ts +1 -0
  42. package/dist/rules/compound-no-stores.js +12 -10
  43. package/dist/rules/constants-in-constants-file.d.ts +2 -8
  44. package/dist/rules/constants-in-constants-file.js +5 -6
  45. package/dist/rules/dialect-through-the-seam.d.ts +0 -13
  46. package/dist/rules/durable-body-reads-through-steps.d.ts +0 -14
  47. package/dist/rules/durable-body-reads-through-steps.js +8 -3
  48. package/dist/rules/effect-hook-naming.d.ts +9 -11
  49. package/dist/rules/effect-hook-naming.js +33 -31
  50. package/dist/rules/emit-declares-attempts.d.ts +0 -17
  51. package/dist/rules/emit-declares-attempts.js +5 -10
  52. package/dist/rules/font-roles-only.d.ts +0 -18
  53. package/dist/rules/font-roles-only.js +19 -22
  54. package/dist/rules/hermetic-e2e-no-wall-clock-waits.d.ts +53 -0
  55. package/dist/rules/hermetic-e2e-no-wall-clock-waits.js +100 -0
  56. package/dist/rules/icon-control-has-name.js +7 -1
  57. package/dist/rules/layer-walls.d.ts +0 -25
  58. package/dist/rules/layer-walls.js +7 -13
  59. package/dist/rules/lib/atom-map.d.ts +24 -0
  60. package/dist/rules/lib/atom-map.js +16 -0
  61. package/dist/rules/lib/cells-barrel.d.ts +0 -1
  62. package/dist/rules/lib/colors.d.ts +0 -19
  63. package/dist/rules/lib/colors.js +10 -243
  64. package/dist/rules/lib/comments.d.ts +19 -0
  65. package/dist/rules/lib/comments.js +5 -0
  66. package/dist/rules/lib/context.d.ts +0 -10
  67. package/dist/rules/lib/direction-rule.d.ts +4 -21
  68. package/dist/rules/lib/direction-rule.js +25 -4
  69. package/dist/rules/lib/doors.d.ts +2 -18
  70. package/dist/rules/lib/doors.js +81 -41
  71. package/dist/rules/lib/fresh.d.ts +5 -0
  72. package/dist/rules/lib/fresh.js +15 -0
  73. package/dist/rules/lib/hooks.d.ts +2 -17
  74. package/dist/rules/lib/hooks.js +14 -9
  75. package/dist/rules/lib/imports.d.ts +2 -23
  76. package/dist/rules/lib/imports.js +7 -2
  77. package/dist/rules/lib/jsx.d.ts +31 -0
  78. package/dist/rules/lib/jsx.js +11 -0
  79. package/dist/rules/lib/landing.d.ts +0 -40
  80. package/dist/rules/lib/literal-call.d.ts +1 -0
  81. package/dist/rules/lib/literal-call.js +2 -0
  82. package/dist/rules/lib/module-text.d.ts +3 -27
  83. package/dist/rules/lib/module-text.js +19 -4
  84. package/dist/rules/lib/mutations.d.ts +0 -14
  85. package/dist/rules/lib/nodes.d.ts +1 -18
  86. package/dist/rules/lib/nodes.js +1 -0
  87. package/dist/rules/lib/objects.d.ts +2 -11
  88. package/dist/rules/lib/objects.js +21 -0
  89. package/dist/rules/lib/options.d.ts +3 -52
  90. package/dist/rules/lib/options.js +49 -18
  91. package/dist/rules/lib/package-imports.d.ts +0 -5
  92. package/dist/rules/lib/package-imports.js +4 -10
  93. package/dist/rules/lib/paths.d.ts +15 -41
  94. package/dist/rules/lib/paths.js +34 -1
  95. package/dist/rules/lib/pattern-match.d.ts +2 -12
  96. package/dist/rules/lib/pattern-match.js +5 -1
  97. package/dist/rules/lib/probe.d.ts +0 -43
  98. package/dist/rules/lib/reexports.d.ts +0 -18
  99. package/dist/rules/lib/render-tracking.d.ts +0 -8
  100. package/dist/rules/lib/run-files.d.ts +18 -0
  101. package/dist/rules/lib/run-files.js +144 -0
  102. package/dist/rules/lib/shared-features.d.ts +0 -8
  103. package/dist/rules/lib/shared-features.js +2 -9
  104. package/dist/rules/lib/state-sources.d.ts +8 -0
  105. package/dist/rules/lib/state-sources.js +55 -0
  106. package/dist/rules/lib/steps.d.ts +3 -5
  107. package/dist/rules/lib/steps.js +6 -1
  108. package/dist/rules/lib/styling.d.ts +9 -42
  109. package/dist/rules/lib/styling.js +36 -10
  110. package/dist/rules/lib/tiers.d.ts +11 -24
  111. package/dist/rules/lib/tiers.js +41 -1
  112. package/dist/rules/lib/tissue-entrypoint.d.ts +0 -1
  113. package/dist/rules/lib/tissue-entrypoint.js +4 -5
  114. package/dist/rules/lib/token-layer.d.ts +19 -0
  115. package/dist/rules/lib/token-layer.js +170 -0
  116. package/dist/rules/lib/tsconfig-paths.d.ts +0 -4
  117. package/dist/rules/lib/tsconfig-paths.js +47 -15
  118. package/dist/rules/lib/type-members.d.ts +6 -0
  119. package/dist/rules/lib/type-members.js +165 -0
  120. package/dist/rules/lib/visitors.d.ts +0 -22
  121. package/dist/rules/lib/workspace-members.d.ts +2 -0
  122. package/dist/rules/lib/workspace-members.js +113 -0
  123. package/dist/rules/list-take-without-order.d.ts +24 -0
  124. package/dist/rules/list-take-without-order.js +293 -16
  125. package/dist/rules/max-comment-density.d.ts +3 -18
  126. package/dist/rules/max-comment-density.js +3 -3
  127. package/dist/rules/molecule-atoms-only.d.ts +1 -0
  128. package/dist/rules/molecule-atoms-only.js +11 -7
  129. package/dist/rules/molecule-must-compose.d.ts +2 -0
  130. package/dist/rules/molecule-must-compose.js +17 -21
  131. package/dist/rules/mutating-step-declares-retry.d.ts +2 -7
  132. package/dist/rules/mutating-step-declares-retry.js +29 -11
  133. package/dist/rules/named-when-block.d.ts +0 -2
  134. package/dist/rules/named-when-block.js +2 -8
  135. package/dist/rules/next-route-segment-is-thin-delegate.d.ts +4 -19
  136. package/dist/rules/next-route-segment-is-thin-delegate.js +47 -71
  137. package/dist/rules/no-brand-names.d.ts +0 -9
  138. package/dist/rules/no-brand-names.js +1 -1
  139. package/dist/rules/no-card-shaped-div.d.ts +4 -42
  140. package/dist/rules/no-card-shaped-div.js +12 -4
  141. package/dist/rules/no-cross-feature-stores.d.ts +1 -0
  142. package/dist/rules/no-cross-feature-stores.js +9 -5
  143. package/dist/rules/no-d1-transaction.d.ts +2 -5
  144. package/dist/rules/no-d1-transaction.js +7 -0
  145. package/dist/rules/no-date-string-compare.d.ts +2 -0
  146. package/dist/rules/no-date-string-compare.js +14 -8
  147. package/dist/rules/no-duplicate-jsx-patterns.d.ts +3 -1
  148. package/dist/rules/no-duplicate-jsx-patterns.js +16 -1
  149. package/dist/rules/no-generic-utility-package.d.ts +48 -2
  150. package/dist/rules/no-generic-utility-package.js +48 -9
  151. package/dist/rules/no-hook-in-component-disguise.d.ts +27 -13
  152. package/dist/rules/no-hook-in-component-disguise.js +38 -30
  153. package/dist/rules/no-index-count-as-exact.d.ts +0 -3
  154. package/dist/rules/no-inert-hidden-jsx.d.ts +4 -21
  155. package/dist/rules/no-inert-hidden-jsx.js +13 -12
  156. package/dist/rules/no-inline-data-in-jsx.d.ts +2 -0
  157. package/dist/rules/no-inline-data-in-jsx.js +17 -2
  158. package/dist/rules/no-invalid-feature-folders.d.ts +2 -0
  159. package/dist/rules/no-invalid-feature-folders.js +12 -4
  160. package/dist/rules/no-loader-side-effect-in-index-file.d.ts +2 -9
  161. package/dist/rules/no-logic-in-component-files.d.ts +6 -5
  162. package/dist/rules/no-logic-in-component-files.js +52 -31
  163. package/dist/rules/no-mock-db-in-integration.d.ts +0 -15
  164. package/dist/rules/no-mock-db-in-integration.js +3 -4
  165. package/dist/rules/no-orm-outside-db.d.ts +1 -0
  166. package/dist/rules/no-orm-outside-db.js +10 -7
  167. package/dist/rules/no-raw-html-atoms.d.ts +3 -22
  168. package/dist/rules/no-raw-html-atoms.js +5 -17
  169. package/dist/rules/no-raw-sql-outside-allowed.d.ts +2 -0
  170. package/dist/rules/no-raw-sql-outside-allowed.js +10 -15
  171. package/dist/rules/no-react-namespace.d.ts +2 -0
  172. package/dist/rules/no-react-namespace.js +13 -0
  173. package/dist/rules/no-renamed-html-props.d.ts +2 -0
  174. package/dist/rules/no-renamed-html-props.js +11 -2
  175. package/dist/rules/no-render-prop-reader.d.ts +4 -20
  176. package/dist/rules/no-render-prop-reader.js +13 -5
  177. package/dist/rules/no-runtime-dead-affordance.d.ts +3 -26
  178. package/dist/rules/no-runtime-dead-affordance.js +7 -20
  179. package/dist/rules/no-swallowed-catch.js +2 -8
  180. package/dist/rules/no-trivial-wrapper-component.d.ts +4 -19
  181. package/dist/rules/no-trivial-wrapper-component.js +24 -33
  182. package/dist/rules/no-ts-in-bio-folders.d.ts +2 -13
  183. package/dist/rules/no-ts-in-bio-folders.js +9 -7
  184. package/dist/rules/no-type-definitions-in-components.d.ts +2 -0
  185. package/dist/rules/no-type-definitions-in-components.js +12 -1
  186. package/dist/rules/no-unregistered-plugin-dir.d.ts +0 -41
  187. package/dist/rules/no-unregistered-plugin-dir.js +1 -9
  188. package/dist/rules/no-void-port.d.ts +2 -4
  189. package/dist/rules/no-void-port.js +7 -0
  190. package/dist/rules/organelle-dependency.d.ts +1 -0
  191. package/dist/rules/organelle-dependency.js +10 -8
  192. package/dist/rules/organelle-single-source.d.ts +2 -0
  193. package/dist/rules/organelle-single-source.js +58 -9
  194. package/dist/rules/plugin-route-namespaced.d.ts +0 -11
  195. package/dist/rules/plugin-route-namespaced.js +2 -2
  196. package/dist/rules/queries-require-org-scope.d.ts +0 -3
  197. package/dist/rules/queries-require-org-scope.js +3 -10
  198. package/dist/rules/queue-loop-is-the-library.js +3 -2
  199. package/dist/rules/route-no-inline-mutation.d.ts +1 -25
  200. package/dist/rules/route-no-inline-mutation.js +2 -7
  201. package/dist/rules/router-schema-parity.d.ts +0 -9
  202. package/dist/rules/router-schema-parity.js +5 -9
  203. package/dist/rules/service-name-not-reserved.d.ts +0 -3
  204. package/dist/rules/service-name-not-reserved.js +1 -34
  205. package/dist/rules/ssot-no-inline-facts.js +2 -5
  206. package/dist/rules/ssot-no-process-env.js +4 -12
  207. package/dist/rules/step-declares-compensation-or-none.d.ts +2 -1
  208. package/dist/rules/step-declares-compensation-or-none.js +17 -1
  209. package/dist/rules/step-opens-its-own-cell.d.ts +3 -26
  210. package/dist/rules/step-opens-its-own-cell.js +8 -13
  211. package/dist/rules/store-route-scopes-tenant-data.js +8 -26
  212. package/dist/rules/subpath-over-barrel.d.ts +3 -10
  213. package/dist/rules/subpath-over-barrel.js +55 -10
  214. package/dist/rules/subscriber-declares-id.d.ts +0 -2
  215. package/dist/rules/subscriber-declares-id.js +2 -8
  216. package/dist/rules/tables-declare-their-plane.d.ts +0 -2
  217. package/dist/rules/tables-declare-their-plane.js +4 -10
  218. package/dist/rules/tenant-tables-carry-org-id.d.ts +1 -4
  219. package/dist/rules/tenant-tables-carry-org-id.js +5 -12
  220. package/dist/rules/test-no-clock-polling-for-promise-flips.d.ts +7 -1
  221. package/dist/rules/test-no-clock-polling-for-promise-flips.js +21 -1
  222. package/dist/rules/time-through-the-door.d.ts +0 -24
  223. package/dist/rules/time-through-the-door.js +5 -5
  224. package/dist/rules/tissue-must-compose.d.ts +4 -2
  225. package/dist/rules/tissue-must-compose.js +25 -20
  226. package/dist/rules/tissue-no-data-props.d.ts +57 -1
  227. package/dist/rules/tissue-no-data-props.js +114 -32
  228. package/dist/rules/tissue-no-fetch.d.ts +26 -0
  229. package/dist/rules/tissue-no-fetch.js +57 -0
  230. package/dist/rules/tissue-no-hooks.d.ts +21 -23
  231. package/dist/rules/tissue-no-hooks.js +22 -4
  232. package/dist/rules/tissue-no-organelles.d.ts +1 -0
  233. package/dist/rules/tissue-no-organelles.js +9 -1
  234. package/dist/rules/tissue-no-stores.d.ts +1 -0
  235. package/dist/rules/tissue-no-stores.js +9 -1
  236. package/dist/rules/todo-names-its-plan.d.ts +1 -28
  237. package/dist/rules/todo-names-its-plan.js +2 -2
  238. package/dist/rules/tokens-only-colors.d.ts +8 -46
  239. package/dist/rules/tokens-only-colors.js +64 -20
  240. package/dist/rules/unique-query-step-name.d.ts +0 -7
  241. package/dist/rules/unique-query-step-name.js +6 -10
  242. package/dist/rules/worker-handles-are-scoped.d.ts +11 -8
  243. package/dist/rules/worker-handles-are-scoped.js +12 -5
  244. package/dist/rules/zustand-v5-best-practices.d.ts +8 -18
  245. package/dist/rules/zustand-v5-best-practices.js +32 -56
  246. package/dist/timing.d.ts +18 -0
  247. package/dist/timing.js +75 -0
  248. package/package.json +2 -2
package/dist/presets.js CHANGED
@@ -34,6 +34,7 @@ var FRONTEND = [
34
34
  "organelle-single-source",
35
35
  "tissue-must-compose",
36
36
  "tissue-no-data-props",
37
+ "tissue-no-fetch",
37
38
  "tissue-no-hooks",
38
39
  "tissue-no-organelles",
39
40
  "tissue-no-stores",
@@ -56,7 +57,11 @@ var BACKEND_WORKFLOWS = [
56
57
  "tables-declare-their-plane",
57
58
  "time-through-the-door"
58
59
  ];
59
- var BACKEND_D1 = ["no-d1-transaction", "worker-handles-are-scoped"];
60
+ var BACKEND_D1 = [
61
+ "list-take-without-order",
62
+ "no-d1-transaction",
63
+ "worker-handles-are-scoped"
64
+ ];
60
65
  var BACKEND_WORKFLOWS_CF = ["step-opens-its-own-cell"];
61
66
  var RENAMED_TO = {
62
67
  "backend-sagaflow-cf": "backend-workflows-cf"
@@ -84,18 +89,22 @@ var BACKEND_MEDUSA = [
84
89
  "subscriber-declares-id",
85
90
  "unique-query-step-name"
86
91
  ];
92
+ var WORKFLOW_RULES = new Set(BACKEND_WORKFLOWS);
87
93
  var FAILURE_PATHS = ["no-swallowed-catch"];
88
94
  var INTEGRATIONS = ["no-unregistered-plugin-dir"];
89
95
  var BACKEND_TRPC = ["router-schema-parity"];
90
96
  var TENANT_SCOPING = [
91
97
  "queries-require-org-scope",
92
- "store-route-scopes-tenant-data",
93
98
  "tables-declare-their-plane",
94
99
  "tenant-tables-carry-org-id"
95
100
  ];
96
101
  var TOOLING = ["layer-walls", "no-generic-utility-package", "todo-names-its-plan"];
97
102
  var THEMEKIT = ["font-roles-only", "tokens-only-colors"];
98
- var TESTBED = ["no-mock-db-in-integration", "test-no-clock-polling-for-promise-flips"];
103
+ var TESTBED = [
104
+ "hermetic-e2e-no-wall-clock-waits",
105
+ "no-mock-db-in-integration",
106
+ "test-no-clock-polling-for-promise-flips"
107
+ ];
99
108
  var PACKAGE_SUBPATHS = ["subpath-over-barrel"];
100
109
  var RULE_PREFIX = "biological-architecture";
101
110
  var asRules = (names) => Object.fromEntries(names.map((name) => [`${RULE_PREFIX}/${name}`, "error"]));
@@ -105,13 +114,35 @@ var COMMENT_CEILING = {
105
114
  "capitalized-comments": "error"
106
115
  }
107
116
  };
117
+ var ACROSS_ENGINES = new Set(["list-take-without-order"]);
108
118
  var PRESUMED_ENGINES = [
109
119
  {
110
120
  engine: "Medusa",
111
121
  packages: ["@medusajs/framework", "@medusajs/medusa", "@medusajs/utils"],
112
- rules: BACKEND_MEDUSA.filter((name) => !BACKEND_WORKFLOWS.includes(name))
122
+ rules: BACKEND_MEDUSA.filter((name) => !WORKFLOW_RULES.has(name) && !ACROSS_ENGINES.has(name))
113
123
  }
114
124
  ];
125
+ var TIER_RULES = {
126
+ atoms: ["atom-no-deps"],
127
+ cells: [
128
+ "cell-must-be-stateful",
129
+ "cell-must-not-compose-cell",
130
+ "cell-no-cross-feature-organelles",
131
+ "cell-no-tissues",
132
+ "cells-folder-index-is-barrel"
133
+ ],
134
+ compounds: ["compound-must-be-stateless", "compound-no-stores"],
135
+ molecules: ["molecule-atoms-only", "molecule-must-compose"],
136
+ organelles: ["organelle-dependency", "organelle-single-source"],
137
+ tissues: [
138
+ "tissue-must-compose",
139
+ "tissue-no-data-props",
140
+ "tissue-no-fetch",
141
+ "tissue-no-hooks",
142
+ "tissue-no-organelles",
143
+ "tissue-no-stores"
144
+ ]
145
+ };
115
146
  var PRESETS = {
116
147
  "backend-d1": { rules: asRules(BACKEND_D1) },
117
148
  "backend-medusa": { rules: asRules(BACKEND_MEDUSA) },
@@ -136,9 +167,68 @@ var PRESETS = {
136
167
  tooling: { rules: asRules(TOOLING) }
137
168
  };
138
169
  var rulesOfPreset = (preset) => Object.keys(PRESETS[preset].rules).filter((id) => id.startsWith(`${RULE_PREFIX}/`)).map((id) => id.slice(RULE_PREFIX.length + 1));
170
+ var SECTIONS = {
171
+ frontend: {
172
+ blurb: "The tier rules every React repo gets. `recommended` is this preset.",
173
+ order: 1
174
+ },
175
+ "backend-workflows": {
176
+ blurb: "Workflows, the data layer, and single-source-of-truth. Storage-engine facts live in `backend-d1`.",
177
+ order: 2
178
+ },
179
+ "backend-d1": {
180
+ blurb: "Cloudflare D1 and cell-worker facts. False on any other engine — enable per repo.",
181
+ order: 3
182
+ },
183
+ "backend-workflows-cf": {
184
+ blurb: "The org workflow engine on Cloudflare Workflows. False next door — an inline run never hibernates.",
185
+ order: 4
186
+ },
187
+ "backend-medusa": { blurb: "Medusa's storefront route surface.", order: 5 },
188
+ "backend-trpc": {
189
+ blurb: "The router/schema mirror. A layout convention, not a framework fact — both directories are options, and in one consumer they are in different packages.",
190
+ order: 6
191
+ },
192
+ integrations: {
193
+ blurb: "The integration seam. Enforces nothing until the repo says where its plugin directories live and which file registers them.",
194
+ order: 7
195
+ },
196
+ "package-subpaths": {
197
+ blurb: "Where a package's names are imported from. The map is each of the repo's own packages' `exports`, read off disk; `packages` overrides it per package.",
198
+ order: 8
199
+ },
200
+ "tenant-scoping": {
201
+ blurb: "Every row placed, scoped and narrowed to one organisation.",
202
+ order: 9
203
+ },
204
+ themekit: {
205
+ blurb: "Conformance with a themekit: the component library never hardcodes what the theme decides. Inert until the repo says how its own tokens are spelled.",
206
+ order: 10
207
+ },
208
+ testbed: {
209
+ blurb: "The harness. Enforces nothing until the repo says where its integration suites live and what its database module is called.",
210
+ order: 11
211
+ },
212
+ "comment-ceiling": {
213
+ blurb: "The comment posture. Opt in per repo — the two source repos were defensible and opposite.",
214
+ order: 12
215
+ },
216
+ "failure-paths": {
217
+ blurb: "The modules a repo cannot afford to fail quietly on. Requires `paths` — which of them fetch and refresh; enabled without it the rule reports that once a run and judges nothing.",
218
+ order: 13
219
+ },
220
+ tooling: {
221
+ blurb: "A repo's own layers, walled. Enforces nothing until the walls are declared.",
222
+ order: 14
223
+ }
224
+ };
225
+ var isPresetName = (name) => (name in PRESETS);
226
+ var presetSections = () => Object.entries(SECTIONS).flatMap(([name, doc]) => isPresetName(name) ? [{ ...doc, name }] : []).toSorted((a, b) => a.order - b.order);
139
227
  export {
140
228
  PRESETS,
141
229
  PRESUMED_ENGINES,
142
230
  RENAMED_TO,
231
+ TIER_RULES,
232
+ presetSections,
143
233
  rulesOfPreset
144
234
  };
@@ -0,0 +1,2 @@
1
+ import type { Rule } from './index.js';
2
+ export declare const registered: Record<string, Rule>;
@@ -0,0 +1,172 @@
1
+ // src/registry.ts
2
+ import atomNoDeps from "./rules/atom-no-deps.js";
3
+ import boundaryTimeIsPrimitive from "./rules/boundary-time-is-primitive.js";
4
+ import cellMustBeStateful from "./rules/cell-must-be-stateful.js";
5
+ import cellMustNotComposeCell from "./rules/cell-must-not-compose-cell.js";
6
+ import cellNoCrossFeatureOrganelles from "./rules/cell-no-cross-feature-organelles.js";
7
+ import cellNoTissues from "./rules/cell-no-tissues.js";
8
+ import cellsFolderIndexIsBarrel from "./rules/cells-folder-index-is-barrel.js";
9
+ import compoundMustBeStateless from "./rules/compound-must-be-stateless.js";
10
+ import compoundNoStores from "./rules/compound-no-stores.js";
11
+ import constantsInConstantsFile from "./rules/constants-in-constants-file.js";
12
+ import dialectThroughTheSeam from "./rules/dialect-through-the-seam.js";
13
+ import durableBodyReadsThroughSteps from "./rules/durable-body-reads-through-steps.js";
14
+ import effectHookNaming from "./rules/effect-hook-naming.js";
15
+ import emitDeclaresAttempts from "./rules/emit-declares-attempts.js";
16
+ import fontRolesOnly from "./rules/font-roles-only.js";
17
+ import hermeticE2eNoWallClockWaits from "./rules/hermetic-e2e-no-wall-clock-waits.js";
18
+ import iconControlHasName from "./rules/icon-control-has-name.js";
19
+ import layerWalls from "./rules/layer-walls.js";
20
+ import listTakeWithoutOrder from "./rules/list-take-without-order.js";
21
+ import maxCommentDensity from "./rules/max-comment-density.js";
22
+ import moleculeAtomsOnly from "./rules/molecule-atoms-only.js";
23
+ import moleculeMustCompose from "./rules/molecule-must-compose.js";
24
+ import mutatingStepDeclaresRetry from "./rules/mutating-step-declares-retry.js";
25
+ import namedWhenBlock from "./rules/named-when-block.js";
26
+ import nextRouteSegmentIsThinDelegate from "./rules/next-route-segment-is-thin-delegate.js";
27
+ import noBrandNames from "./rules/no-brand-names.js";
28
+ import noCardShapedDiv from "./rules/no-card-shaped-div.js";
29
+ import noCrossFeatureStores from "./rules/no-cross-feature-stores.js";
30
+ import noD1Transaction from "./rules/no-d1-transaction.js";
31
+ import noDateStringCompare from "./rules/no-date-string-compare.js";
32
+ import noDuplicateJsxPatterns from "./rules/no-duplicate-jsx-patterns.js";
33
+ import noGenericUtilityPackage from "./rules/no-generic-utility-package.js";
34
+ import noHookInComponentDisguise from "./rules/no-hook-in-component-disguise.js";
35
+ import noIndexCountAsExact from "./rules/no-index-count-as-exact.js";
36
+ import noInertHiddenJsx from "./rules/no-inert-hidden-jsx.js";
37
+ import noInlineDataInJsx from "./rules/no-inline-data-in-jsx.js";
38
+ import noInvalidFeatureFolders from "./rules/no-invalid-feature-folders.js";
39
+ import noLoaderSideEffectInIndexFile from "./rules/no-loader-side-effect-in-index-file.js";
40
+ import noLogicInComponentFiles from "./rules/no-logic-in-component-files.js";
41
+ import noMockDbInIntegration from "./rules/no-mock-db-in-integration.js";
42
+ import noOrmOutsideDb from "./rules/no-orm-outside-db.js";
43
+ import noRawHtmlAtoms from "./rules/no-raw-html-atoms.js";
44
+ import noRawSqlOutsideAllowed from "./rules/no-raw-sql-outside-allowed.js";
45
+ import noReactNamespace from "./rules/no-react-namespace.js";
46
+ import noRenamedHtmlProps from "./rules/no-renamed-html-props.js";
47
+ import noRenderPropReader from "./rules/no-render-prop-reader.js";
48
+ import noRuntimeDeadAffordance from "./rules/no-runtime-dead-affordance.js";
49
+ import noSwallowedCatch from "./rules/no-swallowed-catch.js";
50
+ import noTrivialWrapperComponent from "./rules/no-trivial-wrapper-component.js";
51
+ import noTsInBioFolders from "./rules/no-ts-in-bio-folders.js";
52
+ import noTypeDefinitionsInComponents from "./rules/no-type-definitions-in-components.js";
53
+ import noUnregisteredPluginDir from "./rules/no-unregistered-plugin-dir.js";
54
+ import noVoidPort from "./rules/no-void-port.js";
55
+ import organelleDependency from "./rules/organelle-dependency.js";
56
+ import organelleSingleSource from "./rules/organelle-single-source.js";
57
+ import pluginRouteNamespaced from "./rules/plugin-route-namespaced.js";
58
+ import queriesRequireOrgScope from "./rules/queries-require-org-scope.js";
59
+ import queueLoopIsTheLibrary from "./rules/queue-loop-is-the-library.js";
60
+ import routeNoInlineMutation from "./rules/route-no-inline-mutation.js";
61
+ import routerSchemaParity from "./rules/router-schema-parity.js";
62
+ import serviceNameNotReserved from "./rules/service-name-not-reserved.js";
63
+ import ssotNoInlineFacts from "./rules/ssot-no-inline-facts.js";
64
+ import ssotNoProcessEnv from "./rules/ssot-no-process-env.js";
65
+ import stepDeclaresCompensationOrNone from "./rules/step-declares-compensation-or-none.js";
66
+ import stepOpensItsOwnCell from "./rules/step-opens-its-own-cell.js";
67
+ import storeRouteScopesTenantData from "./rules/store-route-scopes-tenant-data.js";
68
+ import subpathOverBarrel from "./rules/subpath-over-barrel.js";
69
+ import subscriberDeclaresId from "./rules/subscriber-declares-id.js";
70
+ import tablesDeclareTheirPlane from "./rules/tables-declare-their-plane.js";
71
+ import tenantTablesCarryOrgId from "./rules/tenant-tables-carry-org-id.js";
72
+ import testNoClockPollingForPromiseFlips from "./rules/test-no-clock-polling-for-promise-flips.js";
73
+ import timeThroughTheDoor from "./rules/time-through-the-door.js";
74
+ import tissueMustCompose from "./rules/tissue-must-compose.js";
75
+ import tissueNoDataProps from "./rules/tissue-no-data-props.js";
76
+ import tissueNoFetch from "./rules/tissue-no-fetch.js";
77
+ import tissueNoHooks from "./rules/tissue-no-hooks.js";
78
+ import tissueNoOrganelles from "./rules/tissue-no-organelles.js";
79
+ import tissueNoStores from "./rules/tissue-no-stores.js";
80
+ import todoNamesItsPlan from "./rules/todo-names-its-plan.js";
81
+ import tokensOnlyColors from "./rules/tokens-only-colors.js";
82
+ import uniqueQueryStepName from "./rules/unique-query-step-name.js";
83
+ import workerHandlesAreScoped from "./rules/worker-handles-are-scoped.js";
84
+ import zustandV5BestPractices from "./rules/zustand-v5-best-practices.js";
85
+ var registered = {
86
+ "atom-no-deps": atomNoDeps,
87
+ "boundary-time-is-primitive": boundaryTimeIsPrimitive,
88
+ "cell-must-be-stateful": cellMustBeStateful,
89
+ "cell-must-not-compose-cell": cellMustNotComposeCell,
90
+ "cell-no-cross-feature-organelles": cellNoCrossFeatureOrganelles,
91
+ "cell-no-tissues": cellNoTissues,
92
+ "cells-folder-index-is-barrel": cellsFolderIndexIsBarrel,
93
+ "compound-must-be-stateless": compoundMustBeStateless,
94
+ "compound-no-stores": compoundNoStores,
95
+ "constants-in-constants-file": constantsInConstantsFile,
96
+ "dialect-through-the-seam": dialectThroughTheSeam,
97
+ "durable-body-reads-through-steps": durableBodyReadsThroughSteps,
98
+ "effect-hook-naming": effectHookNaming,
99
+ "emit-declares-attempts": emitDeclaresAttempts,
100
+ "font-roles-only": fontRolesOnly,
101
+ "hermetic-e2e-no-wall-clock-waits": hermeticE2eNoWallClockWaits,
102
+ "icon-control-has-name": iconControlHasName,
103
+ "layer-walls": layerWalls,
104
+ "list-take-without-order": listTakeWithoutOrder,
105
+ "max-comment-density": maxCommentDensity,
106
+ "molecule-atoms-only": moleculeAtomsOnly,
107
+ "molecule-must-compose": moleculeMustCompose,
108
+ "mutating-step-declares-retry": mutatingStepDeclaresRetry,
109
+ "named-when-block": namedWhenBlock,
110
+ "next-route-segment-is-thin-delegate": nextRouteSegmentIsThinDelegate,
111
+ "no-brand-names": noBrandNames,
112
+ "no-card-shaped-div": noCardShapedDiv,
113
+ "no-cross-feature-stores": noCrossFeatureStores,
114
+ "no-d1-transaction": noD1Transaction,
115
+ "no-date-string-compare": noDateStringCompare,
116
+ "no-duplicate-jsx-patterns": noDuplicateJsxPatterns,
117
+ "no-generic-utility-package": noGenericUtilityPackage,
118
+ "no-hook-in-component-disguise": noHookInComponentDisguise,
119
+ "no-index-count-as-exact": noIndexCountAsExact,
120
+ "no-inert-hidden-jsx": noInertHiddenJsx,
121
+ "no-inline-data-in-jsx": noInlineDataInJsx,
122
+ "no-invalid-feature-folders": noInvalidFeatureFolders,
123
+ "no-loader-side-effect-in-index-file": noLoaderSideEffectInIndexFile,
124
+ "no-logic-in-component-files": noLogicInComponentFiles,
125
+ "no-mock-db-in-integration": noMockDbInIntegration,
126
+ "no-orm-outside-db": noOrmOutsideDb,
127
+ "no-raw-html-atoms": noRawHtmlAtoms,
128
+ "no-raw-sql-outside-allowed": noRawSqlOutsideAllowed,
129
+ "no-react-namespace": noReactNamespace,
130
+ "no-renamed-html-props": noRenamedHtmlProps,
131
+ "no-render-prop-reader": noRenderPropReader,
132
+ "no-runtime-dead-affordance": noRuntimeDeadAffordance,
133
+ "no-swallowed-catch": noSwallowedCatch,
134
+ "no-trivial-wrapper-component": noTrivialWrapperComponent,
135
+ "no-ts-in-bio-folders": noTsInBioFolders,
136
+ "no-type-definitions-in-components": noTypeDefinitionsInComponents,
137
+ "no-unregistered-plugin-dir": noUnregisteredPluginDir,
138
+ "no-void-port": noVoidPort,
139
+ "organelle-dependency": organelleDependency,
140
+ "organelle-single-source": organelleSingleSource,
141
+ "plugin-route-namespaced": pluginRouteNamespaced,
142
+ "queries-require-org-scope": queriesRequireOrgScope,
143
+ "queue-loop-is-the-library": queueLoopIsTheLibrary,
144
+ "route-no-inline-mutation": routeNoInlineMutation,
145
+ "router-schema-parity": routerSchemaParity,
146
+ "service-name-not-reserved": serviceNameNotReserved,
147
+ "ssot-no-inline-facts": ssotNoInlineFacts,
148
+ "ssot-no-process-env": ssotNoProcessEnv,
149
+ "step-declares-compensation-or-none": stepDeclaresCompensationOrNone,
150
+ "step-opens-its-own-cell": stepOpensItsOwnCell,
151
+ "store-route-scopes-tenant-data": storeRouteScopesTenantData,
152
+ "subpath-over-barrel": subpathOverBarrel,
153
+ "subscriber-declares-id": subscriberDeclaresId,
154
+ "tables-declare-their-plane": tablesDeclareTheirPlane,
155
+ "tenant-tables-carry-org-id": tenantTablesCarryOrgId,
156
+ "test-no-clock-polling-for-promise-flips": testNoClockPollingForPromiseFlips,
157
+ "time-through-the-door": timeThroughTheDoor,
158
+ "tissue-must-compose": tissueMustCompose,
159
+ "tissue-no-data-props": tissueNoDataProps,
160
+ "tissue-no-fetch": tissueNoFetch,
161
+ "tissue-no-hooks": tissueNoHooks,
162
+ "tissue-no-organelles": tissueNoOrganelles,
163
+ "tissue-no-stores": tissueNoStores,
164
+ "todo-names-its-plan": todoNamesItsPlan,
165
+ "tokens-only-colors": tokensOnlyColors,
166
+ "unique-query-step-name": uniqueQueryStepName,
167
+ "worker-handles-are-scoped": workerHandlesAreScoped,
168
+ "zustand-v5-best-practices": zustandV5BestPractices
169
+ };
170
+ export {
171
+ registered
172
+ };
@@ -9,6 +9,7 @@ declare const atomNoDeps: {
9
9
  }) => void;
10
10
  }): import('./lib/visitors.js').Visitor;
11
11
  fixShape: string;
12
+ probe?: ((options: readonly unknown[]) => import('./lib/probe.js').Probe | import('./lib/probe.js').ProbeTree) | undefined;
12
13
  meta: {
13
14
  docs: {
14
15
  description: string;
@@ -1,25 +1,24 @@
1
1
  // src/rules/atom-no-deps.ts
2
2
  import { directionRule } from "./lib/direction-rule.js";
3
- var FORBIDDEN_PATTERNS = [
4
- /(?:^|\/)molecules\//,
5
- /(?:^|\/)compounds\//,
6
- /(?:^|\/)organelles\//,
7
- /(?:^|\/)cells\//,
8
- /(?:^|\/)tissues\//,
9
- /(?:^|\/)stores\//,
10
- /(?:^|[/@])domains(?:\/|$)/,
11
- /\/app\//
12
- ];
3
+ import { inTiersAbove, APP_FOLDER, STORES_FOLDER } from "./lib/tiers.js";
4
+ var FORBIDDEN_PATTERNS = [STORES_FOLDER, /(?:^|[/@])domains(?:\/|$)/, APP_FOLDER];
13
5
  var atomNoDeps = directionRule({
14
6
  description: "Atoms cannot import from molecules, compounds, organelles, cells, stores, or domains",
15
7
  fixShape: `Raw \`<button>\`, \`<input>\`, or a \`<div>\` with border + background in levels 2-6 belongs in an atom.
16
8
  An atom imports nothing above itself — no molecule, compound, organelle, cell, tissue, store or domain,
17
9
  and that includes \`export … from\`. If an atom needs a shared constant, the constant moves down to the atom
18
10
  tier or into a plain \`lib/\` module, never the other way round.`,
19
- forbidden: (target) => FORBIDDEN_PATTERNS.some((pattern) => pattern.test(target.path)),
11
+ forbidden: (target) => inTiersAbove(target.path, "atoms") || FORBIDDEN_PATTERNS.some((pattern) => pattern.test(target.path)),
20
12
  messages: {
21
13
  forbidden: "Atoms cannot import from {{source}}. Atoms are the lowest level and must not depend on higher-level components."
22
14
  },
15
+ probe: () => ({
16
+ path: "src/atoms/geonosis-probe.tsx",
17
+ source: `import { Up } from '../molecules/geonosis-up'
18
+
19
+ export const GeonosisProbe = Up
20
+ `
21
+ }),
23
22
  scope: "atoms",
24
23
  typeOnly: "visit"
25
24
  });
@@ -1,9 +1,7 @@
1
1
  import { type RuleContextWithOptions } from './lib/context.js';
2
2
  import { type Probe } from './lib/probe.js';
3
3
  interface Options {
4
- /** The modules that may name these types in an exported signature, each written out in full. */
5
4
  doors?: string[];
6
- /** The type names that must not cross an exported boundary. A namespace covers its members. */
7
5
  types?: string[];
8
6
  }
9
7
  declare const boundaryTimeIsPrimitive: {
@@ -46,11 +44,6 @@ declare const boundaryTimeIsPrimitive: {
46
44
  }[];
47
45
  type: 'problem';
48
46
  };
49
- /**
50
- * A file that is not one of the declared doors, exporting the repo's own first forbidden type.
51
- * `String(at)` rather than a second annotation: one finding is the claim, and a probe that fired
52
- * twice would pass a count nobody asked for.
53
- */
54
47
  probe(options: readonly unknown[]): Probe;
55
48
  };
56
49
  export default boundaryTimeIsPrimitive;
@@ -1,11 +1,10 @@
1
1
  import { type RuleContext } from './lib/context.js';
2
+ import { type Probe } from './lib/probe.js';
2
3
  declare const cellMustBeStateful: {
3
4
  create(context: RuleContext): {
4
5
  Program?: undefined;
5
- 'Program:exit'?: undefined;
6
6
  } | {
7
7
  Program(node: unknown): void;
8
- 'Program:exit'(): void;
9
8
  };
10
9
  fixShape: string;
11
10
  meta: {
@@ -18,5 +17,6 @@ declare const cellMustBeStateful: {
18
17
  schema: never[];
19
18
  type: 'problem';
20
19
  };
20
+ probe(): Probe;
21
21
  };
22
22
  export default cellMustBeStateful;
@@ -1,7 +1,8 @@
1
1
  // src/rules/cell-must-be-stateful.ts
2
2
  import * as fs from "node:fs";
3
- import { normalise } from "./lib/paths.js";
3
+ import { normalise, fileOf } from "./lib/paths.js";
4
4
  import { launderedHomesOf } from "./lib/reexports.js";
5
+ import { CELL_PRIVATE_SIBLING, inTier } from "./lib/tiers.js";
5
6
  var STATE_SIGNATURE = /\buse[A-Z]\w*\s*\(|from\s+['"][^'"]*\/organelles(?:\/|['"])|from\s+['"][^'"]*\/stores?(?:\/|['"])|createContext\s*\(/;
6
7
  var SIBLING_SUFFIXES = [
7
8
  ".tsx",
@@ -15,41 +16,32 @@ var SIBLING_SUFFIXES = [
15
16
  ".data.tsx",
16
17
  ".steps.tsx"
17
18
  ];
18
- var PRIVATE_OR_NON_ENTRY = /\.(parts|sections|layouts|types|icons|sidebar|summary|form|data|steps|stories|test|spec)\.tsx$/;
19
- function familyHasState(mainFile) {
20
- const stem = mainFile.replace(/\.tsx$/, "");
21
- for (const file of [mainFile, ...SIBLING_SUFFIXES.map((suffix) => stem + suffix)]) {
22
- try {
23
- if (fs.existsSync(file) && STATE_SIGNATURE.test(fs.readFileSync(file, "utf8"))) {
24
- return true;
25
- }
26
- } catch {}
19
+ var readable = (file) => {
20
+ try {
21
+ return fs.readFileSync(file, "utf8");
22
+ } catch {
23
+ return "";
27
24
  }
28
- return false;
29
- }
25
+ };
26
+ var familyHasState = (mainFile) => {
27
+ const stem = mainFile.replace(/\.tsx$/, "");
28
+ return [mainFile, ...SIBLING_SUFFIXES.map((suffix) => stem + suffix)].some((file) => STATE_SIGNATURE.test(readable(file)));
29
+ };
30
30
  var cellMustBeStateful = {
31
31
  create(context) {
32
32
  const normalized = normalise(context.filename);
33
- const base = normalized.split("/").pop() || "";
34
- const isCellEntrypoint = /(?:^|\/)cells\//.test(normalized) && base.endsWith(".tsx") && base !== "index.tsx" && !PRIVATE_OR_NON_ENTRY.test(base);
33
+ const base = fileOf(normalized);
34
+ const isCellEntrypoint = inTier(normalized, "cells") && base.endsWith(".tsx") && base !== "index.tsx" && !CELL_PRIVATE_SIBLING.test(base);
35
35
  if (!isCellEntrypoint)
36
36
  return {};
37
37
  const laundered = launderedHomesOf(context.filename);
38
38
  if (laundered !== undefined && laundered.length === 0)
39
39
  return {};
40
40
  const judged = laundered ?? [normalized];
41
- let programNode = null;
42
41
  return {
43
42
  Program(node) {
44
- programNode = node;
45
- },
46
- "Program:exit"() {
47
43
  if (!judged.every((file) => familyHasState(normalise(file)))) {
48
- context.report({
49
- data: { file: base },
50
- messageId: "shouldBeCompound",
51
- node: programNode
52
- });
44
+ context.report({ data: { file: base }, messageId: "shouldBeCompound", node });
53
45
  }
54
46
  }
55
47
  };
@@ -67,6 +59,15 @@ stateful, put the store read or the organelle in it rather than taking the data
67
59
  },
68
60
  schema: [],
69
61
  type: "problem"
62
+ },
63
+ probe() {
64
+ return {
65
+ path: "src/cells/geonosis-probe.tsx",
66
+ source: `export function GeonosisProbe({ label }: { label: string }) {
67
+ return <div>{label}</div>
68
+ }
69
+ `
70
+ };
70
71
  }
71
72
  };
72
73
  var cell_must_be_stateful_default = cellMustBeStateful;
@@ -9,6 +9,7 @@ declare const cellMustNotComposeCell: {
9
9
  }) => void;
10
10
  }): import('./lib/visitors.js').Visitor;
11
11
  fixShape: string;
12
+ probe?: ((options: readonly unknown[]) => import('./lib/probe.js').Probe | import('./lib/probe.js').ProbeTree) | undefined;
12
13
  meta: {
13
14
  docs: {
14
15
  description: string;
@@ -1,8 +1,8 @@
1
1
  // src/rules/cell-must-not-compose-cell.ts
2
2
  import { isCellsBarrel } from "./lib/cells-barrel.js";
3
3
  import { directionRule } from "./lib/direction-rule.js";
4
+ import { inTier } from "./lib/tiers.js";
4
5
  var cellStem = (filePath) => filePath.replace(/\.[jt]sx?$/, "").replace(/\.(parts|sections|summary|layouts|icons|types|hooks|utils|context|store|sidebar|data|config)$/i, "");
5
- var IN_CELLS = /(?:^|\/)cells\//;
6
6
  var cellMustNotComposeCell = directionRule({
7
7
  data: (target, importer) => ({ currentFile: importer.file, importedFile: target.file }),
8
8
  description: "Cells must not import from any other cell anywhere in the repo. No same-feature exception, no shared/document/charts exception. Cells compose only atoms, molecules, compounds, and organelles. Type-only imports are allowed.",
@@ -17,11 +17,18 @@ the other. A stateless adapter between cells is a tissue — it may take the dat
17
17
  component (\`invoice-editor.parts.tsx\` from \`invoice-editor.tsx\`) are one cell and are fine. The
18
18
  barrels \`cells/index.ts\`, \`cells/dynamic.ts\` and \`cells/dynamic.tsx\` are exempt — re-exporting
19
19
  siblings is the shape \`cells-folder-index-is-barrel\` requires of them.`,
20
- forbidden: (target, importer) => IN_CELLS.test(target.path) && cellStem(target.file) !== cellStem(importer.file),
20
+ forbidden: (target, importer) => inTier(target.path, "cells") && cellStem(target.file) !== cellStem(importer.file),
21
21
  messages: {
22
22
  forbidden: 'Cell "{{currentFile}}" imports from another cell "{{importedFile}}". Cells never compose other cells. Decompose: if this is arrangement, move the parent to tissues/. If the import is a stateless display, move the child to compounds/. If the child is a store adapter, move it to organelles/.'
23
23
  },
24
- scope: (importer) => IN_CELLS.test(importer.path) && !isCellsBarrel(importer.path)
24
+ probe: () => ({
25
+ path: "src/cells/geonosis-probe.tsx",
26
+ source: `import { Other } from './geonosis-other'
27
+
28
+ export const GeonosisProbe = Other
29
+ `
30
+ }),
31
+ scope: (importer) => inTier(importer.path, "cells") && !isCellsBarrel(importer.path)
25
32
  });
26
33
  var cell_must_not_compose_cell_default = cellMustNotComposeCell;
27
34
  export {
@@ -9,6 +9,7 @@ declare const cellNoCrossFeatureOrganelles: {
9
9
  }) => void;
10
10
  }): import('./lib/visitors.js').Visitor;
11
11
  fixShape: string;
12
+ probe?: ((options: readonly unknown[]) => import('./lib/probe.js').Probe | import('./lib/probe.js').ProbeTree) | undefined;
12
13
  meta: {
13
14
  docs: {
14
15
  description: string;
@@ -28,6 +28,13 @@ aliased sources are judged alike: \`../../billing/organelles/totals\` lands wher
28
28
  forbidden: `Cell "{{file}}" in feature "{{ownFeature}}" imports "{{source}}", an organelle of feature "{{importedFeature}}". A cell hosts its own feature's organelles; another feature's state arrives as props from the tissue above. Only features/{{ownFeature}}/organelles/* and the shared features are allowed here.`
29
29
  },
30
30
  optionsFrom: (raw) => raw ?? {},
31
+ probe: () => ({
32
+ path: "src/features/geonosis/cells/geonosis-probe.tsx",
33
+ source: `import { other } from '@/features/geonosis-neighbour/organelles/other'
34
+
35
+ export const GeonosisProbe = other
36
+ `
37
+ }),
31
38
  scope: "cells",
32
39
  schema: SHARED_FEATURES_SCHEMA
33
40
  });
@@ -9,6 +9,7 @@ declare const cellNoTissues: {
9
9
  }) => void;
10
10
  }): import('./lib/visitors.js').Visitor;
11
11
  fixShape: string;
12
+ probe?: ((options: readonly unknown[]) => import('./lib/probe.js').Probe | import('./lib/probe.js').ProbeTree) | undefined;
12
13
  meta: {
13
14
  docs: {
14
15
  description: string;
@@ -1,20 +1,24 @@
1
1
  // src/rules/cell-no-tissues.ts
2
2
  import { directionRule } from "./lib/direction-rule.js";
3
- var HIGHER_LEVEL_PATTERNS = [
4
- /(?:^|\/)(?:features\/[^/]+\/)?tissues\//,
5
- /(?:^|\/)(?:features\/[^/]+\/)?organs\//,
6
- /(?:^|\/)layouts\//,
7
- /(?:^|\/)app\//
8
- ];
3
+ import { matchesAny } from "./lib/paths.js";
4
+ import { APP_FOLDER, inOrgans, inTier, LAYOUTS_FOLDER } from "./lib/tiers.js";
5
+ var OTHER_HIGHER_LEVEL_FOLDERS = [LAYOUTS_FOLDER, APP_FOLDER];
9
6
  var cellNoTissues = directionRule({
10
7
  description: "Cells cannot import tissues, organs, or app route files. Direction is wrong: tissues compose cells (not the reverse), and organs compose tissues and cells.",
11
8
  fixShape: `A cell cannot import a tissue, an organ, a layout or an app route — that is the direction
12
9
  inverted. If a cell needs a layout around it, take the layout as \`children\` and let a tissue arrange
13
10
  both. Type-only imports are exempt; \`export … from\` is not.`,
14
- forbidden: (target) => HIGHER_LEVEL_PATTERNS.some((pattern) => pattern.test(target.path)),
11
+ forbidden: (target) => inTier(target.path, "tissues") || inOrgans(target.path) || matchesAny(OTHER_HIGHER_LEVEL_FOLDERS, target.path),
15
12
  messages: {
16
13
  forbidden: 'Cell "{{file}}" cannot import higher-level component "{{source}}". Direction is wrong: tissues/organs compose cells, not the reverse. If this cell needs to render a layout-like wrapper, the wrapper should be a compound, or this cell should be promoted.'
17
14
  },
15
+ probe: () => ({
16
+ path: "src/cells/geonosis-probe.tsx",
17
+ source: `import { Up } from '../tissues/geonosis-up'
18
+
19
+ export const GeonosisProbe = Up
20
+ `
21
+ }),
18
22
  scope: "cells"
19
23
  });
20
24
  var cell_no_tissues_default = cellNoTissues;
@@ -1,24 +1,24 @@
1
1
  import { type RuleContext } from './lib/context.js';
2
- interface ProgramNode {
3
- body: Statement[];
4
- loc?: unknown;
5
- type: 'Program';
6
- }
2
+ import { type Probe } from './lib/probe.js';
7
3
  interface Statement {
8
- declaration?: unknown;
9
4
  importKind?: string;
10
- loc?: unknown;
11
5
  source?: {
12
- value: string;
6
+ value?: unknown;
13
7
  } | null;
14
- specifiers?: unknown[];
8
+ specifiers?: {
9
+ exported?: {
10
+ name?: unknown;
11
+ };
12
+ }[];
15
13
  type: string;
16
14
  }
17
15
  declare const cellsFolderIndexIsBarrel: {
18
16
  create(context: RuleContext): {
19
17
  Program?: undefined;
20
18
  } | {
21
- Program(node: ProgramNode): void;
19
+ Program(node: {
20
+ body: Statement[];
21
+ }): void;
22
22
  };
23
23
  fixShape: string;
24
24
  meta: {
@@ -26,10 +26,12 @@ declare const cellsFolderIndexIsBarrel: {
26
26
  description: string;
27
27
  };
28
28
  messages: {
29
+ organelleWearingCells: string;
29
30
  notBarrel: string;
30
31
  };
31
32
  schema: never[];
32
33
  type: 'problem';
33
34
  };
35
+ probe(): Probe;
34
36
  };
35
37
  export default cellsFolderIndexIsBarrel;