@codefast/cli 0.3.12 → 0.3.13-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 (225) hide show
  1. package/README.md +206 -51
  2. package/dist/analyze-DvtJ3j9m.mjs +92 -0
  3. package/dist/arrange-Bbh62DrU.mjs +107 -0
  4. package/dist/arrange-BqP2DI3w.mjs +8 -0
  5. package/dist/ast-helpers-MDRYuIAk.mjs +115 -0
  6. package/dist/{bin.js → bin.mjs} +4 -1
  7. package/dist/caught-unknown-message-CRhkDWO7.mjs +20 -0
  8. package/dist/collectors-cn-BS8TTQ8O.mjs +61 -0
  9. package/dist/collectors-jsx-D9tL2ZPM.mjs +20 -0
  10. package/dist/collectors-tv-D2PXZaRt.mjs +170 -0
  11. package/dist/colors-BjdkCfGK.mjs +12 -0
  12. package/dist/commands/arrange.mjs +2 -0
  13. package/dist/commands/mirror.mjs +2 -0
  14. package/dist/commands/mirror.test.mjs +48 -0
  15. package/dist/commands/tag.mjs +2 -0
  16. package/dist/config-DVia_f9a.mjs +3 -0
  17. package/dist/config-reporter-SCcWDZc1.mjs +10 -0
  18. package/dist/constants-AtPhF-Ct.mjs +169 -0
  19. package/dist/constants-Bq5wdyLC.mjs +8 -0
  20. package/dist/dirent-list-BSDeRG5i.mjs +14 -0
  21. package/dist/engine-CcWEHSCq.mjs +287 -0
  22. package/dist/engine-DRTdkc5u.mjs +236 -0
  23. package/dist/errors-CFH4w6ic.mjs +15 -0
  24. package/dist/errors-CpU1ogfA.mjs +19 -0
  25. package/dist/formatters-DGE8jJ53.mjs +52 -0
  26. package/dist/group-file-ClW7XdD6.mjs +113 -0
  27. package/dist/grouping-c1grpCQT.mjs +235 -0
  28. package/dist/imports-DSVq1xhB.mjs +54 -0
  29. package/dist/lib/arrange/application/analyze.mjs +2 -0
  30. package/dist/lib/arrange/application/analyze.test.mjs +171 -0
  31. package/dist/lib/arrange/application/group-file.mjs +2 -0
  32. package/dist/lib/arrange/application/group-file.test.mjs +273 -0
  33. package/dist/lib/arrange/application/run-target.mjs +2 -0
  34. package/dist/lib/arrange/application/run-target.test.mjs +97 -0
  35. package/dist/lib/arrange/domain/ast/ast-helpers.mjs +2 -0
  36. package/dist/lib/arrange/domain/ast/ast-helpers.test.mjs +148 -0
  37. package/dist/lib/arrange/domain/ast/collectors-cn.mjs +2 -0
  38. package/dist/lib/arrange/domain/ast/collectors-cn.test.mjs +58 -0
  39. package/dist/lib/arrange/domain/ast/collectors-jsx.mjs +2 -0
  40. package/dist/lib/arrange/domain/ast/collectors-jsx.test.mjs +20 -0
  41. package/dist/lib/arrange/domain/ast/collectors-tv.mjs +2 -0
  42. package/dist/lib/arrange/domain/ast/collectors-tv.test.mjs +145 -0
  43. package/dist/lib/arrange/domain/ast/targets.mjs +2 -0
  44. package/dist/lib/arrange/domain/ast/targets.test.mjs +50 -0
  45. package/dist/lib/arrange/domain/constants.mjs +2 -0
  46. package/dist/lib/arrange/domain/errors.mjs +2 -0
  47. package/dist/lib/arrange/domain/grouping.mjs +2 -0
  48. package/dist/lib/arrange/domain/grouping.test.mjs +186 -0
  49. package/dist/lib/arrange/domain/imports.mjs +2 -0
  50. package/dist/lib/arrange/domain/imports.test.mjs +48 -0
  51. package/dist/lib/arrange/domain/tokenizer.mjs +2 -0
  52. package/dist/lib/arrange/domain/tokenizer.test.mjs +165 -0
  53. package/dist/lib/arrange/index.mjs +16 -0
  54. package/dist/lib/arrange/infra/walk.mjs +2 -0
  55. package/dist/lib/arrange/infra/walk.test.mjs +50 -0
  56. package/dist/lib/arrange/presentation/formatters.mjs +2 -0
  57. package/dist/lib/arrange/presentation/formatters.test.mjs +73 -0
  58. package/dist/lib/arrange/presentation/report.mjs +2 -0
  59. package/dist/lib/config/domain/schema.mjs +2 -0
  60. package/dist/lib/config/index.mjs +4 -0
  61. package/dist/lib/config/infra/loader.mjs +2 -0
  62. package/dist/lib/config/infra/loader.test.mjs +116 -0
  63. package/dist/lib/infra/caught-unknown-message.mjs +2 -0
  64. package/dist/lib/infra/config-reporter.mjs +2 -0
  65. package/dist/lib/infra/node-io.mjs +2 -0
  66. package/dist/lib/infra/workspace/repo-root.mjs +2 -0
  67. package/dist/lib/infra/workspace/repo-root.test.mjs +45 -0
  68. package/dist/lib/mirror/application/engine.mjs +2 -0
  69. package/dist/lib/mirror/application/sync.mjs +2 -0
  70. package/dist/lib/mirror/application/sync.test.mjs +482 -0
  71. package/dist/lib/mirror/domain/constants.mjs +2 -0
  72. package/dist/lib/mirror/domain/errors.mjs +2 -0
  73. package/dist/lib/mirror/domain/types.mjs +1 -0
  74. package/dist/lib/mirror/index.mjs +3 -0
  75. package/dist/lib/mirror/infra/dirent-list.mjs +2 -0
  76. package/dist/lib/mirror/infra/dirent-list.test.mjs +18 -0
  77. package/dist/lib/mirror/infra/package-filter.mjs +2 -0
  78. package/dist/lib/mirror/infra/package-filter.test.mjs +32 -0
  79. package/dist/lib/mirror/infra/path-normalizer.mjs +2 -0
  80. package/dist/lib/mirror/infra/update-pkg.mjs +2 -0
  81. package/dist/lib/mirror/infra/workspace-packages.mjs +2 -0
  82. package/dist/lib/mirror/infra/workspace-packages.test.mjs +167 -0
  83. package/dist/lib/mirror/presentation/reporter.mjs +2 -0
  84. package/dist/lib/tag/application/engine.mjs +2 -0
  85. package/dist/lib/tag/application/engine.test.mjs +159 -0
  86. package/dist/lib/tag/domain/types.mjs +1 -0
  87. package/dist/lib/tag/index.mjs +5 -0
  88. package/dist/lib/tag/infra/target-resolver.mjs +2 -0
  89. package/dist/lib/tag/presentation/colors.mjs +2 -0
  90. package/dist/lib/tag/presentation/tag-presenter.mjs +2 -0
  91. package/dist/lib/tag/presentation/tag-presenter.test.mjs +29 -0
  92. package/dist/loader-DpN-zl5g.mjs +78 -0
  93. package/dist/mirror-CbGiLXKT.mjs +2 -0
  94. package/dist/mirror-DqJE3ejL.mjs +66 -0
  95. package/dist/node-io-DFRbHO6m.mjs +30 -0
  96. package/dist/package-filter-C_8ryqvz.mjs +26 -0
  97. package/dist/path-normalizer-D4ceBO3l.mjs +7 -0
  98. package/dist/program-DR-H4tH3.mjs +34 -0
  99. package/dist/program.mjs +2 -0
  100. package/dist/repo-root-HmtIEm6J.mjs +22 -0
  101. package/dist/report-DYxvw34T.mjs +24 -0
  102. package/dist/reporter-CwIe_tAM.mjs +108 -0
  103. package/dist/run-target-CJWrqNT4.mjs +65 -0
  104. package/dist/schema-Or8RAARd.mjs +25 -0
  105. package/dist/sync-e1Sp-shi.mjs +157 -0
  106. package/dist/tag-Cd9JujU5.mjs +2 -0
  107. package/dist/tag-hEKVZ_-7.mjs +58 -0
  108. package/dist/tag-presenter-Cs66UBsH.mjs +63 -0
  109. package/dist/target-resolver-CR3FXMTy.mjs +170 -0
  110. package/dist/targets-pCTslnFh.mjs +106 -0
  111. package/dist/tokenizer-CeZx73Zu.mjs +216 -0
  112. package/dist/update-pkg-CnGfjWBy.mjs +37 -0
  113. package/dist/walk-DjgQn66n.mjs +33 -0
  114. package/dist/workspace-packages-K2NYTHJq.mjs +159 -0
  115. package/package.json +11 -11
  116. package/dist/bin.d.ts +0 -3
  117. package/dist/bin.d.ts.map +0 -1
  118. package/dist/commands/arrange.d.ts +0 -3
  119. package/dist/commands/arrange.d.ts.map +0 -1
  120. package/dist/commands/arrange.js +0 -114
  121. package/dist/commands/mirror.d.ts +0 -4
  122. package/dist/commands/mirror.d.ts.map +0 -1
  123. package/dist/commands/mirror.js +0 -63
  124. package/dist/lib/arrange/analyze.d.ts +0 -4
  125. package/dist/lib/arrange/analyze.d.ts.map +0 -1
  126. package/dist/lib/arrange/analyze.js +0 -105
  127. package/dist/lib/arrange/ast/collectors-cn.d.ts +0 -10
  128. package/dist/lib/arrange/ast/collectors-cn.d.ts.map +0 -1
  129. package/dist/lib/arrange/ast/collectors-cn.js +0 -84
  130. package/dist/lib/arrange/ast/collectors-jsx.d.ts +0 -4
  131. package/dist/lib/arrange/ast/collectors-jsx.d.ts.map +0 -1
  132. package/dist/lib/arrange/ast/collectors-jsx.js +0 -18
  133. package/dist/lib/arrange/ast/collectors-tv.d.ts +0 -13
  134. package/dist/lib/arrange/ast/collectors-tv.d.ts.map +0 -1
  135. package/dist/lib/arrange/ast/collectors-tv.js +0 -273
  136. package/dist/lib/arrange/ast/targets.d.ts +0 -8
  137. package/dist/lib/arrange/ast/targets.d.ts.map +0 -1
  138. package/dist/lib/arrange/ast/targets.js +0 -143
  139. package/dist/lib/arrange/ast/utils.d.ts +0 -36
  140. package/dist/lib/arrange/ast/utils.d.ts.map +0 -1
  141. package/dist/lib/arrange/ast/utils.js +0 -138
  142. package/dist/lib/arrange/constants.d.ts +0 -68
  143. package/dist/lib/arrange/constants.d.ts.map +0 -1
  144. package/dist/lib/arrange/constants.js +0 -179
  145. package/dist/lib/arrange/errors.d.ts +0 -14
  146. package/dist/lib/arrange/errors.d.ts.map +0 -1
  147. package/dist/lib/arrange/errors.js +0 -16
  148. package/dist/lib/arrange/formatters.d.ts +0 -16
  149. package/dist/lib/arrange/formatters.d.ts.map +0 -1
  150. package/dist/lib/arrange/formatters.js +0 -57
  151. package/dist/lib/arrange/group-file.d.ts +0 -4
  152. package/dist/lib/arrange/group-file.d.ts.map +0 -1
  153. package/dist/lib/arrange/group-file.js +0 -115
  154. package/dist/lib/arrange/grouping.d.ts +0 -41
  155. package/dist/lib/arrange/grouping.d.ts.map +0 -1
  156. package/dist/lib/arrange/grouping.js +0 -280
  157. package/dist/lib/arrange/imports.d.ts +0 -6
  158. package/dist/lib/arrange/imports.d.ts.map +0 -1
  159. package/dist/lib/arrange/imports.js +0 -74
  160. package/dist/lib/arrange/report.d.ts +0 -4
  161. package/dist/lib/arrange/report.d.ts.map +0 -1
  162. package/dist/lib/arrange/report.js +0 -37
  163. package/dist/lib/arrange/run-target.d.ts +0 -4
  164. package/dist/lib/arrange/run-target.d.ts.map +0 -1
  165. package/dist/lib/arrange/run-target.js +0 -28
  166. package/dist/lib/arrange/tokenizer.d.ts +0 -37
  167. package/dist/lib/arrange/tokenizer.d.ts.map +0 -1
  168. package/dist/lib/arrange/tokenizer.js +0 -390
  169. package/dist/lib/arrange/types.d.ts +0 -104
  170. package/dist/lib/arrange/types.d.ts.map +0 -1
  171. package/dist/lib/arrange/types.js +0 -7
  172. package/dist/lib/arrange/walk.d.ts +0 -4
  173. package/dist/lib/arrange/walk.d.ts.map +0 -1
  174. package/dist/lib/arrange/walk.js +0 -34
  175. package/dist/lib/arrange.d.ts +0 -41
  176. package/dist/lib/arrange.d.ts.map +0 -1
  177. package/dist/lib/arrange.js +0 -38
  178. package/dist/lib/infra/fs-contract.d.ts +0 -28
  179. package/dist/lib/infra/fs-contract.d.ts.map +0 -1
  180. package/dist/lib/infra/node-io.d.ts +0 -4
  181. package/dist/lib/infra/node-io.d.ts.map +0 -1
  182. package/dist/lib/infra/node-io.js +0 -27
  183. package/dist/lib/mirror/config.d.ts +0 -7
  184. package/dist/lib/mirror/config.d.ts.map +0 -1
  185. package/dist/lib/mirror/config.js +0 -146
  186. package/dist/lib/mirror/constants.d.ts +0 -10
  187. package/dist/lib/mirror/constants.d.ts.map +0 -1
  188. package/dist/lib/mirror/constants.js +0 -13
  189. package/dist/lib/mirror/engine.d.ts +0 -12
  190. package/dist/lib/mirror/engine.d.ts.map +0 -1
  191. package/dist/lib/mirror/engine.js +0 -248
  192. package/dist/lib/mirror/errors.d.ts +0 -10
  193. package/dist/lib/mirror/errors.d.ts.map +0 -1
  194. package/dist/lib/mirror/errors.js +0 -12
  195. package/dist/lib/mirror/package-filter.d.ts +0 -8
  196. package/dist/lib/mirror/package-filter.d.ts.map +0 -1
  197. package/dist/lib/mirror/package-filter.js +0 -33
  198. package/dist/lib/mirror/reporter.d.ts +0 -24
  199. package/dist/lib/mirror/reporter.d.ts.map +0 -1
  200. package/dist/lib/mirror/reporter.js +0 -126
  201. package/dist/lib/mirror/sync.d.ts +0 -4
  202. package/dist/lib/mirror/sync.d.ts.map +0 -1
  203. package/dist/lib/mirror/sync.js +0 -135
  204. package/dist/lib/mirror/types.d.ts +0 -81
  205. package/dist/lib/mirror/types.d.ts.map +0 -1
  206. package/dist/lib/mirror/update-pkg.d.ts +0 -13
  207. package/dist/lib/mirror/update-pkg.d.ts.map +0 -1
  208. package/dist/lib/mirror/update-pkg.js +0 -39
  209. package/dist/lib/mirror/workspace-packages.d.ts +0 -26
  210. package/dist/lib/mirror/workspace-packages.d.ts.map +0 -1
  211. package/dist/lib/mirror/workspace-packages.js +0 -160
  212. package/dist/lib/mirror.d.ts +0 -7
  213. package/dist/lib/mirror.d.ts.map +0 -1
  214. package/dist/lib/mirror.js +0 -5
  215. package/dist/lib/repo-root.d.ts +0 -7
  216. package/dist/lib/repo-root.d.ts.map +0 -1
  217. package/dist/lib/repo-root.js +0 -23
  218. package/dist/lib/shared/utils.d.ts +0 -9
  219. package/dist/lib/shared/utils.d.ts.map +0 -1
  220. package/dist/lib/shared/utils.js +0 -12
  221. package/dist/program.d.ts +0 -4
  222. package/dist/program.d.ts.map +0 -1
  223. package/dist/program.js +0 -38
  224. /package/dist/lib/{infra/fs-contract.js → arrange/domain/types.mjs} +0 -0
  225. /package/dist/lib/{mirror/types.js → infra/fs-contract.mjs} +0 -0
package/README.md CHANGED
@@ -1,100 +1,255 @@
1
1
  # @codefast/cli
2
2
 
3
- Two tools bundled in one CLI:
3
+ A focused CLI for two recurring maintenance tasks in monorepos:
4
4
 
5
- | Command | Purpose |
6
- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
7
- | `mirror sync` | Regenerate `package.json` `exports` from each package's built `dist/` tree (pnpm workspace packages) |
8
- | `arrange` | Analyze, dry-run, or apply suggested grouping for long Tailwind class strings inside `cn()` / `tv()` call sites (Tailwind v4–oriented heuristics), or `group` a pasted class string |
5
+ - **`arrange`** — analyze and regroup Tailwind class strings inside `cn()` / `tv()` calls according to a consistent render-pipeline order.
6
+ - **`mirror`** — regenerate `package.json` `exports` fields from built `dist/` trees across a pnpm workspace.
7
+ - **`tag`** (alias: **`annotate`**) — auto-add `@since <version>` to exported TypeScript declarations that are still missing version metadata.
8
+
9
+ ```mermaid
10
+ flowchart LR
11
+ R[codefast]
12
+ R --> A[arrange]
13
+ R --> M[mirror]
14
+ R --> T[tag]
15
+
16
+ A --> A0[analyze]
17
+ A --> A1[preview]
18
+ A --> A2[apply]
19
+ A --> A3[group]
20
+
21
+ M --> M0[sync]
22
+ ```
9
23
 
10
24
  ---
11
25
 
12
- ## Requirements & Install
26
+ ## Requirements
27
+
28
+ - Node.js `>=22.0.0`
29
+ - pnpm (recommended)
30
+
31
+ ---
13
32
 
14
- **Node.js ≥ 24** is required.
33
+ ## Installation
15
34
 
16
35
  ```bash
17
- # Global
36
+ # Install globally
18
37
  pnpm add -g @codefast/cli
19
38
 
20
- # Without a global install
21
- pnpm dlx @codefast/cli -- --help
39
+ # Or run without installing
40
+ pnpm dlx @codefast/cli --help
41
+ ```
42
+
43
+ ---
44
+
45
+ ## Quick start
46
+
47
+ ```bash
48
+ # 1. Preview proposed changes — no files written
49
+ codefast arrange preview packages/ui/src/components
50
+
51
+ # 2. Apply after reviewing the diff
52
+ codefast arrange apply packages/ui/src/components
53
+
54
+ # 3. Regenerate package exports from built dist/
55
+ codefast mirror sync
56
+
57
+ # 4. Add @since tags to exported APIs under src/
58
+ codefast tag
22
59
  ```
23
60
 
24
61
  ---
25
62
 
26
63
  ## `arrange`
27
64
 
28
- ### Recommended workflow
65
+ Reads `cn()` and `tv()` call sites, classifies each Tailwind utility, and rewrites the class strings in render-pipeline order (see [Grouping philosophy](#grouping-philosophy--render-pipeline-order) below).
66
+
67
+ ### Workflow
29
68
 
30
- 1. **`analyze [target]`** — Prints a report (long strings, nested `cn` in `tv`, related notes). No files changed.
31
- 2. **`preview [target]`** — Same transforms as `apply`, but writes nothing. Inspect stdout before touching the tree.
32
- 3. **`apply [target]`** — Writes edits. Run `preview` first.
69
+ Run the three subcommands in order:
33
70
 
34
- **Default target** (when path is omitted): `packages/ui/src/components` resolved from `process.cwd()`.
71
+ | Step | Command | Effect |
72
+ | ---- | ----------------------------------- | ------------------------------------- |
73
+ | 1 | `codefast arrange analyze [target]` | Report only — no files changed |
74
+ | 2 | `codefast arrange preview [target]` | Show exactly what `apply` would write |
75
+ | 3 | `codefast arrange apply [target]` | Write the changes |
35
76
 
36
- ### Useful options
77
+ The default `target` when omitted is `packages/ui/src/components`, resolved from `process.cwd()`.
37
78
 
38
- - **`--with-class-name`** — Append `className` as the last argument to the suggested `cn(...)`.
39
- - **`--cn-import <spec>`** — Override the module specifier when the tool adds a `cn` import.
79
+ ### Flags
40
80
 
41
- ### `group [tokens...]`
81
+ | Flag | Description |
82
+ | -------------------- | ----------------------------------------------------------------------- |
83
+ | `--with-class-name` | Append `className` as the last argument when rewriting a `cn(...)` call |
84
+ | `--cn-import <spec>` | Override the module specifier used when adding a missing `cn` import |
42
85
 
43
- No filesystem involved — paste a class string, get back a suggested `cn(...)` (or a `tv()`-style array with `--tv`) plus a short buckets summary. Use this to tune your mental model before running `analyze` on a large tree.
86
+ ### `arrange group` — one-shot string grouping
87
+
88
+ Groups a single class string without touching the filesystem. Useful for checking how a string would be classified before running `apply`:
89
+
90
+ ```bash
91
+ codefast arrange group "relative flex items-center h-10 w-full rounded-md bg-primary text-white hover:bg-primary/90"
92
+ ```
44
93
 
45
94
  ---
46
95
 
47
- ## Grouping philosophy — Render Pipeline Order
96
+ ## `mirror sync`
48
97
 
49
- `arrange` does **not** sort alphabetically. It groups utilities in roughly the same order the browser reasons about them:
98
+ Scans built `dist/` trees and regenerates the `exports` field in each `package.json`. Run from anywhere inside the monorepo — the workspace root is discovered automatically via `pnpm-workspace.yaml`.
50
99
 
51
- **Existence → Position → Layout → Sizing → Spacing → Shape → Background → Shadow → Typography → Composite → Motion → Starting → Behavior → Conditions (State)**
100
+ ```bash
101
+ codefast mirror sync # all packages in the workspace
102
+ codefast mirror sync packages/ui # a single package path
103
+ codefast mirror sync -v # verbose output
104
+ ```
52
105
 
53
- Bucket breakdown:
106
+ > **Note:** Packages must be built first so `dist/` exists. Run your build step before `mirror sync`.
107
+
108
+ ### Configuration
109
+
110
+ Create a `codefast.config.js` (or `.mjs`, `.cjs`, `.json`) at the repo root with a `mirror` key:
111
+
112
+ ```js
113
+ // codefast.config.mjs
114
+ export default {
115
+ mirror: {
116
+ skipPackages: ["@acme/internal"],
117
+ pathTransformations: {
118
+ "@acme/ui": {
119
+ removePrefix: "./components/",
120
+ },
121
+ },
122
+ customExports: {
123
+ "@acme/ui": {
124
+ "./css/*": "./src/styles/*",
125
+ },
126
+ },
127
+ cssExports: {
128
+ "@acme/ui": {
129
+ enabled: true,
130
+ customExports: {
131
+ "./tokens.css": "./dist/tokens.css",
132
+ },
133
+ },
134
+ },
135
+ },
136
+ };
137
+ ```
54
138
 
55
- | Bucket | What it covers | Examples |
56
- | -------------- | ------------------------------------------------- | ------------------------------------------------- |
57
- | **Existence** | Display / containment context | `hidden`, `block`, `@container`, `group`, `peer` |
58
- | **Position** | Where the box sits | `absolute`, `inset-*`, `top-*`, `z-*` |
59
- | **Layout** | How children flow | `flex`, `grid`, `gap-*`, `items-*` |
60
- | **Sizing** | Box dimensions and overflow | `w-*`, `h-*`, `aspect-*`, `overflow-*` |
61
- | **Spacing** | Padding and margin only (gaps stay with Layout) | `p-*`, `m-*` |
62
- | **Shape** | Corners and strokes | `rounded-*`, `border-*`, `ring-*` |
63
- | **Background** | Surfaces and masks | `bg-*`, `from-*`, `via-*`, `to-*`, `mask-*` |
64
- | **Shadow** | Depth | `shadow-*`, `inset-shadow-*`, `text-shadow-*` |
65
- | **Typography** | Text appearance | `font-*`, `text-*`, `leading-*` |
66
- | **Composite** | Layers and transforms — 3D context → 3D → 2D | `opacity-*`, `rotate-x-*`, `translate-*` |
67
- | **Motion** | Time-based change | `transition-*`, `animate-*` |
68
- | **Starting** | Tailwind's `starting:` layer, kept next to Motion | `starting:*` |
69
- | **Behavior** | Input / scrolling / chrome | `cursor-*`, `scroll-*`, `field-sizing-*`, `inert` |
70
- | **State** | Everything with a variant stack | `hover:`, `md:`, `@md/sidebar:`, `data-[…]:` |
139
+ Use your real package names from `package.json#name` (for example `@acme/ui`) and adjust entries to match your workspace.
71
140
 
72
- Some adjacent buckets may be merged into one string literal when declared _compatible_ (e.g. `layout` + `sizing`) — keeps `cn()` readable without flattening unrelated concerns.
141
+ > **Migration:** Path-based keys (for example `packages/ui`) are deprecated for `pathTransformations`, `customExports`, `cssExports`, and `skipPackages`. Migrate to package-name keys.
73
142
 
74
- To change a placement, edit `classifyBareUtility` in `src/lib/arrange/tokenizer.ts` and add a `classifyToken` test in `src/lib/arrange.test.ts`.
143
+ > **Security note:** `.js`, `.mjs`, and `.cjs` config files are loaded via `import()`. Only run `mirror sync` in repositories you trust.
75
144
 
76
145
  ---
77
146
 
78
- ## `mirror sync`
147
+ ## Lifecycle hooks (`codefast.config.mjs`)
148
+
149
+ `codefast` supports lifecycle hooks so teams can plug in their own post-write workflow (formatter, lint-fix, codemods) without hardcoding any formatter inside CLI core.
150
+
151
+ ```javascript
152
+ import { execSync } from "node:child_process";
153
+
154
+ export default {
155
+ tag: {
156
+ onAfterWrite: ({ files }) => {
157
+ console.log(`Formatting ${files.length} files with Oxc...`);
158
+ execSync(`npx oxc format ${files.join(" ")}`, { stdio: "inherit" });
159
+ },
160
+ },
161
+ arrange: {
162
+ onAfterWrite: ({ files }) => {
163
+ execSync(`npx oxc format ${files.join(" ")}`, { stdio: "inherit" });
164
+ },
165
+ },
166
+ };
167
+ ```
168
+
169
+ Hook contract:
170
+
171
+ - `tag.onAfterWrite?.({ files })` runs after `codefast tag` writes files.
172
+ - `arrange.onAfterWrite?.({ files })` runs after `codefast arrange apply` writes files.
173
+ - Hooks support both sync and async functions (`void | Promise<void>`).
174
+ - Hook errors are logged but do not crash the CLI process.
175
+
176
+ ---
177
+
178
+ ## `tag` / `annotate`
79
179
 
80
- Run from anywhere under the monorepo — the CLI finds the root via `pnpm-workspace.yaml`.
180
+ Scans `.ts` / `.tsx` source files and annotates exported declarations with `@since <current-package-version>`. This keeps API evolution visible and reduces documentation drift in long-lived codebases.
81
181
 
82
182
  ```bash
83
- codefast mirror sync # All workspace packages
84
- codefast mirror sync packages/ui # One package only
85
- codefast mirror sync -v # Verbose
183
+ codefast tag # annotate exports in ./src
184
+ codefast tag packages/ui/src # annotate a custom target
185
+ codefast annotate --dry-run # preview only, do not write files
86
186
  ```
87
187
 
88
- **Config:** Place `codefast.config.js` (or `.mjs` / `.cjs` / `.json`) at repo root with a `mirror` object (`skipPackages`, `pathTransformations`, `customExports`, …).
188
+ What it updates:
89
189
 
90
- > ⚠️ `.js`/`.mjs`/`.cjs` config files are loaded via `import()` — only run `mirror sync` in repositories you trust.
190
+ - Adds `/** @since <version> */` when an exported declaration has no JSDoc.
191
+ - Injects `@since <version>` into an existing JSDoc block when missing.
192
+ - Leaves declarations unchanged when `@since` is already present.
193
+
194
+ The `<version>` value is read from the nearest `package.json` found by walking up from the target path.
91
195
 
92
196
  ---
93
197
 
94
- ## Developing inside this monorepo
198
+ ## Grouping philosophy — Render Pipeline Order
199
+
200
+ `arrange` does **not** sort classes alphabetically. Instead, it groups utilities in roughly the order the browser applies them — from the box's existence, through its shape and surface, to interactive behavior. This makes class strings easier to scan and reason about at a glance.
201
+
202
+ **Existence → Position → Layout → Sizing → Spacing → Shape → Background → Shadow → Typography → Composite → Motion → Starting → Behavior → State → Selector**
203
+
204
+ | Bucket | What it covers | Examples |
205
+ | -------------- | --------------------------------------------------- | ------------------------------------------------- |
206
+ | **Existence** | Display and containment context | `hidden`, `block`, `@container`, `group`, `peer` |
207
+ | **Position** | Where the box sits | `absolute`, `inset-*`, `top-*`, `z-*` |
208
+ | **Layout** | How children flow | `flex`, `grid`, `gap-*`, `items-*` |
209
+ | **Sizing** | Box dimensions and overflow | `w-*`, `h-*`, `aspect-*`, `overflow-*` |
210
+ | **Spacing** | Padding and margin only (gaps stay with Layout) | `p-*`, `m-*` |
211
+ | **Shape** | Corners and strokes | `rounded-*`, `border-*`, `ring-*` |
212
+ | **Background** | Surfaces and masks | `bg-*`, `from-*`, `via-*`, `to-*`, `mask-*` |
213
+ | **Shadow** | Depth | `shadow-*`, `inset-shadow-*`, `text-shadow-*` |
214
+ | **Typography** | Text appearance | `font-*`, `text-*`, `leading-*` |
215
+ | **Composite** | Layers and transforms (3D context → 3D → 2D) | `opacity-*`, `rotate-x-*`, `translate-*` |
216
+ | **Motion** | Time-based change | `transition-*`, `animate-*` |
217
+ | **Starting** | Tailwind's `starting:` layer — kept next to Motion | `starting:*` |
218
+ | **Behavior** | Input, scrolling, and browser chrome | `cursor-*`, `scroll-*`, `field-sizing-*`, `inert` |
219
+ | **State** | Interactive and conditional variants (non-selector) | `hover:`, `md:`, `@md/sidebar:`, `data-[…]:` |
220
+ | **Selector** | Selector-driven variants | `[&…]:`, `*:`, `**:`, `has-*`, `group-[…]:` |
221
+
222
+ Adjacent buckets may be merged into one string literal when declared _compatible_ (e.g. `layout` + `sizing`). This keeps `cn()` calls readable without flattening unrelated concerns into a single undifferentiated blob.
223
+
224
+ To change a placement, edit `classifyBareUtility` in `src/lib/arrange/tokenizer.ts` and add a corresponding `classifyToken` test in `src/lib/arrange.test.ts`.
225
+
226
+ ---
227
+
228
+ ## Troubleshooting
229
+
230
+ **`codefast: command not found`**
231
+ Install globally with `pnpm add -g @codefast/cli`, or run via `pnpm dlx @codefast/cli <command>`.
232
+
233
+ **`mirror sync` writes little or no output**
234
+ Packages must be built before syncing. Ensure `dist/` exists by running your build step first, then re-run `codefast mirror sync`.
235
+
236
+ **Unexpected class reorder after `arrange apply`**
237
+ Run `arrange preview` before applying and smoke-test the UI. Some components rely on cascade-sensitive ordering that `arrange` cannot detect automatically.
238
+
239
+ ---
240
+
241
+ ## Contributing (monorepo setup)
95
242
 
96
243
  ```bash
244
+ # Build the local CLI (produces dist/bin.js)
245
+ pnpm --filter @codefast/cli build
246
+
247
+ # Run the local entrypoint
97
248
  pnpm exec codefast --help
98
249
  ```
99
250
 
100
- Root `package.json` defines optional `cli:*` scripts (e.g. `cli:mirror-sync`, `cli:arrange-analyze`) as thin wrappers around `pnpm exec codefast …`.
251
+ A few naming conventions to keep in mind:
252
+
253
+ - **`codefast <command>`** refers to CLI commands exposed via the `@codefast/cli` `bin` entry.
254
+ - **Scripts in `packages/cli/package.json`** (`build`, `test`, …) are package-local dev scripts, not CLI commands.
255
+ - The root `package.json` includes optional convenience wrappers such as `cli:mirror-sync` and `cli:arrange-analyze` for common dev workflows.
@@ -0,0 +1,92 @@
1
+ import "./constants-AtPhF-Ct.mjs";
2
+ import { d as tokenizeClassString } from "./tokenizer-CeZx73Zu.mjs";
3
+ import { r as forEachStringLiteralInClassExpression } from "./collectors-cn-BS8TTQ8O.mjs";
4
+ import { t as jsxClassNameStaticLiteral } from "./collectors-jsx-D9tL2ZPM.mjs";
5
+ import { a as isCnOrTvIdentifier, o as lineOf, r as buildKnownCnTvBindings } from "./ast-helpers-MDRYuIAk.mjs";
6
+ import { c as traverseTvObject, t as collectCnCallsInsideTv } from "./collectors-tv-D2PXZaRt.mjs";
7
+ import { n as walkTsxFiles } from "./walk-DjgQn66n.mjs";
8
+ import ts from "typescript";
9
+ //#region src/lib/arrange/application/analyze.ts
10
+ function analyzeCnCall(sf, call, report) {
11
+ for (const arg of call.arguments) forEachStringLiteralInClassExpression(arg, (lit) => {
12
+ const text = lit.text;
13
+ const tokenCount = tokenizeClassString(text).length;
14
+ if (tokenCount >= 18) report.longCnStringLiterals.push({
15
+ file: sf.fileName,
16
+ line: lineOf(sf, lit),
17
+ tokenCount,
18
+ preview: text.length > 72 ? `${text.slice(0, 72)}…` : text
19
+ });
20
+ });
21
+ }
22
+ function visitCallExpressionForArrangeAnalyze(callExpression, sf, sourceText, knownBindings, report) {
23
+ if (isCnOrTvIdentifier(callExpression.expression, "cn", knownBindings)) {
24
+ report.cnCallExpressions++;
25
+ analyzeCnCall(sf, callExpression, report);
26
+ return;
27
+ }
28
+ if (!isCnOrTvIdentifier(callExpression.expression, "tv", knownBindings)) return;
29
+ report.tvCallExpressions++;
30
+ const arg0 = callExpression.arguments[0];
31
+ if (!arg0 || !ts.isObjectLiteralExpression(arg0)) return;
32
+ for (const nestedCn of collectCnCallsInsideTv(sf, arg0, knownBindings, 0)) {
33
+ const src = sourceText.slice(nestedCn.getStart(sf), nestedCn.getEnd());
34
+ const preview = src.length > 72 ? `${src.slice(0, 72)}…` : src;
35
+ report.cnInsideTvCalls.push({
36
+ file: sf.fileName,
37
+ line: lineOf(sf, nestedCn),
38
+ argCount: nestedCn.arguments.length,
39
+ preview
40
+ });
41
+ }
42
+ traverseTvObject(sf, arg0, (classLiteral) => {
43
+ const text = classLiteral.text;
44
+ const tokenCount = tokenizeClassString(text).length;
45
+ if (tokenCount >= 18) report.longTvStringLiterals.push({
46
+ file: sf.fileName,
47
+ line: lineOf(sf, classLiteral),
48
+ tokenCount,
49
+ preview: text.length > 72 ? `${text.slice(0, 72)}…` : text
50
+ });
51
+ }, 0, knownBindings);
52
+ }
53
+ function visitJsxAttributeForArrangeAnalyze(jsxClassAttribute, sf, filePath, report) {
54
+ if (!filePath.endsWith(".tsx")) return;
55
+ const parsed = jsxClassNameStaticLiteral(jsxClassAttribute);
56
+ if (!parsed) return;
57
+ const text = parsed.lit.text;
58
+ const tokenCount = tokenizeClassString(text).length;
59
+ if (tokenCount >= 18) report.longJsxClassNameLiterals.push({
60
+ file: sf.fileName,
61
+ line: lineOf(sf, parsed.lit),
62
+ tokenCount,
63
+ preview: text.length > 72 ? `${text.slice(0, 72)}…` : text
64
+ });
65
+ }
66
+ function analyzeDirectory(analyzeRootPath, fs) {
67
+ const report = {
68
+ files: 0,
69
+ cnCallExpressions: 0,
70
+ tvCallExpressions: 0,
71
+ cnInsideTvCalls: [],
72
+ longCnStringLiterals: [],
73
+ longTvStringLiterals: [],
74
+ longJsxClassNameLiterals: []
75
+ };
76
+ const files = fs.statSync(analyzeRootPath).isDirectory() ? walkTsxFiles(analyzeRootPath, fs) : [analyzeRootPath];
77
+ for (const filePath of files) {
78
+ const sourceText = fs.readFileSync(filePath, "utf8");
79
+ const sf = ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true, filePath.endsWith(".tsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
80
+ report.files++;
81
+ const knownBindings = buildKnownCnTvBindings(sf);
82
+ const visitTypeScriptSubtree = (tsNode) => {
83
+ if (ts.isCallExpression(tsNode)) visitCallExpressionForArrangeAnalyze(tsNode, sf, sourceText, knownBindings, report);
84
+ if (ts.isJsxAttribute(tsNode)) visitJsxAttributeForArrangeAnalyze(tsNode, sf, filePath, report);
85
+ ts.forEachChild(tsNode, visitTypeScriptSubtree);
86
+ };
87
+ visitTypeScriptSubtree(sf);
88
+ }
89
+ return report;
90
+ }
91
+ //#endregion
92
+ export { analyzeDirectory as t };
@@ -0,0 +1,107 @@
1
+ import { t as messageFromCaughtUnknown } from "./caught-unknown-message-CRhkDWO7.mjs";
2
+ import { t as loadConfig } from "./loader-DpN-zl5g.mjs";
3
+ import "./config-DVia_f9a.mjs";
4
+ import { t as printConfigSchemaWarnings } from "./config-reporter-SCcWDZc1.mjs";
5
+ import { i as DEFAULT_ARRANGE_TARGET } from "./constants-AtPhF-Ct.mjs";
6
+ import { t as analyzeDirectory } from "./analyze-DvtJ3j9m.mjs";
7
+ import { t as printAnalyzeReport } from "./report-DYxvw34T.mjs";
8
+ import { a as suggestCnGroups, o as summarizeGroupBucketLabels } from "./grouping-c1grpCQT.mjs";
9
+ import { i as formatCnCall, n as formatArray } from "./formatters-DGE8jJ53.mjs";
10
+ import { t as runArrangeSync } from "./run-target-CJWrqNT4.mjs";
11
+ import { n as createNodeCliLogger, t as createNodeCliFs } from "./node-io-DFRbHO6m.mjs";
12
+ import "./arrange-BqP2DI3w.mjs";
13
+ import { t as findRepoRoot } from "./repo-root-HmtIEm6J.mjs";
14
+ import process from "node:process";
15
+ import path from "node:path";
16
+ import { Option } from "commander";
17
+ //#region src/commands/arrange.ts
18
+ /** Commander attribute `withClassName` (second long flag `--with-class-name`). */
19
+ function createWithClassNameOption() {
20
+ return new Option("--with-classname, --with-class-name", "Append className as final cn() argument").default(false);
21
+ }
22
+ function defaultTargetPath() {
23
+ return path.resolve(process.cwd(), DEFAULT_ARRANGE_TARGET);
24
+ }
25
+ function checkTargetExists(resolved, fs, logger) {
26
+ if (!fs.existsSync(resolved)) {
27
+ logger.err(`Not found: ${resolved}`);
28
+ process.exitCode = 1;
29
+ return false;
30
+ }
31
+ return true;
32
+ }
33
+ async function loadArrangeCommandConfig(fs, logger, rootDir) {
34
+ try {
35
+ const { config, warnings } = await loadConfig(fs, rootDir);
36
+ printConfigSchemaWarnings(logger, warnings);
37
+ return { arrangeConfig: config.arrange ?? {} };
38
+ } catch (caughtConfigError) {
39
+ logger.err(messageFromCaughtUnknown(caughtConfigError));
40
+ process.exitCode = 1;
41
+ return;
42
+ }
43
+ }
44
+ function registerArrangeCommand(program) {
45
+ const arrange = program.command("arrange").description("Analyze and regroup Tailwind classes in cn() / tv() calls (Tailwind v4)");
46
+ arrange.command("analyze").description("Report long strings, nested cn in tv(), and related findings").argument("[target]", "Directory or file (default: packages/ui/src/components)").action(async (target) => {
47
+ const fs = createNodeCliFs();
48
+ const logger = createNodeCliLogger();
49
+ const resolved = target ? path.resolve(target) : defaultTargetPath();
50
+ if (!checkTargetExists(resolved, fs, logger)) return;
51
+ if (!await loadArrangeCommandConfig(fs, logger, findRepoRoot(fs))) return;
52
+ printAnalyzeReport(resolved, analyzeDirectory(resolved, fs), logger);
53
+ });
54
+ arrange.command("preview").description("Dry-run: print suggested replacements without writing files").argument("[target]", "Directory or file (default: packages/ui/src/components)").addOption(createWithClassNameOption()).option("--cn-import <spec>", "Override module specifier when adding cn import").action(async (target, opts) => {
55
+ const fs = createNodeCliFs();
56
+ const logger = createNodeCliLogger();
57
+ const resolved = target ? path.resolve(target) : defaultTargetPath();
58
+ if (!checkTargetExists(resolved, fs, logger)) return;
59
+ const rootDir = findRepoRoot(fs);
60
+ const loaded = await loadArrangeCommandConfig(fs, logger, rootDir);
61
+ if (!loaded) return;
62
+ process.exitCode = await runArrangeSync({
63
+ rootDir,
64
+ config: loaded.arrangeConfig,
65
+ targetPath: resolved,
66
+ write: false,
67
+ withClassName: opts.withClassName,
68
+ cnImport: opts.cnImport,
69
+ fs,
70
+ logger
71
+ });
72
+ });
73
+ arrange.command("apply").description("Apply grouping and cn-in-tv unwrap edits to files").argument("[target]", "Directory or file (default: packages/ui/src/components)").addOption(createWithClassNameOption()).option("--cn-import <spec>", "Override module specifier when adding cn import").action(async (target, opts) => {
74
+ const fs = createNodeCliFs();
75
+ const logger = createNodeCliLogger();
76
+ const resolved = target ? path.resolve(target) : defaultTargetPath();
77
+ if (!checkTargetExists(resolved, fs, logger)) return;
78
+ const rootDir = findRepoRoot(fs);
79
+ const loaded = await loadArrangeCommandConfig(fs, logger, rootDir);
80
+ if (!loaded) return;
81
+ process.exitCode = await runArrangeSync({
82
+ rootDir,
83
+ config: loaded.arrangeConfig,
84
+ targetPath: resolved,
85
+ write: true,
86
+ withClassName: opts.withClassName,
87
+ cnImport: opts.cnImport,
88
+ fs,
89
+ logger
90
+ });
91
+ });
92
+ arrange.command("group").description("Try grouping on a pasted class string (stdout: cn(...) or tv array with --tv)").argument("[tokens...]", "Class tokens (quote a single string if it contains spaces)").option("--tv", "Emit tv()-style array instead of cn() call", false).addOption(createWithClassNameOption()).action((tokens, opts) => {
93
+ const inlineClasses = tokens.join(" ").trim();
94
+ if (!inlineClasses) {
95
+ process.stderr.write("Pass a class string. Example: codefast arrange group \"flex gap-2 text-sm rounded-md\"\n");
96
+ process.exitCode = 1;
97
+ return;
98
+ }
99
+ const groups = suggestCnGroups(inlineClasses);
100
+ const result = opts.tv ? formatArray(groups) : formatCnCall(groups, { trailingClassName: !!opts.withClassName });
101
+ process.stdout.write(`${result}\n`);
102
+ const bucketSummary = summarizeGroupBucketLabels(groups);
103
+ process.stdout.write(`\n// Buckets: ${JSON.stringify(bucketSummary)}\n`);
104
+ });
105
+ }
106
+ //#endregion
107
+ export { registerArrangeCommand as t };
@@ -0,0 +1,8 @@
1
+ import "./collectors-cn-BS8TTQ8O.mjs";
2
+ import "./ast-helpers-MDRYuIAk.mjs";
3
+ import "./analyze-DvtJ3j9m.mjs";
4
+ import "./formatters-DGE8jJ53.mjs";
5
+ import "./imports-DSVq1xhB.mjs";
6
+ import "./group-file-ClW7XdD6.mjs";
7
+ import "./run-target-CJWrqNT4.mjs";
8
+ export {};
@@ -0,0 +1,115 @@
1
+ import { a as EMPTY_CN_TV_BINDINGS } from "./constants-AtPhF-Ct.mjs";
2
+ import ts from "typescript";
3
+ //#region src/lib/arrange/domain/ast/ast-helpers.ts
4
+ /** Known module specifiers that export `cn` / `tv`. */
5
+ const KNOWN_CN_TV_MODULES = new Set([
6
+ "@codefast/tailwind-variants",
7
+ "clsx",
8
+ "class-variance-authority",
9
+ "tailwind-variants",
10
+ "#lib/utils",
11
+ "~/lib/utils",
12
+ "@/lib/utils"
13
+ ]);
14
+ /**
15
+ * True for typical shadcn-style re-exports: `./utils`, `@/lib/utils`, `…/utils/…`,
16
+ * or a dedicated `cn.ts` / `cn.tsx` module.
17
+ */
18
+ function moduleLooksLikeCnTvReexport(moduleSpecifier) {
19
+ const norm = moduleSpecifier.replace(/\\/g, "/");
20
+ if (/(?:^|[./])utils(?:\/|$)/.test(norm)) return true;
21
+ if (/\/utils\//.test(norm) || /\/utils$/.test(norm)) return true;
22
+ if (/(?:^|\/)cn\.tsx?$/.test(norm)) return true;
23
+ return false;
24
+ }
25
+ /**
26
+ * Build a set of local binding names that are imported from a known cn/tv
27
+ * module in `sf`.
28
+ */
29
+ function buildKnownCnTvBindings(sf) {
30
+ const bindings = /* @__PURE__ */ new Set();
31
+ for (const stmt of sf.statements) {
32
+ if (!ts.isImportDeclaration(stmt) || !stmt.importClause) continue;
33
+ if (stmt.importClause.isTypeOnly) continue;
34
+ const spec = stmt.moduleSpecifier;
35
+ if (!ts.isStringLiteral(spec)) continue;
36
+ const moduleSpecifier = spec.text;
37
+ if (!KNOWN_CN_TV_MODULES.has(moduleSpecifier) && !moduleLooksLikeCnTvReexport(moduleSpecifier)) continue;
38
+ const clause = stmt.importClause;
39
+ if (clause.name) bindings.add(clause.name.text);
40
+ if (!clause.namedBindings) continue;
41
+ if (ts.isNamedImports(clause.namedBindings)) {
42
+ for (const namedImport of clause.namedBindings.elements) bindings.add(namedImport.name.text);
43
+ continue;
44
+ }
45
+ bindings.add(clause.namedBindings.name.text);
46
+ }
47
+ return bindings;
48
+ }
49
+ /**
50
+ * Returns true when `expr` is an identifier whose name matches `name` AND
51
+ * that name is listed in `knownBindings` from a recognized import.
52
+ */
53
+ function isCnOrTvIdentifier(expr, name, knownBindings = EMPTY_CN_TV_BINDINGS) {
54
+ if (knownBindings.size === 0) return false;
55
+ if (ts.isIdentifier(expr) && expr.text === name) return knownBindings.has(expr.text);
56
+ if (ts.isPropertyAccessExpression(expr) && expr.name.text === name) return ts.isIdentifier(expr.expression) && knownBindings.has(expr.expression.text);
57
+ return false;
58
+ }
59
+ function propertyAssignmentNameText(prop) {
60
+ if (ts.isIdentifier(prop.name)) return prop.name.text;
61
+ if (ts.isStringLiteral(prop.name)) return prop.name.text;
62
+ }
63
+ function lineOf(sf, tsNode) {
64
+ return sf.getLineAndCharacterOfPosition(tsNode.getStart(sf)).line + 1;
65
+ }
66
+ /**
67
+ * Returns only the leading whitespace (tabs / spaces) of the line containing
68
+ * `pos`.
69
+ */
70
+ function indentOfLineContaining(source, pos) {
71
+ const searchPos = Math.max(0, Math.min(pos, source.length));
72
+ const prevLineBreak = Math.max(source.lastIndexOf("\n", searchPos - 1), source.lastIndexOf("\r", searchPos - 1));
73
+ const lineStart = prevLineBreak === -1 ? 0 : prevLineBreak + 1;
74
+ let lineEnd = source.length;
75
+ for (let i = lineStart; i < source.length; i++) {
76
+ const ch = source[i];
77
+ if (ch === "\n" || ch === "\r") {
78
+ lineEnd = i;
79
+ break;
80
+ }
81
+ }
82
+ const line = source.slice(lineStart, lineEnd);
83
+ return /^[\t ]*/.exec(line)?.[0] ?? "";
84
+ }
85
+ function applyEditsDescending(sourceText, edits) {
86
+ const sorted = [...edits].sort((editA, editB) => editB.start - editA.start);
87
+ let out = sourceText;
88
+ for (const edit of sorted) out = out.slice(0, edit.start) + edit.replacement + out.slice(edit.end);
89
+ return out;
90
+ }
91
+ /**
92
+ * Replace `cn(...)` nested in `tv({...})` with a plain string (one arg) or a string
93
+ * array (multiple args). Returns undefined when there are zero arguments.
94
+ */
95
+ function unwrapCnInsideTvCallReplacement(call, sourceText, sf) {
96
+ const args = call.arguments;
97
+ if (args.length === 0) return void 0;
98
+ const baseIndent = indentOfLineContaining(sourceText, call.getStart(sf));
99
+ const innerIndent = `${baseIndent} `;
100
+ if (args.length === 1) {
101
+ const firstArg = args[0];
102
+ return sourceText.slice(firstArg.getStart(sf), firstArg.getEnd());
103
+ }
104
+ const lines = ["["];
105
+ for (let i = 0; i < args.length; i++) {
106
+ const arg = args[i];
107
+ const piece = sourceText.slice(arg.getStart(sf), arg.getEnd());
108
+ const comma = i < args.length - 1 || args.length > 1 ? "," : "";
109
+ lines.push(`${innerIndent}${piece}${comma}`);
110
+ }
111
+ lines.push(`${baseIndent}]`);
112
+ return lines.join("\n");
113
+ }
114
+ //#endregion
115
+ export { isCnOrTvIdentifier as a, propertyAssignmentNameText as c, indentOfLineContaining as i, unwrapCnInsideTvCallReplacement as l, applyEditsDescending as n, lineOf as o, buildKnownCnTvBindings as r, moduleLooksLikeCnTvReexport as s, KNOWN_CN_TV_MODULES as t };
@@ -1,5 +1,8 @@
1
1
  #!/usr/bin/env node
2
+ import { n as runCli } from "./program-DR-H4tH3.mjs";
2
3
  import process from "node:process";
3
- import { runCli } from "#program";
4
+ //#region src/bin.ts
4
5
  const code = await runCli(process.argv);
5
6
  process.exit(code);
7
+ //#endregion
8
+ export {};
@@ -0,0 +1,20 @@
1
+ //#region src/lib/infra/caught-unknown-message.ts
2
+ /**
3
+ * Stable, user-facing text for values caught as `unknown`.
4
+ * Avoids `String(unknown)` / `[object Object]` and satisfies strict `no-base-to-string`.
5
+ */
6
+ function messageFromCaughtUnknown(value) {
7
+ if (value instanceof Error) return value.message;
8
+ if (typeof value === "string") return value;
9
+ if (typeof value === "number" || typeof value === "bigint" || typeof value === "boolean") return String(value);
10
+ if (typeof value === "symbol") return String(value);
11
+ if (value === null) return "null";
12
+ if (value === void 0) return "undefined";
13
+ try {
14
+ const serialized = JSON.stringify(value);
15
+ if (typeof serialized === "string") return serialized;
16
+ } catch {}
17
+ return "[unserializable non-Error value]";
18
+ }
19
+ //#endregion
20
+ export { messageFromCaughtUnknown as t };