@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
@@ -1,47 +1,21 @@
1
- /**
2
- * The one place a filename becomes a path.
3
- *
4
- * oxlint hands a rule the name the platform gave it, so on Windows it arrives with backslashes and
5
- * every pattern in this plugin — which is written with `/` — misses. Thirty-seven rules carried
6
- * their own copy of the conversion, spelled three different ways, and one of them fixed only the
7
- * first separator. A single fix to how a path is read had to be found rule by rule; now there is
8
- * one line to find.
9
- */
10
1
  export declare const normalise: (filename: string) => string;
11
- /**
12
- * The last segment of a path — what a diagnostic calls the file. An empty last segment is not a
13
- * name, so a path that ends in a separator answers with itself rather than with `""`.
14
- */
15
2
  export declare const fileOf: (path: string) => string;
16
- /**
17
- * Whether a file IS a door a repo named — one grammar for every rule that has doors (#204).
18
- *
19
- * A repo writes its time module down once and enables two rules with it. `time-through-the-door`
20
- * took a REGEX over the filename; `boundary-time-is-primitive` compared a plain path, so the same
21
- * spelling — `packages/utils/src/time\.ts$`, or the specifier callers import it by — made the
22
- * second rule report the door's own signatures. All three spellings are accepted here: the exact
23
- * path, a path the filename ends with (a specifier is one), and a regex over the filename.
24
- */
25
3
  export declare const isDoor: (filename: string, door: string) => boolean;
26
- /**
27
- * Whether a file IS one of an ENUMERATED list of modules — never a pattern.
28
- *
29
- * The counterpart to a path grant, and deliberately not `isDoor`: a door accepts a regex, and a
30
- * regex is exactly the glob whose exemption stops wherever it happens to stop. An entry here names
31
- * one module, by its full path or by the tail a repo writes it as, so the count of exemptions is a
32
- * number a reader can hold.
33
- */
34
4
  export declare const isModule: (filename: string, module: string) => boolean;
35
- /**
36
- * Whether a NORMALISED path sits inside a `__tests__/` directory — the exemption five rules
37
- * (tissue-no-data-props, next-route-segment-is-thin-delegate, no-inert-hidden-jsx,
38
- * no-hook-in-component-disguise, no-trivial-wrapper-component) each spelled as their own
39
- * `/__tests__\//` literal. Takes an already-`normalise`d path, the shape every call site already
40
- * held in a local before testing it.
41
- */
5
+ export declare const isExempt: (filename: string, { allow, exemptModules }: {
6
+ allow?: string[];
7
+ exemptModules?: string[];
8
+ }) => boolean;
42
9
  export declare const isTestFile: (path: string) => boolean;
43
- /**
44
- * Whether a NORMALISED path matches any of a list of regex sources — `dialect-through-the-seam`
45
- * and `no-d1-transaction` each carried this loop by hand, over the same shape of option.
46
- */
47
10
  export declare const anyOf: (patterns: readonly string[], path: string) => boolean;
11
+ export declare const matchesAny: (patterns: readonly RegExp[], path: string) => boolean;
12
+ export declare const trimSlashes: (path: string) => string;
13
+ export declare const rootedAt: (filename: string, directory: string) => {
14
+ after: string;
15
+ repoRoot: string;
16
+ } | null;
17
+ export declare const HARNESS_PATHS: string[];
18
+ export declare const ROUTE_FALLBACK_BASENAMES: Set<string>;
19
+ export declare const LAYOUT_BASENAMES: Set<string>;
20
+ export declare const ROUTE_BODY_BASENAMES: Set<string>;
21
+ export declare const ROUTE_BASENAMES: Set<string>;
@@ -16,13 +16,46 @@ var isModule = (filename, module) => {
16
16
  const named = module.replace(/^\.?\//, "");
17
17
  return path === named || path.endsWith(`/${named}`);
18
18
  };
19
+ var isExempt = (filename, { allow = [], exemptModules = [] }) => exemptModules.some((module) => isModule(filename, module)) || allow.some((path) => filename.includes(path));
19
20
  var isTestFile = (path) => /__tests__\//.test(path);
20
21
  var anyOf = (patterns, path) => patterns.some((pattern) => new RegExp(pattern).test(path));
22
+ var matchesAny = (patterns, path) => patterns.some((pattern) => pattern.test(path));
23
+ var trimSlashes = (path) => path.replace(/^\/+/, "").replace(/\/+$/, "");
24
+ var rootedAt = (filename, directory) => {
25
+ const marker = `/${directory}/`;
26
+ const at = filename.indexOf(marker);
27
+ return at < 0 ? null : { after: filename.slice(at + marker.length), repoRoot: filename.slice(0, at) };
28
+ };
29
+ var HARNESS_PATHS = [
30
+ String.raw`/__tests__/`,
31
+ String.raw`/scripts/`,
32
+ String.raw`/testing/`,
33
+ String.raw`/seeds/`
34
+ ];
35
+ var ROUTE_FALLBACK_BASENAMES = new Set([
36
+ "default.tsx",
37
+ "error.tsx",
38
+ "global-error.tsx",
39
+ "loading.tsx",
40
+ "not-found.tsx"
41
+ ]);
42
+ var LAYOUT_BASENAMES = new Set(["layout.tsx", "template.tsx"]);
43
+ var ROUTE_BODY_BASENAMES = new Set([...LAYOUT_BASENAMES, "page.tsx"]);
44
+ var ROUTE_BASENAMES = new Set([...ROUTE_FALLBACK_BASENAMES, ...ROUTE_BODY_BASENAMES]);
21
45
  export {
46
+ HARNESS_PATHS,
47
+ LAYOUT_BASENAMES,
48
+ ROUTE_BASENAMES,
49
+ ROUTE_BODY_BASENAMES,
50
+ ROUTE_FALLBACK_BASENAMES,
22
51
  anyOf,
23
52
  fileOf,
24
53
  isDoor,
54
+ isExempt,
25
55
  isModule,
26
56
  isTestFile,
27
- normalise
57
+ matchesAny,
58
+ normalise,
59
+ rootedAt,
60
+ trimSlashes
28
61
  };
@@ -1,16 +1,6 @@
1
- /**
2
- * The longest match for a wildcard pattern against a source string — tsc's own rule for
3
- * `tsconfig.json` `paths`, and the same rule Node uses for `package.json` `imports`. An exact key
4
- * wins outright; a pattern with one `*` matches when the source starts with what comes before it
5
- * and ends with what comes after, and among those the longest fixed (non-star) part wins.
6
- *
7
- * `tsconfig-paths.ts` and `package-imports.ts` each carried this loop by hand, spelled identically
8
- * down to the variable names — one truth about how a wildcard map is searched, read for two
9
- * different maps. What each caller does with the match (a `paths` entry names several targets and
10
- * takes `target.replace`; an `imports` value is one target read through `conditionTargetOf` and
11
- * takes `target.replaceAll`) is NOT unified here, because those two shapes are not the same truth.
12
- */
13
1
  export declare const longestMatch: <Value>(entries: Iterable<[string, Value]>, source: string) => {
14
2
  matched: string;
15
3
  value: Value;
16
4
  } | undefined;
5
+ export declare const escapeForRegex: (value: string) => string;
6
+ export declare const readableSource: (pattern: string) => string;
@@ -24,6 +24,10 @@ var longestMatch = (entries, source) => {
24
24
  }
25
25
  return best === undefined ? undefined : { matched: best.matched, value: best.value };
26
26
  };
27
+ var escapeForRegex = (value) => value.replaceAll(/[.*+?^${}()|[\]\\]/g, "\\$&");
28
+ var readableSource = (pattern) => pattern.replaceAll("\\", "").replace(/\$$/, "");
27
29
  export {
28
- longestMatch
30
+ escapeForRegex,
31
+ longestMatch,
32
+ readableSource
29
33
  };
@@ -1,55 +1,12 @@
1
- /**
2
- * The file a rule says MUST fire it, given the options it was handed.
3
- *
4
- * `requireOption` closed one hole: an enabled rule with no option reports that, once a run,
5
- * instead of reading as a clean tree. It leaves the next one open. A rule CONFIGURED with a repo's own
6
- * vocabulary — its own brand, `packages/workflows/src/`, `@repo/db` — cannot be exercised by a corpus
7
- * that ships fixtures saying `acme` and `corpus/workflows/`, so "fires nowhere" is what an
8
- * adoption's doctor reports about a rule the repo is enforcing perfectly well. Both consumers filed
9
- * exactly that on 2026-08-30, and both were right about the fact and wrong about whose defect it is.
10
- *
11
- * A probe is the answer the rule itself can give: hand it the options and it writes the smallest
12
- * source that must fire it under THOSE options, at a path that lands where it looks. It is a claim,
13
- * so it is tested through the real binary — a probe that does not fire is a rule that cannot be
14
- * exercised, and the honest thing then is to say so rather than to widen the probe until it does.
15
- */
16
1
  export type Probe = {
17
- /** Repo-root-relative, `/`-separated. It must satisfy every PATH option the rule reads. */
18
2
  path: string;
19
3
  source: string;
20
4
  };
21
- /**
22
- * A probe that needs a TREE rather than a file.
23
- *
24
- * A rule that reads a sibling off disk — a registry, a schema's twin — cannot be exercised by one
25
- * file however right its source is: `no-unregistered-plugin-dir` returns silently when the registry
26
- * its options name is not there, so the single-file probe fired nothing and every consumer
27
- * enforcing the rule over a real registry read as a permanent FAIL. The answer is not a widened
28
- * probe, which would be the rule claiming evidence it does not have; it is a probe that may declare
29
- * the sibling too, and a reader that mounts all of them.
30
- */
31
5
  export type ProbeTree = {
32
6
  files: readonly Probe[];
33
7
  };
34
8
  export type ProbeFn = (options: readonly unknown[]) => Probe | ProbeTree;
35
- /** The files a probe declares, whichever of the two forms it answered in. */
36
9
  export declare const filesOf: (answer: Probe | ProbeTree) => readonly Probe[];
37
- /** The bag a rule reads its keyed options out of, whatever the caller passed. */
38
10
  export declare const bagOf: <Options>(options: readonly unknown[]) => Partial<Options>;
39
- /**
40
- * The first configured value, or the first default. A probe reads the option the same way `create`
41
- * does, defaults included — otherwise a repo keeping a default would get a probe built for a value
42
- * its rule is not using, which fires nowhere and blames the repo for it.
43
- */
44
11
  export declare const firstOf: (configured: readonly string[] | undefined, fallback: readonly string[]) => string;
45
- /**
46
- * A path a regex option would match, built back out of the regex.
47
- *
48
- * The path options in this plugin are regex SOURCES — `within`, `paths`, `directories`, a layer's
49
- * `paths` — so a probe cannot be written against them without inverting one. This inverts the
50
- * subset those options are actually written in: anchors, non-capturing groups, one-segment
51
- * wildcards and escaped separators. It is deliberately not a general inverse, and it never has to
52
- * be trusted: the caller tests the result against the pattern it came from and refuses when it
53
- * does not match, which turns a wrong guess into a sentence rather than into a silent zero.
54
- */
55
12
  export declare const pathMatching: (pattern: string, leaf: string) => string;
@@ -1,20 +1,2 @@
1
- /**
2
- * A pure re-export shim (only `export … from`, no component) is a compatibility alias left behind
3
- * when a component moves to a feature folder — not the tier it sits in. Three rules read this off
4
- * the file, each with its own copy.
5
- */
6
1
  export declare function isReexportOnly(file: string): boolean;
7
- /**
8
- * Where a shim's code LIVES, when that is not the shim's own tier — or undefined when the file is
9
- * not a pure re-export shim at all.
10
- *
11
- * The three "must" rules exempted every shim outright, and that was #11's hole surviving in three
12
- * rules the direction rules' fix did not reach (row 594): `cells/laundered.tsx` re-exporting a
13
- * stateless compound is a cell that holds no state, and `tissues/alias.tsx` re-exporting a compound
14
- * is a tissue that composes nothing. Each name the shim hands on is followed to the file that
15
- * declares it, as the direction rules follow it; an `export *` is judged at the module it hands
16
- * through. A home in the shim's own tier is the alias the exemption was written for and is not
17
- * named. A re-export nothing on disk answers — a bare package — is not named either: unresolved
18
- * stays exempt, exactly as before.
19
- */
20
2
  export declare const launderedHomesOf: (file: string) => string[] | undefined;
@@ -1,8 +1,3 @@
1
- /**
2
- * Whether a file RENDERS one of the tiers it imports, not merely names it — a gate satisfied by an
3
- * import nobody renders was not satisfied. `molecule-must-compose` and `tissue-must-compose` both
4
- * read this off the import list and the JSX tree; each had its own copy of the reader.
5
- */
6
1
  type ImportSpecifierNode = {
7
2
  imported?: {
8
3
  name?: string;
@@ -45,10 +40,7 @@ export type JsxOpeningElementNode = {
45
40
  };
46
41
  type: 'JSXOpeningElement';
47
42
  };
48
- /** The identifier a JSX tag renders under — `<Foo/>`'s own name, or `<NS.Foo/>`'s namespace root. */
49
43
  export declare const rootTagNameOf: (name: JsxOpeningElementNode['name'] | undefined) => string | undefined;
50
- /** The local names an import brings in, added to `into` when its source composes and it is a value. */
51
44
  export declare const collectCompositionLocals: (node: CompositionImportNode, isComposition: (source: string) => boolean, into: Set<string>) => void;
52
- /** Whether any of the imported locals was actually rendered as a JSX tag. */
53
45
  export declare const rendersAnyOf: (locals: ReadonlySet<string>, renderedTagNames: ReadonlySet<string>) => boolean;
54
46
  export {};
@@ -0,0 +1,18 @@
1
+ type Override = {
2
+ files: string[];
3
+ rules: Record<string, unknown>;
4
+ };
5
+ type Run = {
6
+ candidates: string[];
7
+ overrides: Override[];
8
+ prefixes: string[];
9
+ root: string;
10
+ rules: Record<string, unknown>;
11
+ };
12
+ export declare const runOf: ({ argv, cwd }: {
13
+ argv: readonly string[];
14
+ cwd: string;
15
+ }) => Run | null;
16
+ export declare const designatedIn: (run: Run, rule: string, accepts: (file: string, text: string) => boolean) => string | null;
17
+ export declare const designatedFile: (rule: string, salt?: string, accepts?: (file: string, text: string) => boolean) => string | null;
18
+ export {};
@@ -0,0 +1,144 @@
1
+ // src/rules/lib/run-files.ts
2
+ import { spawnSync } from "node:child_process";
3
+ import { closeSync, existsSync, openSync, readFileSync, readSync } from "node:fs";
4
+ import { basename, dirname, join, matchesGlob, relative, resolve } from "node:path";
5
+ import { parseLooseJson } from "./module-text.js";
6
+ var LINTED = /\.(?:[cm]?[jt]sx?)$/;
7
+ var HEAD_BYTES = 4096;
8
+ var SWITCHES_OFF = /\b(?:oxlint|eslint)-disable(?!-(?:next-)?line)\b/;
9
+ var PLUGIN_NAME = "biological-architecture";
10
+ var FLAGS_WITH_VALUE = new Set([
11
+ "-c",
12
+ "--config",
13
+ "-f",
14
+ "--format",
15
+ "--threads",
16
+ "--tsconfig",
17
+ "-D",
18
+ "-W",
19
+ "-A",
20
+ "--deny",
21
+ "--warn",
22
+ "--allow",
23
+ "--max-warnings"
24
+ ]);
25
+ var UNKNOWABLE = ["--ignore-path", "--ignore-pattern", "--no-ignore", "--lsp", "--stdin-filepath"];
26
+ var recordOf = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? Object.fromEntries(Object.entries(value)) : {};
27
+ var stringsOf = (value) => Array.isArray(value) ? value.filter((one) => typeof one === "string") : [];
28
+ var ignoredBy = (path, patterns) => patterns.some((pattern) => {
29
+ const bare = pattern.replace(/^\.\//, "").replace(/\/$/, "");
30
+ if (!bare.includes("/"))
31
+ return path.split("/").some((segment) => matchesGlob(segment, bare));
32
+ return matchesGlob(path, bare) || matchesGlob(path, `${bare}/**`);
33
+ });
34
+ var looselyMatches = (path, glob) => matchesGlob(path, glob) || matchesGlob(path, `**/${glob}`) || matchesGlob(basename(path), glob);
35
+ var switchedOff = (file) => {
36
+ let handle;
37
+ try {
38
+ handle = openSync(file, "r");
39
+ const buffer = Buffer.alloc(HEAD_BYTES);
40
+ const read = readSync(handle, buffer, 0, HEAD_BYTES, 0);
41
+ return SWITCHES_OFF.test(buffer.subarray(0, read).toString("utf8"));
42
+ } catch {
43
+ return true;
44
+ } finally {
45
+ if (handle !== undefined)
46
+ closeSync(handle);
47
+ }
48
+ };
49
+ var prefixesOf = (jsPlugins) => {
50
+ const names = (Array.isArray(jsPlugins) ? jsPlugins : []).flatMap((entry) => {
51
+ if (typeof entry === "string") {
52
+ return entry.includes("oxlint-plugin-biological-architecture") ? [PLUGIN_NAME] : [];
53
+ }
54
+ const { name, specifier } = recordOf(entry);
55
+ return typeof name === "string" && typeof specifier === "string" && specifier.includes("oxlint-plugin-biological-architecture") ? [name] : [];
56
+ });
57
+ return names.length === 0 ? [PLUGIN_NAME] : names;
58
+ };
59
+ var runOf = ({ argv, cwd }) => {
60
+ let config;
61
+ const paths = [];
62
+ for (let at = 0;at < argv.length; at += 1) {
63
+ const word = argv[at] ?? "";
64
+ const name = word.split("=")[0] ?? word;
65
+ if (UNKNOWABLE.includes(name))
66
+ return null;
67
+ if (name === "-c" || name === "--config") {
68
+ config = word.includes("=") ? word.slice(word.indexOf("=") + 1) : argv[at += 1];
69
+ } else if (FLAGS_WITH_VALUE.has(word))
70
+ at += 1;
71
+ else if (!word.startsWith("-"))
72
+ paths.push(word);
73
+ }
74
+ const configPath = config === undefined ? join(cwd, ".oxlintrc.json") : resolve(cwd, config);
75
+ if (!/\.jsonc?$/.test(configPath) || !existsSync(configPath))
76
+ return null;
77
+ const declared = parseLooseJson(readFileSync(configPath, "utf8"));
78
+ const record = recordOf(declared);
79
+ const patterns = stringsOf(record["ignorePatterns"]);
80
+ if (patterns.some((one) => one.startsWith("!")))
81
+ return null;
82
+ const listed = spawnSync("git", ["ls-files", "-co", "--exclude-standard", "-z", "--", ...paths.length === 0 ? ["."] : paths], { cwd, encoding: "utf8", maxBuffer: 256 * 1024 * 1024 });
83
+ if (listed.status !== 0)
84
+ return null;
85
+ const root = dirname(configPath);
86
+ return {
87
+ candidates: listed.stdout.split("\x00").filter((path) => LINTED.test(path)).map((path) => resolve(cwd, path)).filter((path) => existsSync(path) && !ignoredBy(relative(root, path), patterns)).toSorted(),
88
+ overrides: (Array.isArray(record["overrides"]) ? record["overrides"] : []).map((one) => ({
89
+ files: stringsOf(recordOf(one)["files"]),
90
+ rules: recordOf(recordOf(one)["rules"])
91
+ })),
92
+ prefixes: prefixesOf(record["jsPlugins"]),
93
+ root,
94
+ rules: recordOf(record["rules"])
95
+ };
96
+ };
97
+ var isOff = (level) => {
98
+ const first = Array.isArray(level) ? level[0] : level;
99
+ return first === "off" || first === 0;
100
+ };
101
+ var designatedIn = (run, rule, accepts) => {
102
+ const keys = run.prefixes.map((prefix) => `${prefix}/${rule}`);
103
+ const base = keys.map((key) => run.rules[key]).find((level) => level !== undefined);
104
+ if (isOff(base))
105
+ return null;
106
+ const mentioning = run.overrides.filter((one) => keys.some((key) => (key in one.rules)));
107
+ return run.candidates.find((path) => {
108
+ const here = relative(run.root, path);
109
+ if (mentioning.some((one) => one.files.some((glob) => looselyMatches(here, glob)))) {
110
+ return false;
111
+ }
112
+ if (switchedOff(path))
113
+ return false;
114
+ try {
115
+ return accepts(path, readFileSync(path, "utf8"));
116
+ } catch {
117
+ return false;
118
+ }
119
+ }) ?? null;
120
+ };
121
+ var HOST = /[\\/]oxlint[\\/]bin[\\/]oxlint(?:\.\w+)?$/;
122
+ var run;
123
+ var designated = new Map;
124
+ var designatedFile = (rule, salt = "", accepts = () => true) => {
125
+ if (run === undefined) {
126
+ const [, script = "", ...argv] = process.argv;
127
+ try {
128
+ run = HOST.test(script) ? runOf({ argv, cwd: process.cwd() }) : null;
129
+ } catch {
130
+ run = null;
131
+ }
132
+ }
133
+ if (run === null)
134
+ return null;
135
+ const key = `${rule}\x00${salt}`;
136
+ if (!designated.has(key))
137
+ designated.set(key, designatedIn(run, rule, accepts));
138
+ return designated.get(key) ?? null;
139
+ };
140
+ export {
141
+ designatedFile,
142
+ designatedIn,
143
+ runOf
144
+ };
@@ -1,13 +1,6 @@
1
- /**
2
- * Which features may be reached ACROSS feature boundaries is one repo's layout, so it is an
3
- * option — `organelle-dependency` and `cell-no-cross-feature-organelles` draw the same boundary
4
- * from two sides, on the same option, with the same default: the one name every feature-sliced
5
- * repo agrees on.
6
- */
7
1
  export type SharedFeaturesOptions = {
8
2
  sharedFeatures?: string[];
9
3
  };
10
- /** The JSON schema for `{ sharedFeatures?: string[] }`, read by both rules that take it. */
11
4
  export declare const SHARED_FEATURES_SCHEMA: {
12
5
  additionalProperties: boolean;
13
6
  properties: {
@@ -20,5 +13,4 @@ export declare const SHARED_FEATURES_SCHEMA: {
20
13
  };
21
14
  type: 'object';
22
15
  }[];
23
- /** Whether `importedFeature` is a DIFFERENT feature the options have not named as shared. */
24
16
  export declare const crossesFeatureBoundary: (importedFeature: string | undefined, ownFeature: string | undefined, options: SharedFeaturesOptions) => boolean;
@@ -1,14 +1,7 @@
1
1
  // src/rules/lib/shared-features.ts
2
+ import { optionsBag, STRINGS } from "./schema.js";
2
3
  var DEFAULT_SHARED_FEATURES = ["shared"];
3
- var SHARED_FEATURES_SCHEMA = [
4
- {
5
- additionalProperties: false,
6
- properties: {
7
- sharedFeatures: { items: { type: "string" }, type: "array" }
8
- },
9
- type: "object"
10
- }
11
- ];
4
+ var SHARED_FEATURES_SCHEMA = optionsBag({ sharedFeatures: STRINGS });
12
5
  var crossesFeatureBoundary = (importedFeature, ownFeature, options) => importedFeature !== undefined && importedFeature !== ownFeature && !new Set(options.sharedFeatures ?? DEFAULT_SHARED_FEATURES).has(importedFeature);
13
6
  export {
14
7
  SHARED_FEATURES_SCHEMA,
@@ -0,0 +1,8 @@
1
+ export declare const FORM_SOURCES: Set<string>;
2
+ export declare const FORM_CONTEXT_IMPORT_PATTERN: RegExp;
3
+ export declare const isProvidersSource: (source: string) => boolean;
4
+ export interface StateSources {
5
+ forms: Set<string>;
6
+ stores: Set<string>;
7
+ }
8
+ export declare const stateSourcesOf: (file: string, seen?: Set<string>) => StateSources;
@@ -0,0 +1,55 @@
1
+ // src/rules/lib/state-sources.ts
2
+ import { STORE_HOOK_NAME } from "./hooks.js";
3
+ import { isRelative } from "./imports.js";
4
+ import { fileFor, readSource } from "./module-text.js";
5
+ import { inTier } from "./tiers.js";
6
+ var FORM_SOURCES = new Set(["react-hook-form"]);
7
+ var FORM_CONTEXT_IMPORT_PATTERN = /FormContext$/;
8
+ var isProvidersSource = (source) => /(?:^|\/)providers(?:\/|$)/.test(source);
9
+ var IMPORT = /\bimport\s+(type\s+)?([^'";]*?)\s*from\s*['"]([^'"]+)['"]/g;
10
+ var STORE_CALL = /(?<!function\s)\b(use\w+Store)\s*\(/g;
11
+ var DEPTH = 4;
12
+ var localsOf = (clause) => {
13
+ const named = /\{([^}]*)\}/.exec(clause)?.[1] ?? "";
14
+ const bare = clause.replace(/\{[^}]*\}/, "").replaceAll(",", " ").trim();
15
+ const fromBare = /^[A-Za-z_$][\w$]*$/.test(bare) ? [bare] : [];
16
+ const fromNamed = named.split(",").map((one) => one.trim()).filter((one) => one !== "" && !one.startsWith("type ")).map((one) => one.split(/\s+as\s+/).at(-1) ?? "");
17
+ return [...fromBare, ...fromNamed].filter((one) => one !== "");
18
+ };
19
+ var importsOf = (text) => [...text.matchAll(IMPORT)].flatMap((found) => found[1] === undefined ? [{ locals: localsOf(found[2] ?? ""), source: found[3] ?? "" }] : []);
20
+ var isCalled = (text, name) => new RegExp(String.raw`(?<![\w$.])${name.replaceAll("$", String.raw`\$`)}\s*\(`).test(text);
21
+ var gather = (file, seen, depth, into) => {
22
+ if (seen.has(file) || depth > DEPTH)
23
+ return;
24
+ seen.add(file);
25
+ const text = readSource(file);
26
+ if (text === undefined)
27
+ return;
28
+ for (const found of text.matchAll(STORE_CALL)) {
29
+ const name = found[1] ?? "";
30
+ if (STORE_HOOK_NAME.test(name))
31
+ into.stores.add(name);
32
+ }
33
+ for (const one of importsOf(text)) {
34
+ if (FORM_SOURCES.has(one.source))
35
+ into.forms.add(one.source);
36
+ else if (isProvidersSource(one.source) && one.locals.some((local) => FORM_CONTEXT_IMPORT_PATTERN.test(local))) {
37
+ into.forms.add(one.source);
38
+ } else if (isRelative(one.source) && one.locals.some((local) => isCalled(text, local))) {
39
+ const next = fileFor(file, one.source);
40
+ if (next !== undefined && inTier(next, "organelles"))
41
+ gather(next, seen, depth + 1, into);
42
+ }
43
+ }
44
+ };
45
+ var stateSourcesOf = (file, seen = new Set) => {
46
+ const into = { forms: new Set, stores: new Set };
47
+ gather(file, seen, 0, into);
48
+ return into;
49
+ };
50
+ export {
51
+ FORM_CONTEXT_IMPORT_PATTERN,
52
+ FORM_SOURCES,
53
+ isProvidersSource,
54
+ stateSourcesOf
55
+ };
@@ -1,11 +1,10 @@
1
1
  import { type AnyNode } from './nodes.js';
2
+ export declare const DEFAULT_STEP_FACTORIES: string[];
3
+ export declare const DEFAULT_WORKFLOW_FACTORIES: string[];
4
+ export declare const workflowFactoriesOf: (rule: string, configured: string[] | undefined) => Set<string>;
2
5
  export declare const stepFactoriesOf: (rule: string, configured: string[] | undefined) => Set<string>;
3
6
  export declare const isStepCall: (node: AnyNode, factories: ReadonlySet<string>) => boolean;
4
7
  export declare const argumentsOf: (node: AnyNode) => AnyNode[];
5
- /**
6
- * The step's config, told apart three ways: a bare string name (a call that can carry no config at
7
- * all), an object this rule can read, and an object it cannot — a spread, or a variable.
8
- */
9
8
  export type StepConfig = {
10
9
  kind: 'bare';
11
10
  } | {
@@ -15,5 +14,4 @@ export type StepConfig = {
15
14
  kind: 'unreadable';
16
15
  };
17
16
  export declare const configOf: (node: AnyNode) => StepConfig;
18
- /** Whatever names the step in a message: its literal name, its config's `name`, or the factory. */
19
17
  export declare const stepNameOf: (node: AnyNode, config: StepConfig) => string;
@@ -3,6 +3,8 @@ import { calleeNameOf } from "./nodes.js";
3
3
  import { propertiesOf, stringOf, valueOf } from "./objects.js";
4
4
  import { requireOption } from "./options.js";
5
5
  var DEFAULT_STEP_FACTORIES = ["createStep"];
6
+ var DEFAULT_WORKFLOW_FACTORIES = ["createWorkflow"];
7
+ var workflowFactoriesOf = (rule, configured) => new Set(requireOption(rule, configured ?? DEFAULT_WORKFLOW_FACTORIES, "workflowFactories"));
6
8
  var stepFactoriesOf = (rule, configured) => new Set(requireOption(rule, configured ?? DEFAULT_STEP_FACTORIES, "stepFactories"));
7
9
  var isStepCall = (node, factories) => {
8
10
  const callee = calleeNameOf(node);
@@ -29,9 +31,12 @@ var stepNameOf = (node, config) => {
29
31
  return calleeNameOf(node) ?? "a step";
30
32
  };
31
33
  export {
34
+ DEFAULT_STEP_FACTORIES,
35
+ DEFAULT_WORKFLOW_FACTORIES,
32
36
  argumentsOf,
33
37
  configOf,
34
38
  isStepCall,
35
39
  stepFactoriesOf,
36
- stepNameOf
40
+ stepNameOf,
41
+ workflowFactoriesOf
37
42
  };
@@ -1,12 +1,3 @@
1
- /**
2
- * Reading the two places a component says what something looks like: `className` and `style`.
3
- *
4
- * Both conformance rules need the same three things — the string behind a `className`, the
5
- * `property: 'literal'` pairs behind a `style` object, and the arbitrary values a Tailwind class
6
- * carries in brackets — and neither of them needs the other's vocabulary. Written once here so the
7
- * two cannot disagree about what a className IS, which is the divergence the union of two plugins
8
- * was cut to end.
9
- */
10
1
  interface Node {
11
2
  type: string;
12
3
  }
@@ -20,46 +11,22 @@ interface Attribute extends Node {
20
11
  export interface OpeningElement extends Node {
21
12
  attributes?: Attribute[];
22
13
  }
23
- /** One `key: 'value'` pair of an inline style object, where both sides were written out. */
24
14
  interface StyleEntry {
25
15
  property: string;
26
16
  value: string;
27
17
  }
28
- /**
29
- * The literal text of a `className`, or nothing.
30
- *
31
- * A className assembled at runtime — `cn(base, isActive && 'x')`, a template with a hole — has no
32
- * one text, and guessing at one is how a rule reports a line the reader cannot find the value on.
33
- * Template quasis are joined because their static halves ARE written out, and a hardcoded colour
34
- * hides in them exactly as well as in a plain string.
35
- */
36
18
  export declare const classNameOf: (node: OpeningElement) => string | undefined;
37
- /**
38
- * The written-out entries of a `style={{ … }}` object.
39
- *
40
- * Only the pairs where BOTH halves are written out: a computed key or a value read from a variable
41
- * is a value this file cannot see, and reporting on it would be a guess. A spread is skipped for
42
- * the same reason.
43
- */
19
+ export declare const staticClassNameOf: (node: OpeningElement) => string | undefined;
20
+ export declare const keyOf: (property: {
21
+ computed?: boolean;
22
+ key?: Node & {
23
+ name?: string;
24
+ value?: unknown;
25
+ };
26
+ }) => string | undefined;
44
27
  export declare const styleEntriesOf: (node: OpeningElement) => StyleEntry[];
45
- /**
46
- * Tailwind arbitrary values, as `[utility, inner]` pairs: `bg-[#fff]` → `['bg', '#fff']`.
47
- *
48
- * The utility half is returned because what is inside the brackets only means something with it:
49
- * `[tan]` is a colour after `text-` and a font after `font-`, and a rule that looked at the inner
50
- * text alone would have to guess which.
51
- */
28
+ export declare const styleObjectOf: (node: OpeningElement) => Node | null;
52
29
  export declare const arbitraryValuesOf: (className: string) => [utility: string, inner: string][];
53
- /**
54
- * The custom properties the element sets on ITSELF, in its own `style`.
55
- *
56
- * A component that writes `style={{ '--pb-text': fromAProp }}` and reads `text-[var(--pb-text)]`
57
- * back is using a local alias: the variable is declared and consumed on one element and never
58
- * leaves it. Treating that as a component reaching around the theme reports an accepted convention,
59
- * which is how a rule earns the reputation that gets it switched off — measured on a consumer's
60
- * kit, where the pattern appears fifty times.
61
- */
62
30
  export declare const localVariablesOf: (node: OpeningElement) => Set<string>;
63
- /** The custom property a `var(--x)` names, or nothing. */
64
31
  export declare const variableOf: (value: string) => string | undefined;
65
32
  export {};