@codefast/cli 0.3.7-canary.0 → 0.3.12

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 (120) hide show
  1. package/README.md +60 -299
  2. package/dist/bin.d.ts +3 -0
  3. package/dist/bin.d.ts.map +1 -0
  4. package/dist/bin.js +5 -0
  5. package/dist/commands/arrange.d.ts +3 -0
  6. package/dist/commands/arrange.d.ts.map +1 -0
  7. package/dist/commands/arrange.js +114 -0
  8. package/dist/commands/mirror.d.ts +4 -0
  9. package/dist/commands/mirror.d.ts.map +1 -0
  10. package/dist/commands/mirror.js +63 -0
  11. package/dist/lib/arrange/analyze.d.ts +4 -0
  12. package/dist/lib/arrange/analyze.d.ts.map +1 -0
  13. package/dist/lib/arrange/analyze.js +105 -0
  14. package/dist/lib/arrange/ast/collectors-cn.d.ts +10 -0
  15. package/dist/lib/arrange/ast/collectors-cn.d.ts.map +1 -0
  16. package/dist/lib/arrange/ast/collectors-cn.js +84 -0
  17. package/dist/lib/arrange/ast/collectors-jsx.d.ts +4 -0
  18. package/dist/lib/arrange/ast/collectors-jsx.d.ts.map +1 -0
  19. package/dist/lib/arrange/ast/collectors-jsx.js +18 -0
  20. package/dist/lib/arrange/ast/collectors-tv.d.ts +13 -0
  21. package/dist/lib/arrange/ast/collectors-tv.d.ts.map +1 -0
  22. package/dist/lib/arrange/ast/collectors-tv.js +273 -0
  23. package/dist/lib/arrange/ast/targets.d.ts +8 -0
  24. package/dist/lib/arrange/ast/targets.d.ts.map +1 -0
  25. package/dist/lib/arrange/ast/targets.js +143 -0
  26. package/dist/lib/arrange/ast/utils.d.ts +36 -0
  27. package/dist/lib/arrange/ast/utils.d.ts.map +1 -0
  28. package/dist/lib/arrange/ast/utils.js +138 -0
  29. package/dist/lib/arrange/constants.d.ts +68 -0
  30. package/dist/lib/arrange/constants.d.ts.map +1 -0
  31. package/dist/lib/arrange/constants.js +179 -0
  32. package/dist/lib/arrange/errors.d.ts +14 -0
  33. package/dist/lib/arrange/errors.d.ts.map +1 -0
  34. package/dist/lib/arrange/errors.js +16 -0
  35. package/dist/lib/arrange/formatters.d.ts +16 -0
  36. package/dist/lib/arrange/formatters.d.ts.map +1 -0
  37. package/dist/lib/arrange/formatters.js +57 -0
  38. package/dist/lib/arrange/group-file.d.ts +4 -0
  39. package/dist/lib/arrange/group-file.d.ts.map +1 -0
  40. package/dist/lib/arrange/group-file.js +115 -0
  41. package/dist/lib/arrange/grouping.d.ts +41 -0
  42. package/dist/lib/arrange/grouping.d.ts.map +1 -0
  43. package/dist/lib/arrange/grouping.js +280 -0
  44. package/dist/lib/arrange/imports.d.ts +6 -0
  45. package/dist/lib/arrange/imports.d.ts.map +1 -0
  46. package/dist/lib/arrange/imports.js +74 -0
  47. package/dist/lib/arrange/report.d.ts +4 -0
  48. package/dist/lib/arrange/report.d.ts.map +1 -0
  49. package/dist/lib/arrange/report.js +37 -0
  50. package/dist/lib/arrange/run-target.d.ts +4 -0
  51. package/dist/lib/arrange/run-target.d.ts.map +1 -0
  52. package/dist/lib/arrange/run-target.js +28 -0
  53. package/dist/lib/arrange/tokenizer.d.ts +37 -0
  54. package/dist/lib/arrange/tokenizer.d.ts.map +1 -0
  55. package/dist/lib/arrange/tokenizer.js +390 -0
  56. package/dist/lib/arrange/types.d.ts +104 -0
  57. package/dist/lib/arrange/types.d.ts.map +1 -0
  58. package/dist/lib/arrange/types.js +7 -0
  59. package/dist/lib/arrange/walk.d.ts +4 -0
  60. package/dist/lib/arrange/walk.d.ts.map +1 -0
  61. package/dist/lib/arrange/walk.js +34 -0
  62. package/dist/lib/arrange.d.ts +41 -0
  63. package/dist/lib/arrange.d.ts.map +1 -0
  64. package/dist/lib/arrange.js +38 -0
  65. package/dist/lib/infra/fs-contract.d.ts +28 -0
  66. package/dist/lib/infra/fs-contract.d.ts.map +1 -0
  67. package/dist/lib/infra/fs-contract.js +1 -0
  68. package/dist/lib/infra/node-io.d.ts +4 -0
  69. package/dist/lib/infra/node-io.d.ts.map +1 -0
  70. package/dist/lib/infra/node-io.js +27 -0
  71. package/dist/lib/mirror/config.d.ts +7 -0
  72. package/dist/lib/mirror/config.d.ts.map +1 -0
  73. package/dist/lib/mirror/config.js +146 -0
  74. package/dist/lib/mirror/constants.d.ts +10 -0
  75. package/dist/lib/mirror/constants.d.ts.map +1 -0
  76. package/dist/lib/mirror/constants.js +13 -0
  77. package/dist/lib/mirror/engine.d.ts +12 -0
  78. package/dist/lib/mirror/engine.d.ts.map +1 -0
  79. package/dist/lib/mirror/engine.js +248 -0
  80. package/dist/lib/mirror/errors.d.ts +10 -0
  81. package/dist/lib/mirror/errors.d.ts.map +1 -0
  82. package/dist/lib/mirror/errors.js +12 -0
  83. package/dist/lib/mirror/package-filter.d.ts +8 -0
  84. package/dist/lib/mirror/package-filter.d.ts.map +1 -0
  85. package/dist/lib/mirror/package-filter.js +33 -0
  86. package/dist/lib/mirror/reporter.d.ts +24 -0
  87. package/dist/lib/mirror/reporter.d.ts.map +1 -0
  88. package/dist/lib/mirror/reporter.js +126 -0
  89. package/dist/lib/mirror/sync.d.ts +4 -0
  90. package/dist/lib/mirror/sync.d.ts.map +1 -0
  91. package/dist/lib/mirror/sync.js +135 -0
  92. package/dist/lib/mirror/types.d.ts +81 -0
  93. package/dist/lib/mirror/types.d.ts.map +1 -0
  94. package/dist/lib/mirror/types.js +1 -0
  95. package/dist/lib/mirror/update-pkg.d.ts +13 -0
  96. package/dist/lib/mirror/update-pkg.d.ts.map +1 -0
  97. package/dist/lib/mirror/update-pkg.js +39 -0
  98. package/dist/lib/mirror/workspace-packages.d.ts +26 -0
  99. package/dist/lib/mirror/workspace-packages.d.ts.map +1 -0
  100. package/dist/lib/mirror/workspace-packages.js +160 -0
  101. package/dist/lib/mirror.d.ts +7 -0
  102. package/dist/lib/mirror.d.ts.map +1 -0
  103. package/dist/lib/mirror.js +5 -0
  104. package/dist/lib/repo-root.d.ts +7 -0
  105. package/dist/lib/repo-root.d.ts.map +1 -0
  106. package/dist/lib/repo-root.js +23 -0
  107. package/dist/lib/shared/utils.d.ts +9 -0
  108. package/dist/lib/shared/utils.d.ts.map +1 -0
  109. package/dist/lib/shared/utils.js +12 -0
  110. package/dist/program.d.ts +4 -0
  111. package/dist/program.d.ts.map +1 -0
  112. package/dist/program.js +38 -0
  113. package/package.json +42 -53
  114. package/CHANGELOG.md +0 -15
  115. package/dist/cjs/index.cjs +0 -2
  116. package/dist/cjs/src/index.d.ts +0 -3
  117. package/dist/cjs/src/index.d.ts.map +0 -1
  118. package/dist/esm/index.js +0 -2
  119. package/dist/esm/src/index.d.ts +0 -3
  120. package/dist/esm/src/index.d.ts.map +0 -1
package/README.md CHANGED
@@ -1,339 +1,100 @@
1
1
  # @codefast/cli
2
2
 
3
- Command line interface tools for CodeFast development, built with explicit architecture principles.
3
+ Two tools bundled in one CLI:
4
4
 
5
- ## Overview
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 |
6
9
 
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.
10
+ ---
8
11
 
9
- ## Features
12
+ ## Requirements & Install
10
13
 
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
25
- ```
26
-
27
- ## Usage
28
-
29
- ### Available Commands
30
-
31
- #### Hello Command
32
-
33
- ```bash
34
- codefast hello [options]
35
- codefast hello --name "Developer"
36
- ```
37
-
38
- Simple greeting command for testing CLI functionality.
39
-
40
- **Options:**
41
-
42
- - `-n, --name <name>`: Name to greet (default: "World")
43
-
44
- #### Analyze Command
45
-
46
- ```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
14
+ **Node.js ≥ 24** is required.
71
15
 
72
16
  ```bash
73
- codefast check-component-types [options]
74
- codefast check-component-types --packages-dir "packages"
75
- ```
76
-
77
- Check React component type correspondence across packages in a monorepo.
17
+ # Global
18
+ pnpm add -g @codefast/cli
78
19
 
79
- **Options:**
80
-
81
- - `-d, --packages-dir <dir>`: Packages directory to analyze (default: "packages")
82
-
83
- ## Architecture
84
-
85
- This CLI follows **Explicit Architecture** principles, ensuring clear separation of concerns, testability, and maintainability.
86
-
87
- ### Directory Structure
88
-
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
20
+ # Without a global install
21
+ pnpm dlx @codefast/cli -- --help
107
22
  ```
108
23
 
109
- ### Architectural Layers
110
-
111
- #### 1. Core/Application Layer
112
-
113
- Contains business logic and defines interfaces (ports) for external dependencies.
114
-
115
- **Use Cases:**
116
-
117
- - `AnalyzeProjectUseCase`: Orchestrates TypeScript project analysis
118
- - `CheckComponentTypesUseCase`: Handles React component type validation
119
- - `GreetUserUseCase`: Simple greeting functionality
24
+ ---
120
25
 
121
- **Ports (Interfaces):**
26
+ ## `arrange`
122
27
 
123
- - **Analysis Ports**: `TypeScriptAnalysisPort`, `ComponentAnalysisPort`
124
- - **Service Ports**: `LoggingServicePort`
125
- - **System Ports**: `FileSystemSystemPort`, `PathSystemPort`, `UrlSystemPort`
28
+ ### Recommended workflow
126
29
 
127
- #### 2. Infrastructure Layer
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.
128
33
 
129
- Implements the ports using concrete technologies and external libraries.
34
+ **Default target** (when path is omitted): `packages/ui/src/components` resolved from `process.cwd()`.
130
35
 
131
- **Adapters:**
36
+ ### Useful options
132
37
 
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
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.
139
40
 
140
- #### 3. Commands Layer
41
+ ### `group [tokens...]`
141
42
 
142
- Handles CLI interface using the Commander.js framework.
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.
143
44
 
144
- - `CommandHandler`: Main command orchestrator with dependency injection
45
+ ---
145
46
 
146
- #### 4. Dependency Injection Layer
47
+ ## Grouping philosophy — Render Pipeline Order
147
48
 
148
- Manages dependencies using InversifyJS container.
49
+ `arrange` does **not** sort alphabetically. It groups utilities in roughly the same order the browser reasons about them:
149
50
 
150
- **Modules:**
51
+ **Existence → Position → Layout → Sizing → Spacing → Shape → Background → Shadow → Typography → Composite → Motion → Starting → Behavior → Conditions (State)**
151
52
 
152
- - `infrastructureModule`: Binds infrastructure adapters
153
- - `applicationModule`: Binds use cases and application services
154
- - `commandsModule`: Binds command handlers
53
+ Bucket breakdown:
155
54
 
156
- ### Key Design Principles
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-[…]:` |
157
71
 
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
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.
163
73
 
164
- ## Development
74
+ To change a placement, edit `classifyBareUtility` in `src/lib/arrange/tokenizer.ts` and add a `classifyToken` test in `src/lib/arrange.test.ts`.
165
75
 
166
- ### Prerequisites
76
+ ---
167
77
 
168
- - Node.js 20.0.0+
169
- - pnpm 10.13.1+
78
+ ## `mirror sync`
170
79
 
171
- ### Setup
80
+ Run from anywhere under the monorepo — the CLI finds the root via `pnpm-workspace.yaml`.
172
81
 
173
82
  ```bash
174
- # Install dependencies
175
- pnpm install
176
-
177
- # Build the package
178
- pnpm build
179
-
180
- # Run in development mode
181
- pnpm dev
182
- ```
183
-
184
- ### Available Scripts
185
-
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
204
- ```
205
-
206
- ### Testing Strategy
207
-
208
- The CLI follows a comprehensive testing approach aligned with the explicit architecture:
209
-
210
- #### Unit Testing
211
-
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
215
-
216
- #### Test Structure
217
-
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
229
- ```
230
-
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
239
-
240
- #### 1. Define the Port (Interface)
241
-
242
- ```typescript
243
- // src/core/application/ports/new-feature.port.ts
244
- export interface NewFeaturePort {
245
- performAction(input: string): Promise<string>;
246
- }
83
+ codefast mirror sync # All workspace packages
84
+ codefast mirror sync packages/ui # One package only
85
+ codefast mirror sync -v # Verbose
247
86
  ```
248
87
 
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
- ```
88
+ **Config:** Place `codefast.config.js` (or `.mjs` / `.cjs` / `.json`) at repo root with a `mirror` object (`skipPackages`, `pathTransformations`, `customExports`, …).
266
89
 
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
- ```
90
+ > ⚠️ `.js`/`.mjs`/`.cjs` config files are loaded via `import()` — only run `mirror sync` in repositories you trust.
279
91
 
280
- #### 4. Configure Dependency Injection
92
+ ---
281
93
 
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
- };
94
+ ## Developing inside this monorepo
289
95
 
290
- // src/di/modules/infrastructure.module.ts
291
- infrastructureModule.bind<NewFeaturePort>(TYPES.NewFeaturePort).to(NewFeatureAdapter);
292
-
293
- // src/di/modules/application.module.ts
294
- applicationModule.bind<NewFeatureUseCase>(TYPES.NewFeatureUseCase).to(NewFeatureUseCase);
295
- ```
296
-
297
- #### 5. Add CLI Command
298
-
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
- });
96
+ ```bash
97
+ pnpm exec codefast --help
304
98
  ```
305
99
 
306
- ## Dependencies
307
-
308
- ### Core Dependencies
309
-
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
317
-
318
- ### Development Dependencies
319
-
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
325
-
326
- ## Contributing
327
-
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
332
-
333
- ## License
334
-
335
- MIT License - see LICENSE file for details.
336
-
337
- ## Related
338
-
339
- - [CodeFast UI Components](../ui/README.md)
100
+ Root `package.json` defines optional `cli:*` scripts (e.g. `cli:mirror-sync`, `cli:arrange-analyze`) as thin wrappers around `pnpm exec codefast …`.
package/dist/bin.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
package/dist/bin.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import process from "node:process";
3
+ import { runCli } from "#program";
4
+ const code = await runCli(process.argv);
5
+ process.exit(code);
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerArrangeCommand(program: Command): void;
3
+ //# sourceMappingURL=arrange.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arrange.d.ts","sourceRoot":"","sources":["../../src/commands/arrange.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4EzC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAsF7D"}
@@ -0,0 +1,114 @@
1
+ import path from "node:path";
2
+ import process from "node:process";
3
+ import { Option } from "commander";
4
+ import { ArrangeError, ArrangeErrorCode, analyzeDirectory, createNodeCliFs, createNodeCliLogger, DEFAULT_ARRANGE_TARGET, formatArray, formatCnCall, printAnalyzeReport, runOnTarget, suggestCnGroups, summarizeGroupBucketLabels, } from "#lib/arrange";
5
+ /** Commander attribute `withClassName` (second long flag `--with-class-name`). */
6
+ function createWithClassNameOption() {
7
+ return new Option("--with-classname, --with-class-name", "Append className as final cn() argument").default(false);
8
+ }
9
+ function defaultTargetPath() {
10
+ return path.resolve(process.cwd(), DEFAULT_ARRANGE_TARGET);
11
+ }
12
+ function checkTargetExists(resolved, fs, logger) {
13
+ if (!fs.existsSync(resolved)) {
14
+ logger.err(`Not found: ${resolved}`);
15
+ process.exitCode = 1;
16
+ return false;
17
+ }
18
+ return true;
19
+ }
20
+ function handleArrangeLibError(e, logger) {
21
+ if (e instanceof ArrangeError && e.code === ArrangeErrorCode.TARGET_NOT_FOUND) {
22
+ logger.err(e.message);
23
+ process.exitCode = 1;
24
+ return true;
25
+ }
26
+ return false;
27
+ }
28
+ function runArrangeAction(resolvedTarget, runOpts, fs, logger) {
29
+ try {
30
+ runOnTarget(resolvedTarget, {
31
+ write: runOpts.write,
32
+ withClassName: !!runOpts.withClassName,
33
+ cnImport: runOpts.cnImport,
34
+ }, fs, logger);
35
+ }
36
+ catch (e) {
37
+ if (!handleArrangeLibError(e, logger))
38
+ throw e;
39
+ }
40
+ }
41
+ export function registerArrangeCommand(program) {
42
+ const arrange = program
43
+ .command("arrange")
44
+ .description("Analyze and regroup Tailwind classes in cn() / tv() calls (Tailwind v4)");
45
+ arrange
46
+ .command("analyze")
47
+ .description("Report long strings, nested cn in tv(), and related findings")
48
+ .argument("[target]", "Directory or file (default: packages/ui/src/components)")
49
+ .action((target) => {
50
+ const fs = createNodeCliFs();
51
+ const logger = createNodeCliLogger();
52
+ const resolved = target ? path.resolve(target) : defaultTargetPath();
53
+ if (!checkTargetExists(resolved, fs, logger))
54
+ return;
55
+ printAnalyzeReport(resolved, analyzeDirectory(resolved, fs), logger);
56
+ });
57
+ arrange
58
+ .command("preview")
59
+ .description("Dry-run: print suggested replacements without writing files")
60
+ .argument("[target]", "Directory or file (default: packages/ui/src/components)")
61
+ .addOption(createWithClassNameOption())
62
+ .option("--cn-import <spec>", "Override module specifier when adding cn import")
63
+ .action((target, opts) => {
64
+ const fs = createNodeCliFs();
65
+ const logger = createNodeCliLogger();
66
+ const resolved = target ? path.resolve(target) : defaultTargetPath();
67
+ if (!checkTargetExists(resolved, fs, logger))
68
+ return;
69
+ runArrangeAction(resolved, {
70
+ write: false,
71
+ withClassName: opts.withClassName,
72
+ cnImport: opts.cnImport,
73
+ }, fs, logger);
74
+ });
75
+ arrange
76
+ .command("apply")
77
+ .description("Apply grouping and cn-in-tv unwrap edits to files")
78
+ .argument("[target]", "Directory or file (default: packages/ui/src/components)")
79
+ .addOption(createWithClassNameOption())
80
+ .option("--cn-import <spec>", "Override module specifier when adding cn import")
81
+ .action((target, opts) => {
82
+ const fs = createNodeCliFs();
83
+ const logger = createNodeCliLogger();
84
+ const resolved = target ? path.resolve(target) : defaultTargetPath();
85
+ if (!checkTargetExists(resolved, fs, logger))
86
+ return;
87
+ runArrangeAction(resolved, {
88
+ write: true,
89
+ withClassName: opts.withClassName,
90
+ cnImport: opts.cnImport,
91
+ }, fs, logger);
92
+ });
93
+ arrange
94
+ .command("group")
95
+ .description("Try grouping on a pasted class string (stdout: cn(...) or tv array with --tv)")
96
+ .argument("[tokens...]", "Class tokens (quote a single string if it contains spaces)")
97
+ .option("--tv", "Emit tv()-style array instead of cn() call", false)
98
+ .addOption(createWithClassNameOption())
99
+ .action((tokens, opts) => {
100
+ const inlineClasses = tokens.join(" ").trim();
101
+ if (!inlineClasses) {
102
+ process.stderr.write('Pass a class string. Example: codefast arrange group "flex gap-2 text-sm rounded-md"\n');
103
+ process.exitCode = 1;
104
+ return;
105
+ }
106
+ const groups = suggestCnGroups(inlineClasses);
107
+ const result = opts.tv
108
+ ? formatArray(groups)
109
+ : formatCnCall(groups, { trailingClassName: !!opts.withClassName });
110
+ process.stdout.write(`${result}\n`);
111
+ const bucketSummary = summarizeGroupBucketLabels(groups);
112
+ process.stdout.write(`\n// Buckets: ${JSON.stringify(bucketSummary)}\n`);
113
+ });
114
+ }
@@ -0,0 +1,4 @@
1
+ import { Command } from "commander";
2
+ export declare function packageArgToRelative(rootDir: string, arg: string | undefined): string | undefined;
3
+ export declare function registerMirrorCommand(program: Command): void;
4
+ //# sourceMappingURL=mirror.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mirror.d.ts","sourceRoot":"","sources":["../../src/commands/mirror.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAiBjG;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmC5D"}
@@ -0,0 +1,63 @@
1
+ import { realpathSync } from "node:fs";
2
+ import path from "node:path";
3
+ import process from "node:process";
4
+ import { createNodeCliFs } from "#lib/infra/node-io";
5
+ import { normalizePath, runMirrorSync } from "#lib/mirror";
6
+ import { findRepoRoot } from "#lib/repo-root";
7
+ function tryRealpath(entryPath) {
8
+ try {
9
+ return realpathSync.native(entryPath);
10
+ }
11
+ catch {
12
+ return path.resolve(entryPath);
13
+ }
14
+ }
15
+ export function packageArgToRelative(rootDir, arg) {
16
+ if (!arg)
17
+ return undefined;
18
+ const rootReal = tryRealpath(path.resolve(rootDir));
19
+ const cwdReal = tryRealpath(process.cwd());
20
+ const resolved = path.isAbsolute(arg) ? path.resolve(arg) : path.resolve(cwdReal, arg);
21
+ const targetReal = tryRealpath(resolved);
22
+ const rel = path.relative(rootReal, targetReal);
23
+ const normalized = normalizePath(rel);
24
+ if (normalized.startsWith("..") ||
25
+ path.isAbsolute(normalized) ||
26
+ normalized === "" ||
27
+ normalized === ".") {
28
+ throw new Error(`Package path must be a subdirectory under monorepo root: ${rootDir}`);
29
+ }
30
+ return normalized;
31
+ }
32
+ export function registerMirrorCommand(program) {
33
+ const mirror = program
34
+ .command("mirror")
35
+ .description("Keep package manifests aligned with what you ship");
36
+ mirror
37
+ .command("sync")
38
+ .description("Write package.json exports from dist/ for workspace packages")
39
+ .argument("[package]", "Optional package path relative to repo root (e.g. packages/ui)")
40
+ .option("-v, --verbose", "Print extra diagnostics", false)
41
+ .action(async function (pkg, options) {
42
+ const globals = this.optsWithGlobals();
43
+ const fs = createNodeCliFs();
44
+ const rootDir = findRepoRoot(fs);
45
+ let packageFilter;
46
+ try {
47
+ packageFilter = packageArgToRelative(rootDir, pkg);
48
+ }
49
+ catch (e) {
50
+ this.error(e instanceof Error ? e.message : String(e));
51
+ return;
52
+ }
53
+ const exitCode = await runMirrorSync({
54
+ rootDir,
55
+ verbose: options.verbose,
56
+ /** Commander sets `color: false` when `--no-color` is passed (default `color: true`). */
57
+ noColor: globals.color === false,
58
+ packageFilter,
59
+ fs,
60
+ });
61
+ process.exitCode = exitCode;
62
+ });
63
+ }
@@ -0,0 +1,4 @@
1
+ import type { CliFs } from "#lib/infra/fs-contract";
2
+ import type { AnalyzeReport } from "#lib/arrange/types";
3
+ export declare function analyzeDirectory(target: string, fs: CliFs): AnalyzeReport;
4
+ //# sourceMappingURL=analyze.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../../src/lib/arrange/analyze.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAEpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AA8FxD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,GAAG,aAAa,CAsCzE"}