@codefast/cli 0.3.15 → 0.3.16-canary.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 (209) hide show
  1. package/README.md +1 -1
  2. package/dist/arrange/analyze.mjs +24 -0
  3. package/dist/{domains/arrange/presentation/presenters/arrange-cli.schema.mjs → arrange/cli-schema.mjs} +10 -1
  4. package/dist/arrange/command.mjs +106 -0
  5. package/dist/{domains/arrange/domain/arrange-analyze.domain-service.mjs → arrange/domain/analyze-service.mjs} +17 -10
  6. package/dist/{domains/arrange/domain/ast/ast-node.model.mjs → arrange/domain/ast/ast-node.mjs} +57 -3
  7. package/dist/{domains/arrange/domain/ast/collectors-cn.collector.mjs → arrange/domain/ast/collectors-cn.mjs} +21 -9
  8. package/dist/{domains/arrange/domain/ast/collectors-jsx.collector.mjs → arrange/domain/ast/collectors-jsx.mjs} +5 -2
  9. package/dist/{domains/arrange/domain/ast/collectors-tv.collector.mjs → arrange/domain/ast/collectors-tv.mjs} +21 -6
  10. package/dist/{domains/arrange/domain/ast/ast-helpers.helper.mjs → arrange/domain/ast/helpers.mjs} +26 -9
  11. package/dist/{domains/arrange/domain/ast/targets.model.mjs → arrange/domain/ast/targets.mjs} +19 -10
  12. package/dist/{domains/arrange/domain/constants.domain.mjs → arrange/domain/constants.mjs} +63 -18
  13. package/dist/{domains/arrange/domain/arrange-grouping.domain-service.mjs → arrange/domain/grouping-service.mjs} +31 -10
  14. package/dist/{domains/arrange/domain/grouping.domain.mjs → arrange/domain/grouping.mjs} +39 -18
  15. package/dist/{domains/arrange/domain/imports.domain.mjs → arrange/domain/imports.mjs} +7 -3
  16. package/dist/{domains/arrange/domain/source-text-formatters.formatter.mjs → arrange/domain/source-text-formatters.mjs} +22 -5
  17. package/dist/{domains/arrange/domain/tailwind-token.value-object.mjs → arrange/domain/tailwind-token.mjs} +12 -3
  18. package/dist/{domains/arrange/domain/tailwind-token-classifier.domain-service.mjs → arrange/domain/token-classifier.mjs} +40 -15
  19. package/dist/arrange/output.mjs +79 -0
  20. package/dist/arrange/process-file.mjs +42 -0
  21. package/dist/arrange/resolve-target.mjs +24 -0
  22. package/dist/arrange/scan-target.mjs +20 -0
  23. package/dist/arrange/source-parse.mjs +18 -0
  24. package/dist/arrange/suggest.mjs +15 -0
  25. package/dist/arrange/sync.mjs +51 -0
  26. package/dist/arrange/typescript-ast-translator.mjs +354 -0
  27. package/dist/arrange/workspace.mjs +31 -0
  28. package/dist/bin.mjs +1 -1
  29. package/dist/{program.mjs → cli.mjs} +14 -20
  30. package/dist/core/cli/format-error.mjs +15 -0
  31. package/dist/core/cli/global-options.mjs +10 -0
  32. package/dist/core/cli/positional.mjs +11 -0
  33. package/dist/core/cli/result-handle.mjs +51 -0
  34. package/dist/core/config/loader.mjs +79 -0
  35. package/dist/{domains/config/domain/codefast-config.schema.mjs → core/config/schema.mjs} +16 -2
  36. package/dist/core/config/warnings.mjs +12 -0
  37. package/dist/core/config.mjs +19 -0
  38. package/dist/{shell/domain/caught-unknown-message.value-object.mjs → core/errors.mjs} +20 -4
  39. package/dist/core/exit-codes.mjs +17 -0
  40. package/dist/core/filesystem/node.mjs +32 -0
  41. package/dist/core/logger.mjs +15 -0
  42. package/dist/{shell/domain/result.model.mjs → core/result.mjs} +7 -3
  43. package/dist/core/schema-parse.mjs +18 -0
  44. package/dist/{shell/domain/source-text-edit.support.mjs → core/source-text-edit.mjs} +14 -13
  45. package/dist/core/verbose-diagnostics.mjs +11 -0
  46. package/dist/core/workspace/resolver.mjs +177 -0
  47. package/dist/core/workspace/typescript-walk.mjs +36 -0
  48. package/dist/{domains/mirror/application/mirror-sync-cli-result.mjs → mirror/cli-result.mjs} +8 -2
  49. package/dist/{domains/mirror/presentation/presenters/mirror-cli.schema.mjs → mirror/cli-schema.mjs} +4 -1
  50. package/dist/mirror/command.mjs +57 -0
  51. package/dist/mirror/dist-filesystem-impl.mjs +42 -0
  52. package/dist/{domains/mirror/domain/constants.domain.mjs → mirror/domain/constants.mjs} +16 -1
  53. package/dist/{domains/mirror/infrastructure/guards/dirent-list.guard.mjs → mirror/domain/dirent-guard.mjs} +5 -2
  54. package/dist/{domains/mirror/domain/errors.domain.mjs → mirror/domain/errors.mjs} +7 -1
  55. package/dist/{domains/mirror/domain/generate-mirror-exports.domain-service.mjs → mirror/domain/exports.mjs} +17 -10
  56. package/dist/{domains/mirror/domain/package-display-name.policy.mjs → mirror/domain/package-display-name.mjs} +5 -2
  57. package/dist/{domains/mirror/domain/path-normalizer.value-object.mjs → mirror/domain/path-normalizer.mjs} +4 -1
  58. package/dist/mirror/output.mjs +45 -0
  59. package/dist/mirror/package-path.mjs +32 -0
  60. package/dist/mirror/prepare.mjs +33 -0
  61. package/dist/mirror/sync-reporter.mjs +104 -0
  62. package/dist/mirror/sync-workspace-package.mjs +103 -0
  63. package/dist/mirror/sync.mjs +87 -0
  64. package/dist/{domains/mirror/infrastructure/package-json-exports.repository.mjs → mirror/write-exports.mjs} +11 -7
  65. package/dist/tag/cli-result.mjs +11 -0
  66. package/dist/{domains/tag/presentation/presenters/tag-cli.schema.mjs → tag/cli-schema.mjs} +4 -1
  67. package/dist/tag/command.mjs +56 -0
  68. package/dist/tag/output.mjs +87 -0
  69. package/dist/tag/prepare.mjs +29 -0
  70. package/dist/tag/resolve-target-path.mjs +12 -0
  71. package/dist/tag/since-writer.mjs +130 -0
  72. package/dist/tag/sync.mjs +141 -0
  73. package/dist/tag/target-candidates.mjs +68 -0
  74. package/dist/tag/target-runner.mjs +26 -0
  75. package/dist/tag/version-resolver.mjs +25 -0
  76. package/package.json +4 -7
  77. package/dist/bootstrap/cli-application.module.mjs +0 -18
  78. package/dist/bootstrap/composition-root.mjs +0 -14
  79. package/dist/bootstrap/register-cli-command-trees.mjs +0 -7
  80. package/dist/domains/arrange/application/ports/outbound/arrange-target-scanner.port.mjs +0 -1
  81. package/dist/domains/arrange/application/ports/outbound/domain-source-parser.port.mjs +0 -1
  82. package/dist/domains/arrange/application/ports/outbound/file-walker.port.mjs +0 -1
  83. package/dist/domains/arrange/application/ports/presenting/present-analyze-report.presenter.mjs +0 -1
  84. package/dist/domains/arrange/application/ports/presenting/present-arrange-sync-result.presenter.mjs +0 -1
  85. package/dist/domains/arrange/application/ports/presenting/present-group-file-preview.presenter.mjs +0 -1
  86. package/dist/domains/arrange/application/requests/analyze-directory.request.mjs +0 -1
  87. package/dist/domains/arrange/application/requests/arrange-sync.request.mjs +0 -1
  88. package/dist/domains/arrange/application/requests/suggest-groups.request.mjs +0 -1
  89. package/dist/domains/arrange/application/use-cases/analyze-directory.use-case.mjs +0 -255
  90. package/dist/domains/arrange/application/use-cases/prepare-arrange-workspace.use-case.mjs +0 -264
  91. package/dist/domains/arrange/application/use-cases/run-arrange-sync.use-case.mjs +0 -274
  92. package/dist/domains/arrange/application/use-cases/suggest-cn-groups.use-case.mjs +0 -237
  93. package/dist/domains/arrange/arrange.module.mjs +0 -38
  94. package/dist/domains/arrange/composition/tokens.mjs +0 -17
  95. package/dist/domains/arrange/contracts/models.mjs +0 -1
  96. package/dist/domains/arrange/domain/tailwind-grouping.domain-service.mjs +0 -231
  97. package/dist/domains/arrange/domain/types.domain.mjs +0 -1
  98. package/dist/domains/arrange/infrastructure/adapters/arrange-file-processor.adapter.mjs +0 -267
  99. package/dist/domains/arrange/infrastructure/adapters/arrange-target-path-resolver.adapter.mjs +0 -247
  100. package/dist/domains/arrange/infrastructure/adapters/arrange-target-scanner.adapter.mjs +0 -243
  101. package/dist/domains/arrange/infrastructure/adapters/domain-source-parser.adapter.mjs +0 -241
  102. package/dist/domains/arrange/infrastructure/adapters/file-walker.adapter.mjs +0 -240
  103. package/dist/domains/arrange/infrastructure/typescript-ast-translator.mjs +0 -580
  104. package/dist/domains/arrange/presentation/cli/arrange.command.mjs +0 -465
  105. package/dist/domains/arrange/presentation/presenters/arrange-analyze.presenter.mjs +0 -252
  106. package/dist/domains/arrange/presentation/presenters/arrange-sync.presenter.mjs +0 -236
  107. package/dist/domains/arrange/presentation/presenters/group-file-preview.presenter.mjs +0 -271
  108. package/dist/domains/config/application/ports/outbound/codefast-config-schema.port.mjs +0 -1
  109. package/dist/domains/config/application/ports/outbound/config-loader.port.mjs +0 -1
  110. package/dist/domains/config/application/ports/outbound/config-warning-reporter.port.mjs +0 -1
  111. package/dist/domains/config/composition/tokens.mjs +0 -7
  112. package/dist/domains/config/domain/schema.domain.mjs +0 -1
  113. package/dist/domains/config/infrastructure/adapters/config-loader.adapter.mjs +0 -305
  114. package/dist/domains/config/infrastructure/adapters/config-warning-reporter.adapter.mjs +0 -234
  115. package/dist/domains/config/infrastructure/adapters/zod-codefast-config-schema.adapter.mjs +0 -236
  116. package/dist/domains/mirror/application/ports/inbound/prepare-mirror-sync.port.mjs +0 -1
  117. package/dist/domains/mirror/application/ports/inbound/run-mirror-sync.port.mjs +0 -1
  118. package/dist/domains/mirror/application/ports/outbound/file-system-service.port.mjs +0 -1
  119. package/dist/domains/mirror/application/ports/outbound/mirror-package-path.port.mjs +0 -1
  120. package/dist/domains/mirror/application/ports/outbound/mirror-sync-reporter.port.mjs +0 -1
  121. package/dist/domains/mirror/application/ports/outbound/package-repository.port.mjs +0 -1
  122. package/dist/domains/mirror/application/ports/outbound/sync-workspace-package.port.mjs +0 -1
  123. package/dist/domains/mirror/application/ports/presenting/present-mirror-sync-progress.presenter.mjs +0 -1
  124. package/dist/domains/mirror/application/requests/mirror-sync-execution-input.mjs +0 -1
  125. package/dist/domains/mirror/application/requests/mirror-sync.request.mjs +0 -1
  126. package/dist/domains/mirror/application/use-cases/prepare-mirror-sync.use-case.mjs +0 -263
  127. package/dist/domains/mirror/application/use-cases/run-mirror-sync.use-case.mjs +0 -317
  128. package/dist/domains/mirror/composition/tokens.mjs +0 -12
  129. package/dist/domains/mirror/contracts/models.mjs +0 -1
  130. package/dist/domains/mirror/domain/types.domain.mjs +0 -1
  131. package/dist/domains/mirror/infrastructure/adapters/file-system-service.adapter.mjs +0 -261
  132. package/dist/domains/mirror/infrastructure/adapters/mirror-package-path-resolver.adapter.mjs +0 -252
  133. package/dist/domains/mirror/infrastructure/adapters/mirror-sync-reporter.adapter.mjs +0 -322
  134. package/dist/domains/mirror/infrastructure/adapters/package-repository.adapter.mjs +0 -241
  135. package/dist/domains/mirror/infrastructure/adapters/sync-workspace-package.adapter.mjs +0 -328
  136. package/dist/domains/mirror/mirror.module.mjs +0 -26
  137. package/dist/domains/mirror/presentation/cli/mirror.command.mjs +0 -328
  138. package/dist/domains/mirror/presentation/presenters/present-mirror-sync-progress.presenter.mjs +0 -265
  139. package/dist/domains/tag/application/ports/inbound/prepare-tag-sync.port.mjs +0 -1
  140. package/dist/domains/tag/application/ports/inbound/run-tag-sync.port.mjs +0 -1
  141. package/dist/domains/tag/application/ports/outbound/tag-eligible-workspace-paths.port.mjs +0 -1
  142. package/dist/domains/tag/application/ports/outbound/tag-since-writer.port.mjs +0 -1
  143. package/dist/domains/tag/application/ports/outbound/tag-target-path-resolver.port.mjs +0 -1
  144. package/dist/domains/tag/application/ports/outbound/tag-target-runner.port.mjs +0 -1
  145. package/dist/domains/tag/application/ports/outbound/tag-version-resolver.port.mjs +0 -1
  146. package/dist/domains/tag/application/ports/presenting/present-tag-sync-progress.presenter.mjs +0 -1
  147. package/dist/domains/tag/application/ports/presenting/present-tag-sync-result.presenter.mjs +0 -1
  148. package/dist/domains/tag/application/requests/tag-sync-execution-input.mjs +0 -1
  149. package/dist/domains/tag/application/requests/tag-sync.request.mjs +0 -1
  150. package/dist/domains/tag/application/tag-sync-cli-result.mjs +0 -11
  151. package/dist/domains/tag/application/use-cases/prepare-tag-sync.use-case.mjs +0 -260
  152. package/dist/domains/tag/application/use-cases/run-tag-sync.use-case.mjs +0 -375
  153. package/dist/domains/tag/composition/tokens.mjs +0 -13
  154. package/dist/domains/tag/contracts/models.mjs +0 -1
  155. package/dist/domains/tag/domain/types.domain.mjs +0 -1
  156. package/dist/domains/tag/infrastructure/adapters/tag-since-writer.adapter.mjs +0 -348
  157. package/dist/domains/tag/infrastructure/adapters/tag-target-path-resolver.adapter.mjs +0 -235
  158. package/dist/domains/tag/infrastructure/adapters/tag-target-resolver.adapter.mjs +0 -302
  159. package/dist/domains/tag/infrastructure/adapters/tag-target-runner.adapter.mjs +0 -259
  160. package/dist/domains/tag/infrastructure/adapters/tag-version-resolver.adapter.mjs +0 -249
  161. package/dist/domains/tag/presentation/cli/tag.command.mjs +0 -325
  162. package/dist/domains/tag/presentation/presenters/present-tag-sync-progress.presenter.mjs +0 -248
  163. package/dist/domains/tag/presentation/presenters/present-tag-sync-result.presenter.mjs +0 -290
  164. package/dist/domains/tag/tag.module.mjs +0 -28
  165. package/dist/shell/application/cli-runtime.tokens.mjs +0 -18
  166. package/dist/shell/application/coordination/cli-executor.coordination.mjs +0 -1
  167. package/dist/shell/application/coordination/cli-schema-parsing.coordination.mjs +0 -1
  168. package/dist/shell/application/global-cli-options.model.mjs +0 -5
  169. package/dist/shell/application/ports/inbound/load-codefast-config.port.mjs +0 -1
  170. package/dist/shell/application/ports/outbound/cli-fs.port.mjs +0 -1
  171. package/dist/shell/application/ports/outbound/cli-logger.port.mjs +0 -1
  172. package/dist/shell/application/ports/outbound/cli-path.port.mjs +0 -1
  173. package/dist/shell/application/ports/outbound/cli-runtime.port.mjs +0 -1
  174. package/dist/shell/application/ports/outbound/cli-telemetry.port.mjs +0 -1
  175. package/dist/shell/application/ports/outbound/cli-verbose-diagnostics.port.mjs +0 -1
  176. package/dist/shell/application/ports/outbound/format-app-error.port.mjs +0 -1
  177. package/dist/shell/application/ports/outbound/global-cli-options-parse.port.mjs +0 -1
  178. package/dist/shell/application/ports/outbound/repo-root-resolver.port.mjs +0 -1
  179. package/dist/shell/application/ports/outbound/typescript-source-file-walker.port.mjs +0 -1
  180. package/dist/shell/application/ports/outbound/workspace-package-layout.port.mjs +0 -1
  181. package/dist/shell/application/ports/primary/command.port.mjs +0 -1
  182. package/dist/shell/application/services/cli-executor.service.mjs +0 -267
  183. package/dist/shell/application/services/schema-validator.service.mjs +0 -236
  184. package/dist/shell/application/use-cases/load-codefast-config.use-case.mjs +0 -243
  185. package/dist/shell/composition/tokens.mjs +0 -5
  186. package/dist/shell/contracts/cli-command-slots.mjs +0 -11
  187. package/dist/shell/domain/cli-exit-codes.domain.mjs +0 -11
  188. package/dist/shell/domain/cli-positional-arg.value-object.mjs +0 -6
  189. package/dist/shell/domain/errors.domain.mjs +0 -17
  190. package/dist/shell/infrastructure/adapters/cli-verbose-diagnostics.adapter.mjs +0 -228
  191. package/dist/shell/infrastructure/adapters/format-app-error.adapter.mjs +0 -236
  192. package/dist/shell/infrastructure/adapters/global-cli-options-parser.adapter.mjs +0 -233
  193. package/dist/shell/infrastructure/adapters/repo-root-resolver.adapter.mjs +0 -244
  194. package/dist/shell/infrastructure/adapters/typescript-source-file-walker.adapter.mjs +0 -259
  195. package/dist/shell/infrastructure/commander/commander-cli-host.adapter.mjs +0 -69
  196. package/dist/shell/infrastructure/node/node-cli-fs.adapter.mjs +0 -249
  197. package/dist/shell/infrastructure/node/node-cli-logger.adapter.mjs +0 -231
  198. package/dist/shell/infrastructure/node/node-cli-path.adapter.mjs +0 -246
  199. package/dist/shell/infrastructure/node/node-cli-runtime.adapter.mjs +0 -234
  200. package/dist/shell/infrastructure/telemetry/cli-telemetry.adapter.mjs +0 -280
  201. package/dist/shell/infrastructure/workspace/node-pnpm-workspace-package-layout.adapter.mjs +0 -385
  202. package/dist/shell/shell.module.mjs +0 -43
  203. package/dist/shell/wiring/optional-cli-port-telemetry-activation.mjs +0 -13
  204. /package/dist/{domains/arrange/application/ports/inbound/analyze-directory.port.mjs → arrange/domain/types.mjs} +0 -0
  205. /package/dist/{domains/arrange/application/ports/inbound/prepare-arrange-workspace.port.mjs → core/filesystem/port.mjs} +0 -0
  206. /package/dist/{domains/arrange/application/ports/inbound/run-arrange-sync.port.mjs → mirror/domain/dist-filesystem.mjs} +0 -0
  207. /package/dist/{domains/arrange/application/ports/inbound/suggest-cn-groups.port.mjs → mirror/domain/types.mjs} +0 -0
  208. /package/dist/{domains/arrange/application/ports/outbound/arrange-file-processor.port.mjs → mirror/sync-types.mjs} +0 -0
  209. /package/dist/{domains/arrange/application/ports/outbound/arrange-target-path-resolver.port.mjs → tag/domain/types.mjs} +0 -0
@@ -1,51 +1,87 @@
1
- //#region src/domains/arrange/domain/constants.domain.ts
1
+ //#region src/arrange/domain/constants.ts
2
2
  /**
3
3
  * Analyze report: long literal threshold (token count).
4
- */ const LONG_STRING_TOKEN_THRESHOLD = 18;
4
+ *
5
+ * @since 0.3.16-canary.0
6
+ */
7
+ const LONG_STRING_TOKEN_THRESHOLD = 18;
5
8
  /**
6
9
  * Minimum token count for a string to be considered a candidate for grouping
7
10
  * in the apply/preview pipeline. Intentionally much lower than
8
11
  * {@link LONG_STRING_TOKEN_THRESHOLD} (analyze only).
9
- */ const APPLY_MIN_TOKENS = 2;
12
+ *
13
+ * @since 0.3.16-canary.0
14
+ */
15
+ const APPLY_MIN_TOKENS = 2;
10
16
  /**
11
17
  * Minimum tokens a group must have to stand alone before singleton-merging.
12
18
  * Set to 2 so single-token groups are not collapsed into unrelated buckets by the merger.
13
- */ const MIN_GROUP_TOKENS = 2;
19
+ *
20
+ * @since 0.3.16-canary.0
21
+ */
22
+ const MIN_GROUP_TOKENS = 2;
14
23
  /**
15
24
  * Dynamic max groups clamp: base bound.
16
- */ const MAX_GROUPS_BASE = 4;
25
+ *
26
+ * @since 0.3.16-canary.0
27
+ */
28
+ const MAX_GROUPS_BASE = 4;
17
29
  /**
18
30
  * Dynamic max groups clamp: upper cap.
19
- */ const MAX_GROUPS_CAP = 24;
31
+ *
32
+ * @since 0.3.16-canary.0
33
+ */
34
+ const MAX_GROUPS_CAP = 24;
20
35
  /**
21
36
  * Extra slots so a few state / aria groups do not force `capGroups` to merge
22
37
  * unrelated buckets (e.g. `bg-border` + `outline-hidden`).
23
- */ const MAX_GROUPS_HEADROOM = 2;
38
+ *
39
+ * @since 0.3.16-canary.0
40
+ */
41
+ const MAX_GROUPS_HEADROOM = 2;
24
42
  /**
25
43
  * Maximum findings printed per category in the analyze report.
26
- */ const MAX_REPORT_LINES = 40;
44
+ *
45
+ * @since 0.3.16-canary.0
46
+ */
47
+ const MAX_REPORT_LINES = 40;
27
48
  /**
28
49
  * Maximum recursion depth when traversing `tv()` object literals.
29
- */ const MAX_OBJECT_DEPTH = 12;
50
+ *
51
+ * @since 0.3.16-canary.0
52
+ */
53
+ const MAX_OBJECT_DEPTH = 12;
30
54
  /**
31
55
  * Maximum depth when peeling conditional / parens / arrays inside `cn(...)` args.
32
- */ const MAX_CLASS_EXPR_DEPTH = 12;
56
+ *
57
+ * @since 0.3.16-canary.0
58
+ */
59
+ const MAX_CLASS_EXPR_DEPTH = 12;
33
60
  /**
34
61
  * Maximum variant-stripping passes in {@link stripVariants}.
35
62
  * Real-world Tailwind stacks rarely exceed 4–5 segments (e.g. `@md/sidebar:group-hover:dark:focus-visible:`);
36
63
  * 12 is a conservative safety cap that prevents runaway loops on malformed input.
37
- */ const MAX_STRIP_VARIANT_PASSES = 12;
64
+ *
65
+ * @since 0.3.16-canary.0
66
+ */
67
+ const MAX_STRIP_VARIANT_PASSES = 12;
38
68
  /**
39
69
  * Passed when optional `knownBindings` is missing — size 0 disables matching
40
70
  * for `cn` / `tv` identifier resolution.
41
- */ const EMPTY_CN_TV_BINDINGS = /* @__PURE__ */ new Set();
71
+ *
72
+ * @since 0.3.16-canary.0
73
+ */
74
+ const EMPTY_CN_TV_BINDINGS = /* @__PURE__ */ new Set();
42
75
  /**
43
76
  * Bucket sort order — **render pipeline** (lower → earlier in `cn()` output).
44
- *
77
+ *
45
78
  * Matches README: Existence → Position → Layout → Sizing → Spacing → Shape → Background
46
79
  * → Shadow → Typography → Composite → Motion → Starting → Behavior → State → Selector,
47
80
  * then `other` and `arbitrary` as sort tails for unknown utilities and arbitrary properties.
48
- */ const BUCKET_ORDER = {
81
+ *
82
+ * @since 0.3.16-canary.0
83
+ */
84
+ const BUCKET_ORDER = {
49
85
  existence: 0,
50
86
  position: 1,
51
87
  layout: 2,
@@ -72,7 +108,10 @@
72
108
  * `position↔layout` and `layout↔sizing` each justify one hop and the whole “box in flow”
73
109
  * (place + tracks + scrollport) stays in one chunk — co-located because they jointly define
74
110
  * stacking and scroll behavior for the same surface.
75
- */ const COMPATIBLE_BUCKET_SETS = [
111
+ *
112
+ * @since 0.3.16-canary.0
113
+ */
114
+ const COMPATIBLE_BUCKET_SETS = [
76
115
  new Set(["existence", "position"]),
77
116
  new Set(["existence", "layout"]),
78
117
  new Set(["position", "layout"]),
@@ -90,13 +129,19 @@
90
129
  ];
91
130
  /**
92
131
  * Responsive / variant prefix — Tailwind v4 aware.
93
- *
132
+ *
94
133
  * v3: sm: md: … — v4: @sm:, @min-[600px]:, @[480px]:, named @md/sidebar:,
95
134
  * viewport md/sidebar:, min-[100px]: / max-[100px]:, …
96
- */ const RESPONSIVE_PREFIX = /^(?:@(?:min|max)-\[[^\]]+\]:|@\[[^\]]+\]:|@(?:[a-z0-9]+(?:-[a-z0-9]+)*)(?:\/[a-z][a-z0-9-]*)?:|(?:max-|min-)?(?:sm|md|lg|xl|2xl|3xl)(?:\/[a-z][a-z0-9-]*)?:|(?:max-|min-)\[[^\]]+\]:)/;
135
+ *
136
+ * @since 0.3.16-canary.0
137
+ */
138
+ const RESPONSIVE_PREFIX = /^(?:@(?:min|max)-\[[^\]]+\]:|@\[[^\]]+\]:|@(?:[a-z0-9]+(?:-[a-z0-9]+)*)(?:\/[a-z][a-z0-9-]*)?:|(?:max-|min-)?(?:sm|md|lg|xl|2xl|3xl)(?:\/[a-z][a-z0-9-]*)?:|(?:max-|min-)\[[^\]]+\]:)/;
97
139
  /**
98
140
  * State variant stems — hoisted to module scope (not recreated per call).
99
- */ const STATE_PREFIXES = new Set([
141
+ *
142
+ * @since 0.3.16-canary.0
143
+ */
144
+ const STATE_PREFIXES = new Set([
100
145
  "hover",
101
146
  "focus",
102
147
  "focus-within",
@@ -1,12 +1,9 @@
1
- import { applyEditsDescending } from "../../../shell/domain/source-text-edit.support.mjs";
2
- import { buildKnownCnTvBindings, unwrapCnInsideTvCallReplacement } from "./ast/ast-helpers.helper.mjs";
3
- import { listAllCnCallsInsideTvInSourceFile } from "./ast/collectors-tv.collector.mjs";
4
- import { collectGroupTargets, planGroupEditForTarget, targetReplaceStart } from "./ast/targets.model.mjs";
5
- //#region src/domains/arrange/domain/arrange-grouping.domain-service.ts
6
- /**
7
- * Rich domain: plan Tailwind class grouping / cn-in-tv unwrap for a single file.
8
- * Pure: only DomainSourceFile, source strings, and options — no I/O.
9
- */ function toUnwrapPlans(cnInTvCalls, sourceText) {
1
+ import { applyEditsDescending } from "../../core/source-text-edit.mjs";
2
+ import { buildKnownCnTvBindings, unwrapCnInsideTvCallReplacement } from "./ast/helpers.mjs";
3
+ import { listAllCnCallsInsideTvInSourceFile } from "./ast/collectors-tv.mjs";
4
+ import { collectGroupTargets, planGroupEditForTarget, targetReplaceStart } from "./ast/targets.mjs";
5
+ //#region src/arrange/domain/grouping-service.ts
6
+ function toUnwrapPlans(cnInTvCalls, sourceText) {
10
7
  return cnInTvCalls.map((call) => {
11
8
  const replacement = unwrapCnInsideTvCallReplacement(call, sourceText);
12
9
  if (replacement === void 0) return;
@@ -18,6 +15,9 @@ import { collectGroupTargets, planGroupEditForTarget, targetReplaceStart } from
18
15
  };
19
16
  }).filter((plan) => plan !== void 0);
20
17
  }
18
+ /**
19
+ * @since 0.3.16-canary.0
20
+ */
21
21
  function buildGroupFileUnwrapState(domainSfInitial, sourceText) {
22
22
  const cnInTvCalls = listAllCnCallsInsideTvInSourceFile(domainSfInitial, buildKnownCnTvBindings(domainSfInitial));
23
23
  const unwrapPlans = toUnwrapPlans(cnInTvCalls, sourceText);
@@ -34,7 +34,10 @@ function buildGroupFileUnwrapState(domainSfInitial, sourceText) {
34
34
  /**
35
35
  * `domainSfGrouped` must be a parse of `unwrap.textAfterUnwrap` (same `text` as source).
36
36
  * Returns `null` when there is no cn-in-tv and no grouping targets.
37
- */ function tryBuildGroupFileWorkPlan(input) {
37
+ *
38
+ * @since 0.3.16-canary.0
39
+ */
40
+ function tryBuildGroupFileWorkPlan(input) {
38
41
  const { filePath, sourceText, domainSfInitial, domainSfGrouped, withClassName, unwrap } = input;
39
42
  if (domainSfGrouped.text !== unwrap.textAfterUnwrap) throw new Error("Domain invariant: domainSfGrouped.text must match unwrap phase textAfterUnwrap");
40
43
  const groupTargets = collectGroupTargets(domainSfGrouped, filePath);
@@ -64,6 +67,9 @@ function buildGroupFileUnwrapState(domainSfInitial, sourceText) {
64
67
  editSitesCount
65
68
  };
66
69
  }
70
+ /**
71
+ * @since 0.3.16-canary.0
72
+ */
67
73
  function groupFileDryRunNoEdits(filePath) {
68
74
  return {
69
75
  filePath,
@@ -71,6 +77,9 @@ function groupFileDryRunNoEdits(filePath) {
71
77
  changed: 0
72
78
  };
73
79
  }
80
+ /**
81
+ * @since 0.3.16-canary.0
82
+ */
74
83
  function groupFilePreviewTotals(work) {
75
84
  return {
76
85
  filePath: work.filePath,
@@ -78,6 +87,9 @@ function groupFilePreviewTotals(work) {
78
87
  changed: 0
79
88
  };
80
89
  }
90
+ /**
91
+ * @since 0.3.16-canary.0
92
+ */
81
93
  function mergeGroupFileBodyText(work) {
82
94
  const groupEdits = work.plannedGroupEdits.map((plannedEdit) => ({
83
95
  start: plannedEdit.start,
@@ -87,12 +99,21 @@ function mergeGroupFileBodyText(work) {
87
99
  }));
88
100
  return groupEdits.length > 0 ? applyEditsDescending(work.textAfterUnwrap, groupEdits) : work.textAfterUnwrap;
89
101
  }
102
+ /**
103
+ * @since 0.3.16-canary.0
104
+ */
90
105
  function groupFileEditsTouchJsxCn(work) {
91
106
  return work.plannedGroupEdits.some((plannedEdit) => plannedEdit.jsxCn);
92
107
  }
108
+ /**
109
+ * @since 0.3.16-canary.0
110
+ */
93
111
  function countPersistedGroupFileEdits(work) {
94
112
  return work.unwrapEdits.length + work.plannedGroupEdits.length;
95
113
  }
114
+ /**
115
+ * @since 0.3.16-canary.0
116
+ */
96
117
  function groupFileWorkHasNothingToReport(work) {
97
118
  return work.editSitesCount === 0 && work.cnInTvNoReplacement === 0;
98
119
  }
@@ -1,14 +1,16 @@
1
- import { BUCKET_ORDER } from "./constants.domain.mjs";
2
- import { stripVariants, tokenizeClassString } from "./tailwind-token.value-object.mjs";
3
- import { bucketsCompatible, bucketsMergeCompatible, classifyToken, compositeSecondaryOrder, selectorKey, stateKey } from "./tailwind-token-classifier.domain-service.mjs";
4
- //#region src/domains/arrange/domain/grouping.domain.ts
1
+ import { BUCKET_ORDER } from "./constants.mjs";
2
+ import { stripVariants, tokenizeClassString } from "./tailwind-token.mjs";
3
+ import { bucketsCompatible, bucketsMergeCompatible, classifyToken, compositeSecondaryOrder, selectorKey, stateKey } from "./token-classifier.mjs";
4
+ //#region src/arrange/domain/grouping.ts
5
5
  /**
6
6
  * `cn()` grouping: bucket sequence is {@link BUCKET_ORDER} only; tokens are classified with
7
7
  * {@link classifyToken}. Comparators here (`compareClassifiedTailwindTokensForCnGrouping`, …)
8
8
  * are the single place for variant-aware sort — do not reintroduce parallel bucket ordering.
9
- */ /**
9
+ */
10
+ /**
10
11
  * Separates bucket id from variant key in {@link buildFirstVariantKeySourceIndex} map keys.
11
- */ const VARIANT_BUCKET_KEY_SEP = "\0";
12
+ */
13
+ const VARIANT_BUCKET_KEY_SEP = "\0";
12
14
  function isVariantKeyedBucket(bucket) {
13
15
  return bucket === "selector" || bucket === "state" || bucket === "starting";
14
16
  }
@@ -21,13 +23,15 @@ function variantKeyedBlockMapKey(bucket, classToken) {
21
23
  /**
22
24
  * Deterministic lexicographic order for Tailwind class tokens (and token-space signatures).
23
25
  * Used wherever we need stable, locale-agnostic ASCII ordering of utility strings.
24
- */ function compareClassTokensCanonically(left, right) {
26
+ */
27
+ function compareClassTokensCanonically(left, right) {
25
28
  return left.localeCompare(right);
26
29
  }
27
30
  /**
28
31
  * First source index where each `(bucket, variantGroupKey)` appears — drives stable clustering
29
32
  * in {@link suggestCnGroups} without reordering unrelated variant blocks by alphabet alone.
30
- */ function buildFirstVariantKeySourceIndex(classified) {
33
+ */
34
+ function buildFirstVariantKeySourceIndex(classified) {
31
35
  const firstVariantKeySourceIndex = /* @__PURE__ */ new Map();
32
36
  for (const item of classified) {
33
37
  if (!isVariantKeyedBucket(item.bucket)) continue;
@@ -62,7 +66,10 @@ function compareClassifiedTailwindTokensForCnGrouping(left, right, firstVariantK
62
66
  * True when `staticLiteralTexts` carries the **same partition** of Tailwind tokens as
63
67
  * `suggestedGroups` from {@link suggestCnGroups}, ignoring order of arguments and
64
68
  * order of tokens within each chunk.
65
- */ function areCnTailwindPartitionsEquivalent(staticLiteralTexts, suggestedGroups) {
69
+ *
70
+ * @since 0.3.16-canary.0
71
+ */
72
+ function areCnTailwindPartitionsEquivalent(staticLiteralTexts, suggestedGroups) {
66
73
  const partitionSignatures = (chunks) => chunks.map((chunk) => {
67
74
  const classTokens = tokenizeClassString(chunk);
68
75
  return classTokens.length === 0 ? "" : [...classTokens].sort(compareClassTokensCanonically).join(" ");
@@ -75,7 +82,8 @@ function compareClassifiedTailwindTokensForCnGrouping(left, right, firstVariantK
75
82
  }
76
83
  /**
77
84
  * Dominant bucket of a whitespace-delimited class group (for merge heuristics).
78
- */ function dominantBucketOfGroup(groupStr) {
85
+ */
86
+ function dominantBucketOfGroup(groupStr) {
79
87
  const counts = /* @__PURE__ */ new Map();
80
88
  for (const classToken of tokenizeClassString(groupStr)) {
81
89
  const tokenBucket = classifyToken(classToken);
@@ -95,7 +103,8 @@ function compareClassifiedTailwindTokensForCnGrouping(left, right, firstVariantK
95
103
  }
96
104
  /**
97
105
  * Dynamic cap: more tokens → allow more groups, within [BASE, CAP].
98
- */ function dynamicMaxGroups(tokenCount) {
106
+ */
107
+ function dynamicMaxGroups(tokenCount) {
99
108
  const byTokens = Math.ceil(tokenCount / 2) + 2;
100
109
  return Math.max(4, Math.min(24, byTokens));
101
110
  }
@@ -107,7 +116,8 @@ function compareClassifiedTailwindTokensForCnGrouping(left, right, firstVariantK
107
116
  * array, guaranteeing progress) or finds no singleton that can merge and exits
108
117
  * via `changed = false`. Two mutually-incompatible singletons both hit
109
118
  * `continue` and `changed` stays false, so the loop exits without spinning.
110
- */ function mergeSingletons(groups) {
119
+ */
120
+ function mergeSingletons(groups) {
111
121
  if (groups.length <= 1) return groups;
112
122
  const result = [...groups];
113
123
  let changed = true;
@@ -145,7 +155,8 @@ function compareClassifiedTailwindTokensForCnGrouping(left, right, firstVariantK
145
155
  }
146
156
  /**
147
157
  * Tie-break when two merge candidates are both {@link bucketsMergeCompatible} (same bucket or COMPATIBLE_BUCKET_SETS).
148
- */ function capMergePenalty(leftBucket, rightBucket) {
158
+ */
159
+ function capMergePenalty(leftBucket, rightBucket) {
149
160
  if (leftBucket === rightBucket) return 0;
150
161
  if (bucketsCompatible(leftBucket, rightBucket)) return 0;
151
162
  return 500;
@@ -159,7 +170,8 @@ function compareClassifiedTailwindTokensForCnGrouping(left, right, firstVariantK
159
170
  * pairs. In practice `n` is bounded by {@link MAX_GROUPS_CAP} (24) and typical class strings
160
171
  * are well below that, so this is not a concern for the current usage pattern. If this ever
161
172
  * runs in a high-throughput batch mode, consider a priority-queue approach.
162
- */ function capGroups(groups, maxGroups) {
173
+ */
174
+ function capGroups(groups, maxGroups) {
163
175
  const result = [...groups];
164
176
  const lengths = result.map((groupStr) => tokenizeClassString(groupStr).length);
165
177
  while (result.length > maxGroups) {
@@ -202,7 +214,8 @@ function compareClassifiedTailwindTokensForCnGrouping(left, right, firstVariantK
202
214
  * **later** chunk that is predominantly `state` **and** contains some `animate-*` utility —
203
215
  * skipping intermediate `state` chunks (`sm:…`, `group-data-[…]:…`, etc.) that sit between
204
216
  * `ease-ui` and `data-open:animate-*`. If no such chunk exists, the ease chunk is left as-is.
205
- */ function mergeEaseTimingIntoFollowingAnimatedState(groups) {
217
+ */
218
+ function mergeEaseTimingIntoFollowingAnimatedState(groups) {
206
219
  const result = [...groups];
207
220
  let changed = true;
208
221
  while (changed) {
@@ -237,6 +250,9 @@ function chunkIsOnlyEaseTimingMotion(groupStr) {
237
250
  if (classTokens.length === 0) return false;
238
251
  return classTokens.every((classToken) => classifyToken(classToken) === "motion" && /^ease-/.test(stripVariants(classToken)));
239
252
  }
253
+ /**
254
+ * @since 0.3.16-canary.0
255
+ */
240
256
  function suggestCnGroups(classString) {
241
257
  const tokens = tokenizeClassString(classString);
242
258
  if (tokens.length === 0) return [];
@@ -247,7 +263,8 @@ function suggestCnGroups(classString) {
247
263
  * variants. Block order follows the **first source index** of each key so unrelated
248
264
  * states keep document order; within a key, `classToken` order is lexicographic for
249
265
  * idempotency.
250
- */ const classified = tokens.map((classToken, index) => ({
266
+ */
267
+ const classified = tokens.map((classToken, index) => ({
251
268
  classToken,
252
269
  bucket: classifyToken(classToken),
253
270
  index
@@ -257,7 +274,8 @@ function suggestCnGroups(classString) {
257
274
  const rawGroups = [];
258
275
  /**
259
276
  * Bucket of the last token already placed in the current run (pairwise compat with `COMPATIBLE_BUCKET_SETS`).
260
- */ let lastBucketInRun = null;
277
+ */
278
+ let lastBucketInRun = null;
261
279
  let currentStateKey = null;
262
280
  let currentTokens = [];
263
281
  const flush = () => {
@@ -304,7 +322,10 @@ function suggestCnGroups(classString) {
304
322
  /**
305
323
  * One label per suggested group: a single bucket name, or `mixed:a+b` when that chunk
306
324
  * spans multiple Tailwind buckets (matches `codefast arrange group` stdout).
307
- */ function summarizeGroupBucketLabels(groups) {
325
+ *
326
+ * @since 0.3.16-canary.0
327
+ */
328
+ function summarizeGroupBucketLabels(groups) {
308
329
  return groups.map((g) => {
309
330
  const uniq = new Set(tokenizeClassString(g).map(classifyToken));
310
331
  if (uniq.size !== 1) return `mixed:${[...uniq].sort(compareClassTokensCanonically).join("+")}`;
@@ -1,5 +1,5 @@
1
- import { isDomainImportDeclaration, isDomainNamedImports, isDomainStringLiteral } from "./ast/ast-node.model.mjs";
2
- //#region src/domains/arrange/domain/imports.domain.ts
1
+ import { isDomainImportDeclaration, isDomainNamedImports, isDomainStringLiteral } from "./ast/ast-node.mjs";
2
+ //#region src/arrange/domain/imports.ts
3
3
  function sourceFileImportsCn(sourceFile) {
4
4
  for (const statement of sourceFile.statements) {
5
5
  if (!isDomainImportDeclaration(statement) || !statement.importClause) continue;
@@ -13,7 +13,8 @@ function sourceFileImportsCn(sourceFile) {
13
13
  }
14
14
  /**
15
15
  * Resolve the module specifier used when injecting a `cn` import into a file.
16
- */ function cnModuleSpecifierForFile(filePath, override) {
16
+ */
17
+ function cnModuleSpecifierForFile(filePath, override) {
17
18
  if (override) return override;
18
19
  return "@codefast/tailwind-variants";
19
20
  }
@@ -24,6 +25,9 @@ function findImportDeclarationFromModule(sourceFile, moduleSpecifier) {
24
25
  if (isDomainStringLiteral(spec) && spec.text === moduleSpecifier) return statement;
25
26
  }
26
27
  }
28
+ /**
29
+ * @since 0.3.16-canary.0
30
+ */
27
31
  function ensureCnImport(sourceFile, cnImportOverride) {
28
32
  const sourceText = sourceFile.text;
29
33
  if (sourceFileImportsCn(sourceFile)) return sourceText;
@@ -1,15 +1,20 @@
1
- import { indentOfLineContaining } from "../../../shell/domain/source-text-edit.support.mjs";
2
- //#region src/domains/arrange/domain/source-text-formatters.formatter.ts
1
+ import { indentOfLineContaining } from "../../core/source-text-edit.mjs";
2
+ //#region src/arrange/domain/source-text-formatters.ts
3
+ /**
4
+ * @since 0.3.16-canary.0
5
+ */
3
6
  function escapeTsStringLiteralContent(group) {
4
7
  return group.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"");
5
8
  }
6
- /** Trailing comma on multiline Tailwind group lists (matches prior `cn` / `tv` array style). */ function commaAfterTailwindGroupLine(groupIndex, groupCount) {
9
+ /** Trailing comma on multiline Tailwind group lists (matches prior `cn` / `tv` array style). */
10
+ function commaAfterTailwindGroupLine(groupIndex, groupCount) {
7
11
  return groupIndex < groupCount - 1 || groupCount > 1 ? "," : "";
8
12
  }
9
13
  /**
10
14
  * Multiline fragment for use *inside* an existing `cn(...)` — replaces one
11
15
  * string argument with several, without producing `cn(cn(...))`.
12
- */ function formatCnArguments(groups, options) {
16
+ */
17
+ function formatCnArguments(groups, options) {
13
18
  const indent = options?.indent ?? " ";
14
19
  const commaAfterLast = options?.commaAfterLastGroup ?? options?.trailingClassName ?? false;
15
20
  const lines = [];
@@ -22,6 +27,9 @@ function escapeTsStringLiteralContent(group) {
22
27
  if (options?.trailingClassName) lines.push(`${indent}className,`);
23
28
  return lines.join("\n");
24
29
  }
30
+ /**
31
+ * @since 0.3.16-canary.0
32
+ */
25
33
  function formatCnCall(groups, options) {
26
34
  const lines = ["cn("];
27
35
  const commaOnEachStringLine = groups.length > 1 || Boolean(options?.trailingClassName);
@@ -35,6 +43,9 @@ function formatCnCall(groups, options) {
35
43
  lines.push(")");
36
44
  return lines.join("\n");
37
45
  }
46
+ /**
47
+ * @since 0.3.16-canary.0
48
+ */
38
49
  function formatArray(groups) {
39
50
  const lines = ["["];
40
51
  for (let i = 0; i < groups.length; i++) {
@@ -49,7 +60,10 @@ function formatArray(groups) {
49
60
  /**
50
61
  * Multiple string literals as sequential lines for insertion **inside** an existing array
51
62
  * (e.g. `tv({ base: [ … ] })` — avoids `[[ "a", "b" ]]` when replacing one long string).
52
- */ function formatArrayElementsAsSiblingLines(groups, continuationPrefix) {
63
+ *
64
+ * @since 0.3.16-canary.0
65
+ */
66
+ function formatArrayElementsAsSiblingLines(groups, continuationPrefix) {
53
67
  const segments = [];
54
68
  for (let i = 0; i < groups.length; i++) {
55
69
  const group = groups[i];
@@ -60,6 +74,9 @@ function formatArray(groups) {
60
74
  }
61
75
  return segments.join("");
62
76
  }
77
+ /**
78
+ * @since 0.3.16-canary.0
79
+ */
63
80
  function formatJsxCnAttributeValue(groups, source, valueNodeStart) {
64
81
  const baseIndent = indentOfLineContaining(source, valueNodeStart);
65
82
  return `{cn(\n${formatCnArguments(groups, {
@@ -1,5 +1,8 @@
1
- import "./constants.domain.mjs";
2
- //#region src/domains/arrange/domain/tailwind-token.value-object.ts
1
+ import "./constants.mjs";
2
+ //#region src/arrange/domain/tailwind-token.ts
3
+ /**
4
+ * @since 0.3.16-canary.0
5
+ */
3
6
  function tokenizeClassString(classString) {
4
7
  return classString.trim().split(/\s+/).filter(Boolean);
5
8
  }
@@ -7,7 +10,10 @@ function tokenizeClassString(classString) {
7
10
  * Index of the first `:` that separates a Tailwind variant segment from the rest.
8
11
  * Colons inside `[...]` (at positive bracket depth) are ignored so selectors like
9
12
  * `[&_a:hover]:text-red-500` split as `[&_a:hover]:` + `text-red-500`.
10
- */ function indexOfFirstVariantColon(text) {
13
+ *
14
+ * @since 0.3.16-canary.0
15
+ */
16
+ function indexOfFirstVariantColon(text) {
11
17
  let depth = 0;
12
18
  for (let i = 0; i < text.length; i++) {
13
19
  const ch = text[i];
@@ -17,6 +23,9 @@ function tokenizeClassString(classString) {
17
23
  }
18
24
  return -1;
19
25
  }
26
+ /**
27
+ * @since 0.3.16-canary.0
28
+ */
20
29
  function stripVariants(token) {
21
30
  let withoutVariants = token;
22
31
  const maxStripVariantPasses = 12;
@@ -1,6 +1,6 @@
1
- import { COMPATIBLE_BUCKET_SETS, RESPONSIVE_PREFIX, STATE_PREFIXES } from "./constants.domain.mjs";
2
- import { indexOfFirstVariantColon, stripVariants } from "./tailwind-token.value-object.mjs";
3
- //#region src/domains/arrange/domain/tailwind-token-classifier.domain-service.ts
1
+ import { COMPATIBLE_BUCKET_SETS, RESPONSIVE_PREFIX, STATE_PREFIXES } from "./constants.mjs";
2
+ import { indexOfFirstVariantColon, stripVariants } from "./tailwind-token.mjs";
3
+ //#region src/arrange/domain/token-classifier.ts
4
4
  /**
5
5
  * Bare-token classification is **total** (always returns a {@link Bucket}). The `Result` pattern
6
6
  * (`isOk` / `isErr`) belongs at arrange use-case / I/O boundaries, not on this hot path.
@@ -9,7 +9,8 @@ import { indexOfFirstVariantColon, stripVariants } from "./tailwind-token.value-
9
9
  * numbered `nth-*`, media features, v4 `in-[…]`, child selectors `*` / `**`, …).
10
10
  * When these are missed, `isStateToken` is false and the variant is stripped — the
11
11
  * utility is then bucketed as if it were unconditional, which breaks preview grouping.
12
- */ function isCompoundOrMediaVariantPrefix(prefix) {
12
+ */
13
+ function isCompoundOrMediaVariantPrefix(prefix) {
13
14
  if (prefix === "*" || prefix === "**") return true;
14
15
  if (prefix === "inert") return true;
15
16
  if (prefix.startsWith("has-")) return true;
@@ -22,7 +23,8 @@ import { indexOfFirstVariantColon, stripVariants } from "./tailwind-token.value-
22
23
  }
23
24
  /**
24
25
  * Outermost variant segment: first `:` at bracket depth 0 → text before it.
25
- */ function firstLeadingVariantPrefix(token) {
26
+ */
27
+ function firstLeadingVariantPrefix(token) {
26
28
  const colonIdx = indexOfFirstVariantColon(token);
27
29
  if (colonIdx === -1) return;
28
30
  return token.slice(0, colonIdx);
@@ -58,7 +60,10 @@ function isStateToken(token) {
58
60
  /**
59
61
  * Secondary sort inside the **composite** bucket: opacity / blend / isolation →
60
62
  * 3D context → 3D transforms → 2D transforms → filters → will-change.
61
- */ function compositeSecondaryOrder(bareUtility) {
63
+ *
64
+ * @since 0.3.16-canary.0
65
+ */
66
+ function compositeSecondaryOrder(bareUtility) {
62
67
  const b = bareUtility;
63
68
  if (/^opacity(?:-|$)/.test(b) || /^mix-blend-/.test(b) || /^isolation(?:-|$)/.test(b) || b === "isolate") return 0;
64
69
  if (b === "transform-3d" || /^perspective(?:-|$)/.test(b)) return 10;
@@ -71,7 +76,8 @@ function isStateToken(token) {
71
76
  }
72
77
  /**
73
78
  * Classify a **bare** utility (no `hover:` / `md:` / … prefixes).
74
- */ function classifyBareUtility(bareUtility) {
79
+ */
80
+ function classifyBareUtility(bareUtility) {
75
81
  const b = bareUtility;
76
82
  if (/^@container(?:\/[a-z][a-z0-9-]*)?$/i.test(b)) return "existence";
77
83
  if (/^(?:hidden|contents|sr-only|not-sr-only|list-item|flow-root)$/.test(b) || /^(?:block|inline-block|inline)$/.test(b)) return "existence";
@@ -98,13 +104,17 @@ function isStateToken(token) {
98
104
  * scope utilities to descendants — bucket as `selector` so they chunk apart from base layout/typography
99
105
  * and are labeled distinctly from interactive `state` variants (`hover:`, `data-[…]:`, …).
100
106
  * Pure arbitrary **properties** (`[--x]:`, `[color:red]`) have no `&` in the leading `[…]` segment.
101
- */ function isArbitraryParentSelectorStateToken(token) {
107
+ */
108
+ function isArbitraryParentSelectorStateToken(token) {
102
109
  if (/^\[&/.test(token)) return true;
103
110
  if (!token.startsWith("[")) return false;
104
111
  const colonIdx = indexOfFirstVariantColon(token);
105
112
  if (colonIdx === -1 || colonIdx === 0 || token[colonIdx - 1] !== "]") return false;
106
113
  return token.slice(0, colonIdx).includes("&");
107
114
  }
115
+ /**
116
+ * @since 0.3.16-canary.0
117
+ */
108
118
  function classifyToken(token) {
109
119
  if (isArbitraryParentSelectorStateToken(token)) return "selector";
110
120
  if (isSelectorVariantToken(token)) return "selector";
@@ -122,19 +132,22 @@ function classifyToken(token) {
122
132
  * Stable key for `data-[…]` variants so unrelated selectors stay split, while
123
133
  * **branching on the full bracket** (e.g. `data-[vaul-drawer-direction=bottom]`
124
134
  * vs `=left`) keeps direction-specific utilities in separate state groups.
125
- */ function dataAttributeStem(token) {
135
+ */
136
+ function dataAttributeStem(token) {
126
137
  const match = token.match(/^data-\[([^\]]*)\]/);
127
138
  return match ? `data-[${match[1]}]` : "data";
128
139
  }
129
140
  /**
130
141
  * Same idea as {@link dataAttributeStem} for `aria-[…]:` variants.
131
- */ function ariaAttributeStem(token) {
142
+ */
143
+ function ariaAttributeStem(token) {
132
144
  const match = token.match(/^aria-\[([^\]]*)\]/);
133
145
  return match ? `aria-[${match[1]}]` : "aria";
134
146
  }
135
147
  /**
136
148
  * `in-data-[…]:` — normalize on the full bracket expression like {@link dataAttributeStem}.
137
- */ function inDataAttributeStem(token) {
149
+ */
150
+ function inDataAttributeStem(token) {
138
151
  const match = token.match(/^in-data-\[([^\]]*)\]/);
139
152
  return match ? `in-data-[${match[1]}]` : "in-data";
140
153
  }
@@ -144,10 +157,13 @@ function classifyToken(token) {
144
157
  * Uses the **full variant stack** (every `:` segment outside `[…]`), not only the
145
158
  * outermost prefix, so `@md/foo:[&>*]:w-auto` and `@md/foo:has-[…]:mt-px` stay in
146
159
  * separate groups while `hover:opacity` still keys as `hover` + `opacity`…
147
- *
160
+ *
148
161
  * `data-[…]` / `aria-[…]` normalize the first segment via {@link dataAttributeStem} /
149
162
  * {@link ariaAttributeStem} (full token required for bracket capture).
150
- */ function stateKey(token) {
163
+ *
164
+ * @since 0.3.16-canary.0
165
+ */
166
+ function stateKey(token) {
151
167
  const layers = [];
152
168
  let rest = token;
153
169
  while (rest.length > 0) {
@@ -169,20 +185,29 @@ const SELECTOR_KEY_SEP = "";
169
185
  * Variant key for {@link Bucket} `"selector"` tokens in {@link suggestCnGroups}.
170
186
  * Reuses {@link stateKey} layer splitting, then normalizes common shadcn/Radix SVG patterns so
171
187
  * `[&_svg]:…` and `[&_svg:not([class*='size-'])]:…` stay in one chunk.
172
- */ function selectorKey(token) {
188
+ *
189
+ * @since 0.3.16-canary.0
190
+ */
191
+ function selectorKey(token) {
173
192
  return stateKey(token).split(SELECTOR_KEY_SEP).map(normalizeSelectorVariantLayer).join(SELECTOR_KEY_SEP);
174
193
  }
175
194
  function normalizeSelectorVariantLayer(layer) {
176
195
  if (layer === "[&_svg:not([class*='size-'])]") return "[&_svg]";
177
196
  return layer;
178
197
  }
198
+ /**
199
+ * @since 0.3.16-canary.0
200
+ */
179
201
  function bucketsCompatible(a, b) {
180
202
  if (a === b) return true;
181
203
  return COMPATIBLE_BUCKET_SETS.some((bucketSet) => bucketSet.has(a) && bucketSet.has(b));
182
204
  }
183
205
  /**
184
206
  * Like {@link bucketsCompatible}, but never merge two distinct state / starting / selector variant blobs.
185
- */ function bucketsMergeCompatible(a, b) {
207
+ *
208
+ * @since 0.3.16-canary.0
209
+ */
210
+ function bucketsMergeCompatible(a, b) {
186
211
  if (a === "state" && b === "state") return false;
187
212
  if (a === "starting" && b === "starting") return false;
188
213
  if (a === "selector" && b === "selector") return false;