@codefast/cli 0.3.7-canary.0 → 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 (124) hide show
  1. package/README.md +178 -262
  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.mjs +8 -0
  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/domain/types.mjs +1 -0
  54. package/dist/lib/arrange/index.mjs +16 -0
  55. package/dist/lib/arrange/infra/walk.mjs +2 -0
  56. package/dist/lib/arrange/infra/walk.test.mjs +50 -0
  57. package/dist/lib/arrange/presentation/formatters.mjs +2 -0
  58. package/dist/lib/arrange/presentation/formatters.test.mjs +73 -0
  59. package/dist/lib/arrange/presentation/report.mjs +2 -0
  60. package/dist/lib/config/domain/schema.mjs +2 -0
  61. package/dist/lib/config/index.mjs +4 -0
  62. package/dist/lib/config/infra/loader.mjs +2 -0
  63. package/dist/lib/config/infra/loader.test.mjs +116 -0
  64. package/dist/lib/infra/caught-unknown-message.mjs +2 -0
  65. package/dist/lib/infra/config-reporter.mjs +2 -0
  66. package/dist/lib/infra/fs-contract.mjs +1 -0
  67. package/dist/lib/infra/node-io.mjs +2 -0
  68. package/dist/lib/infra/workspace/repo-root.mjs +2 -0
  69. package/dist/lib/infra/workspace/repo-root.test.mjs +45 -0
  70. package/dist/lib/mirror/application/engine.mjs +2 -0
  71. package/dist/lib/mirror/application/sync.mjs +2 -0
  72. package/dist/lib/mirror/application/sync.test.mjs +482 -0
  73. package/dist/lib/mirror/domain/constants.mjs +2 -0
  74. package/dist/lib/mirror/domain/errors.mjs +2 -0
  75. package/dist/lib/mirror/domain/types.mjs +1 -0
  76. package/dist/lib/mirror/index.mjs +3 -0
  77. package/dist/lib/mirror/infra/dirent-list.mjs +2 -0
  78. package/dist/lib/mirror/infra/dirent-list.test.mjs +18 -0
  79. package/dist/lib/mirror/infra/package-filter.mjs +2 -0
  80. package/dist/lib/mirror/infra/package-filter.test.mjs +32 -0
  81. package/dist/lib/mirror/infra/path-normalizer.mjs +2 -0
  82. package/dist/lib/mirror/infra/update-pkg.mjs +2 -0
  83. package/dist/lib/mirror/infra/workspace-packages.mjs +2 -0
  84. package/dist/lib/mirror/infra/workspace-packages.test.mjs +167 -0
  85. package/dist/lib/mirror/presentation/reporter.mjs +2 -0
  86. package/dist/lib/tag/application/engine.mjs +2 -0
  87. package/dist/lib/tag/application/engine.test.mjs +159 -0
  88. package/dist/lib/tag/domain/types.mjs +1 -0
  89. package/dist/lib/tag/index.mjs +5 -0
  90. package/dist/lib/tag/infra/target-resolver.mjs +2 -0
  91. package/dist/lib/tag/presentation/colors.mjs +2 -0
  92. package/dist/lib/tag/presentation/tag-presenter.mjs +2 -0
  93. package/dist/lib/tag/presentation/tag-presenter.test.mjs +29 -0
  94. package/dist/loader-DpN-zl5g.mjs +78 -0
  95. package/dist/mirror-CbGiLXKT.mjs +2 -0
  96. package/dist/mirror-DqJE3ejL.mjs +66 -0
  97. package/dist/node-io-DFRbHO6m.mjs +30 -0
  98. package/dist/package-filter-C_8ryqvz.mjs +26 -0
  99. package/dist/path-normalizer-D4ceBO3l.mjs +7 -0
  100. package/dist/program-DR-H4tH3.mjs +34 -0
  101. package/dist/program.mjs +2 -0
  102. package/dist/repo-root-HmtIEm6J.mjs +22 -0
  103. package/dist/report-DYxvw34T.mjs +24 -0
  104. package/dist/reporter-CwIe_tAM.mjs +108 -0
  105. package/dist/run-target-CJWrqNT4.mjs +65 -0
  106. package/dist/schema-Or8RAARd.mjs +25 -0
  107. package/dist/sync-e1Sp-shi.mjs +157 -0
  108. package/dist/tag-Cd9JujU5.mjs +2 -0
  109. package/dist/tag-hEKVZ_-7.mjs +58 -0
  110. package/dist/tag-presenter-Cs66UBsH.mjs +63 -0
  111. package/dist/target-resolver-CR3FXMTy.mjs +170 -0
  112. package/dist/targets-pCTslnFh.mjs +106 -0
  113. package/dist/tokenizer-CeZx73Zu.mjs +216 -0
  114. package/dist/update-pkg-CnGfjWBy.mjs +37 -0
  115. package/dist/walk-DjgQn66n.mjs +33 -0
  116. package/dist/workspace-packages-K2NYTHJq.mjs +159 -0
  117. package/package.json +42 -53
  118. package/CHANGELOG.md +0 -15
  119. package/dist/cjs/index.cjs +0 -2
  120. package/dist/cjs/src/index.d.ts +0 -3
  121. package/dist/cjs/src/index.d.ts.map +0 -1
  122. package/dist/esm/index.js +0 -2
  123. package/dist/esm/src/index.d.ts +0 -3
  124. package/dist/esm/src/index.d.ts.map +0 -1
package/README.md CHANGED
@@ -1,339 +1,255 @@
1
1
  # @codefast/cli
2
2
 
3
- Command line interface tools for CodeFast development, built with explicit architecture principles.
3
+ A focused CLI for two recurring maintenance tasks in monorepos:
4
4
 
5
- ## Overview
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.
6
8
 
7
- The CodeFast CLI is a TypeScript-based command-line tool designed to analyze and validate CodeFast projects. It provides powerful analysis capabilities for TypeScript projects and React component type checking, following explicit architecture guidelines for maintainability, testability, and scalability.
9
+ ```mermaid
10
+ flowchart LR
11
+ R[codefast]
12
+ R --> A[arrange]
13
+ R --> M[mirror]
14
+ R --> T[tag]
8
15
 
9
- ## Features
16
+ A --> A0[analyze]
17
+ A --> A1[preview]
18
+ A --> A2[apply]
19
+ A --> A3[group]
10
20
 
11
- - **TypeScript Project Analysis**: Analyze TypeScript codebases and generate detailed statistics
12
- - **React Component Type Checking**: Validate React component type correspondence across packages
13
- - **Explicit Architecture**: Clean, maintainable codebase following DDD and SOLID principles
14
- - **Dependency Injection**: Fully testable with InversifyJS container
15
- - **Rich CLI Experience**: Colorful output with chalk and comprehensive help system
16
-
17
- ## Installation
18
-
19
- ```bash
20
- # Install globally
21
- npm install -g @codefast/cli
22
-
23
- # Or use with npx
24
- npx @codefast/cli --help
21
+ M --> M0[sync]
25
22
  ```
26
23
 
27
- ## Usage
24
+ ---
28
25
 
29
- ### Available Commands
26
+ ## Requirements
30
27
 
31
- #### Hello Command
28
+ - Node.js `>=22.0.0`
29
+ - pnpm (recommended)
32
30
 
33
- ```bash
34
- codefast hello [options]
35
- codefast hello --name "Developer"
36
- ```
37
-
38
- Simple greeting command for testing CLI functionality.
39
-
40
- **Options:**
31
+ ---
41
32
 
42
- - `-n, --name <name>`: Name to greet (default: "World")
43
-
44
- #### Analyze Command
33
+ ## Installation
45
34
 
46
35
  ```bash
47
- codefast analyze [options]
48
- codefast analyze --pattern "src/**/*.ts" --config "./tsconfig.json"
49
- ```
50
-
51
- Analyze TypeScript project and generate statistics about classes, functions, and interfaces.
52
-
53
- **Options:**
54
-
55
- - `-p, --pattern <pattern>`: File pattern to analyze (default: "src/**/*.ts")
56
- - `-c, --config <path>`: Path to tsconfig.json file
57
-
58
- **Example Output:**
59
-
60
- ```text
61
- 🔍 Analyzing TypeScript project...
62
- ✅ Found 45 TypeScript files
63
- ⚠️ Loaded 45 source files for analysis
64
- 📊 Project Statistics:
65
- Classes: 12
66
- Functions: 89
67
- Interfaces: 23
68
- ```
69
-
70
- #### Check Component Types Command
36
+ # Install globally
37
+ pnpm add -g @codefast/cli
71
38
 
72
- ```bash
73
- codefast check-component-types [options]
74
- codefast check-component-types --packages-dir "packages"
39
+ # Or run without installing
40
+ pnpm dlx @codefast/cli --help
75
41
  ```
76
42
 
77
- Check React component type correspondence across packages in a monorepo.
43
+ ---
78
44
 
79
- **Options:**
45
+ ## Quick start
80
46
 
81
- - `-d, --packages-dir <dir>`: Packages directory to analyze (default: "packages")
82
-
83
- ## Architecture
47
+ ```bash
48
+ # 1. Preview proposed changes — no files written
49
+ codefast arrange preview packages/ui/src/components
84
50
 
85
- This CLI follows **Explicit Architecture** principles, ensuring clear separation of concerns, testability, and maintainability.
51
+ # 2. Apply after reviewing the diff
52
+ codefast arrange apply packages/ui/src/components
86
53
 
87
- ### Directory Structure
54
+ # 3. Regenerate package exports from built dist/
55
+ codefast mirror sync
88
56
 
89
- ```text
90
- src/
91
- ├── core/ # Core business logic
92
- │ └── application/ # Application layer
93
- │ ├── ports/ # Interface definitions
94
- │ │ ├── analysis/ # Analysis service interfaces
95
- │ │ ├── services/ # Service interfaces
96
- │ │ └── system/ # System service interfaces
97
- │ └── use-cases/ # Business workflows
98
- ├── infrastructure/ # Technical implementations
99
- │ └── adapters/ # Port implementations
100
- │ ├── analysis/ # Analysis service adapters
101
- │ ├── services/ # Service adapters
102
- │ └── system/ # System service adapters
103
- ├── commands/ # CLI command handling
104
- ├── di/ # Dependency injection
105
- │ └── modules/ # DI module configurations
106
- └── index.ts # CLI entry point
57
+ # 4. Add @since tags to exported APIs under src/
58
+ codefast tag
107
59
  ```
108
60
 
109
- ### Architectural Layers
110
-
111
- #### 1. Core/Application Layer
61
+ ---
112
62
 
113
- Contains business logic and defines interfaces (ports) for external dependencies.
63
+ ## `arrange`
114
64
 
115
- **Use Cases:**
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).
116
66
 
117
- - `AnalyzeProjectUseCase`: Orchestrates TypeScript project analysis
118
- - `CheckComponentTypesUseCase`: Handles React component type validation
119
- - `GreetUserUseCase`: Simple greeting functionality
67
+ ### Workflow
120
68
 
121
- **Ports (Interfaces):**
69
+ Run the three subcommands in order:
122
70
 
123
- - **Analysis Ports**: `TypeScriptAnalysisPort`, `ComponentAnalysisPort`
124
- - **Service Ports**: `LoggingServicePort`
125
- - **System Ports**: `FileSystemSystemPort`, `PathSystemPort`, `UrlSystemPort`
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 |
126
76
 
127
- #### 2. Infrastructure Layer
77
+ The default `target` when omitted is `packages/ui/src/components`, resolved from `process.cwd()`.
128
78
 
129
- Implements the ports using concrete technologies and external libraries.
79
+ ### Flags
130
80
 
131
- **Adapters:**
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 |
132
85
 
133
- - `TsMorphTypescriptAnalysisAdapter`: TypeScript analysis using ts-morph
134
- - `ReactComponentAnalysisAdapter`: React component analysis
135
- - `ChalkLoggingServiceAdapter`: Colored console logging with chalk
136
- - `FastGlobFileSystemSystemAdapter`: File system operations with fast-glob
137
- - `NodePathSystemAdapter`: Path operations using Node.js path module
138
- - `NodeUrlSystemAdapter`: URL operations using Node.js url module
86
+ ### `arrange group` — one-shot string grouping
139
87
 
140
- #### 3. Commands Layer
88
+ Groups a single class string without touching the filesystem. Useful for checking how a string would be classified before running `apply`:
141
89
 
142
- Handles CLI interface using the Commander.js framework.
143
-
144
- - `CommandHandler`: Main command orchestrator with dependency injection
145
-
146
- #### 4. Dependency Injection Layer
147
-
148
- Manages dependencies using InversifyJS container.
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
+ ```
149
93
 
150
- **Modules:**
94
+ ---
151
95
 
152
- - `infrastructureModule`: Binds infrastructure adapters
153
- - `applicationModule`: Binds use cases and application services
154
- - `commandsModule`: Binds command handlers
96
+ ## `mirror sync`
155
97
 
156
- ### Key Design Principles
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`.
157
99
 
158
- 1. **Dependency Inversion**: All dependencies flow inward toward the core business logic
159
- 2. **Interface Segregation**: Small, focused interfaces for each concern
160
- 3. **Single Responsibility**: Each class has one reason to change
161
- 4. **Testability**: All dependencies are injected and can be mocked
162
- 5. **Explicit Dependencies**: No hidden dependencies or global 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
+ ```
163
105
 
164
- ## Development
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
+ ```
165
138
 
166
- ### Prerequisites
139
+ Use your real package names from `package.json#name` (for example `@acme/ui`) and adjust entries to match your workspace.
167
140
 
168
- - Node.js 20.0.0+
169
- - pnpm 10.13.1+
141
+ > **Migration:** Path-based keys (for example `packages/ui`) are deprecated for `pathTransformations`, `customExports`, `cssExports`, and `skipPackages`. Migrate to package-name keys.
170
142
 
171
- ### Setup
143
+ > **Security note:** `.js`, `.mjs`, and `.cjs` config files are loaded via `import()`. Only run `mirror sync` in repositories you trust.
172
144
 
173
- ```bash
174
- # Install dependencies
175
- pnpm install
145
+ ---
176
146
 
177
- # Build the package
178
- pnpm build
147
+ ## Lifecycle hooks (`codefast.config.mjs`)
179
148
 
180
- # Run in development mode
181
- pnpm dev
182
- ```
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.
183
150
 
184
- ### Available Scripts
151
+ ```javascript
152
+ import { execSync } from "node:child_process";
185
153
 
186
- ```bash
187
- # Development
188
- pnpm dev # Build and watch for changes
189
- pnpm build # Build for production
190
- pnpm clean # Clean build artifacts
191
-
192
- # Code Quality
193
- pnpm lint # Run ESLint
194
- pnpm lint:fix # Fix ESLint issues
195
- pnpm type-check # TypeScript type checking
196
- pnpm format # Format code with Prettier
197
- pnpm format:check # Check code formatting
198
-
199
- # Testing
200
- pnpm test # Run tests
201
- pnpm test:watch # Run tests in watch mode
202
- pnpm test:coverage # Run tests with coverage
203
- pnpm test:coverage:ci # Run tests with CI coverage
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
+ };
204
167
  ```
205
168
 
206
- ### Testing Strategy
169
+ Hook contract:
207
170
 
208
- The CLI follows a comprehensive testing approach aligned with the explicit architecture:
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.
209
175
 
210
- #### Unit Testing
176
+ ---
211
177
 
212
- - **Domain/Application Layer**: Test use cases with mocked ports
213
- - **Infrastructure Layer**: Test adapters with real or mocked external dependencies
214
- - **Commands Layer**: Test command handlers with mocked use cases
178
+ ## `tag` / `annotate`
215
179
 
216
- #### Test Structure
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.
217
181
 
218
- ```text
219
- src/
220
- ├── core/application/use-cases/
221
- │ ├── analyze-project.use-case.ts
222
- │ └── analyze-project.use-case.test.ts
223
- ├── infrastructure/adapters/
224
- │ ├── ts-morph.typescript.analysis.adapter.ts
225
- │ └── ts-morph.typescript.analysis.adapter.test.ts
226
- └── commands/
227
- ├── command-handler.ts
228
- └── command-handler.test.ts
182
+ ```bash
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
229
186
  ```
230
187
 
231
- #### Testing Best Practices
232
-
233
- - Mock at the port boundaries (interfaces)
234
- - Use dependency injection for test isolation
235
- - Test business logic independently of technical details
236
- - Maintain high coverage for critical paths
237
-
238
- ### Adding New Features
188
+ What it updates:
239
189
 
240
- #### 1. Define the Port (Interface)
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.
241
193
 
242
- ```typescript
243
- // src/core/application/ports/new-feature.port.ts
244
- export interface NewFeaturePort {
245
- performAction(input: string): Promise<string>;
246
- }
247
- ```
248
-
249
- #### 2. Create the Use Case
250
-
251
- ```typescript
252
- // src/core/application/use-cases/new-feature.use-case.ts
253
- @injectable()
254
- export class NewFeatureUseCase {
255
- constructor(
256
- @inject(TYPES.NewFeaturePort)
257
- private readonly newFeatureService: NewFeaturePort,
258
- ) {}
259
-
260
- async execute(input: string): Promise<void> {
261
- const result = await this.newFeatureService.performAction(input);
262
- // Handle result...
263
- }
264
- }
265
- ```
194
+ The `<version>` value is read from the nearest `package.json` found by walking up from the target path.
266
195
 
267
- #### 3. Implement the Adapter
268
-
269
- ```typescript
270
- // src/infrastructure/adapters/new-feature.adapter.ts
271
- @injectable()
272
- export class NewFeatureAdapter implements NewFeaturePort {
273
- async performAction(input: string): Promise<string> {
274
- // Implementation using external library
275
- return `Processed: ${ input }`;
276
- }
277
- }
278
- ```
196
+ ---
279
197
 
280
- #### 4. Configure Dependency Injection
198
+ ## Grouping philosophy — Render Pipeline Order
281
199
 
282
- ```typescript
283
- // src/di/types.ts
284
- export const TYPES = {
285
- // ... existing types
286
- NewFeaturePort: Symbol.for('NewFeaturePort'),
287
- NewFeatureUseCase: Symbol.for('NewFeatureUseCase'),
288
- };
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.
289
201
 
290
- // src/di/modules/infrastructure.module.ts
291
- infrastructureModule.bind<NewFeaturePort>(TYPES.NewFeaturePort).to(NewFeatureAdapter);
202
+ **Existence → Position → Layout → Sizing → Spacing → Shape → Background → Shadow → Typography → Composite → Motion → Starting → Behavior → State → Selector**
292
203
 
293
- // src/di/modules/application.module.ts
294
- applicationModule.bind<NewFeatureUseCase>(TYPES.NewFeatureUseCase).to(NewFeatureUseCase);
295
- ```
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-[…]:` |
296
221
 
297
- #### 5. Add CLI Command
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.
298
223
 
299
- ```typescript
300
- // src/commands/command-handler.ts
301
- this.program.command('new-feature').description('Description of new feature').action(async (options) => {
302
- await this.newFeatureUseCase.execute(options.input);
303
- });
304
- ```
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`.
305
225
 
306
- ## Dependencies
226
+ ---
307
227
 
308
- ### Core Dependencies
228
+ ## Troubleshooting
309
229
 
310
- - **chalk**: Terminal styling and colors
311
- - **commander**: CLI framework and argument parsing
312
- - **fast-glob**: Fast file globbing for file system operations
313
- - **inversify**: Dependency injection container
314
- - **reflect-metadata**: Metadata reflection for decorators
315
- - **ts-morph**: TypeScript compiler API wrapper
316
- - **zod**: Schema validation and type safety
230
+ **`codefast: command not found`**
231
+ Install globally with `pnpm add -g @codefast/cli`, or run via `pnpm dlx @codefast/cli <command>`.
317
232
 
318
- ### Development Dependencies
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`.
319
235
 
320
- - **@rslib/core**: Modern build tool for libraries
321
- - **TypeScript**: Type checking and compilation
322
- - **Jest**: Testing framework
323
- - **ESLint**: Code linting
324
- - **Prettier**: Code formatting
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.
325
238
 
326
- ## Contributing
239
+ ---
327
240
 
328
- 1. Follow the explicit architecture guidelines
329
- 2. Write tests for new features
330
- 3. Ensure all quality checks pass (`pnpm lint`, `pnpm type-check`, `pnpm test`)
331
- 4. Update documentation for new commands or features
241
+ ## Contributing (monorepo setup)
332
242
 
333
- ## License
243
+ ```bash
244
+ # Build the local CLI (produces dist/bin.js)
245
+ pnpm --filter @codefast/cli build
334
246
 
335
- MIT License - see LICENSE file for details.
247
+ # Run the local entrypoint
248
+ pnpm exec codefast --help
249
+ ```
336
250
 
337
- ## Related
251
+ A few naming conventions to keep in mind:
338
252
 
339
- - [CodeFast UI Components](../ui/README.md)
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 };