@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.
- package/README.md +178 -262
- package/dist/analyze-DvtJ3j9m.mjs +92 -0
- package/dist/arrange-Bbh62DrU.mjs +107 -0
- package/dist/arrange-BqP2DI3w.mjs +8 -0
- package/dist/ast-helpers-MDRYuIAk.mjs +115 -0
- package/dist/bin.mjs +8 -0
- package/dist/caught-unknown-message-CRhkDWO7.mjs +20 -0
- package/dist/collectors-cn-BS8TTQ8O.mjs +61 -0
- package/dist/collectors-jsx-D9tL2ZPM.mjs +20 -0
- package/dist/collectors-tv-D2PXZaRt.mjs +170 -0
- package/dist/colors-BjdkCfGK.mjs +12 -0
- package/dist/commands/arrange.mjs +2 -0
- package/dist/commands/mirror.mjs +2 -0
- package/dist/commands/mirror.test.mjs +48 -0
- package/dist/commands/tag.mjs +2 -0
- package/dist/config-DVia_f9a.mjs +3 -0
- package/dist/config-reporter-SCcWDZc1.mjs +10 -0
- package/dist/constants-AtPhF-Ct.mjs +169 -0
- package/dist/constants-Bq5wdyLC.mjs +8 -0
- package/dist/dirent-list-BSDeRG5i.mjs +14 -0
- package/dist/engine-CcWEHSCq.mjs +287 -0
- package/dist/engine-DRTdkc5u.mjs +236 -0
- package/dist/errors-CFH4w6ic.mjs +15 -0
- package/dist/errors-CpU1ogfA.mjs +19 -0
- package/dist/formatters-DGE8jJ53.mjs +52 -0
- package/dist/group-file-ClW7XdD6.mjs +113 -0
- package/dist/grouping-c1grpCQT.mjs +235 -0
- package/dist/imports-DSVq1xhB.mjs +54 -0
- package/dist/lib/arrange/application/analyze.mjs +2 -0
- package/dist/lib/arrange/application/analyze.test.mjs +171 -0
- package/dist/lib/arrange/application/group-file.mjs +2 -0
- package/dist/lib/arrange/application/group-file.test.mjs +273 -0
- package/dist/lib/arrange/application/run-target.mjs +2 -0
- package/dist/lib/arrange/application/run-target.test.mjs +97 -0
- package/dist/lib/arrange/domain/ast/ast-helpers.mjs +2 -0
- package/dist/lib/arrange/domain/ast/ast-helpers.test.mjs +148 -0
- package/dist/lib/arrange/domain/ast/collectors-cn.mjs +2 -0
- package/dist/lib/arrange/domain/ast/collectors-cn.test.mjs +58 -0
- package/dist/lib/arrange/domain/ast/collectors-jsx.mjs +2 -0
- package/dist/lib/arrange/domain/ast/collectors-jsx.test.mjs +20 -0
- package/dist/lib/arrange/domain/ast/collectors-tv.mjs +2 -0
- package/dist/lib/arrange/domain/ast/collectors-tv.test.mjs +145 -0
- package/dist/lib/arrange/domain/ast/targets.mjs +2 -0
- package/dist/lib/arrange/domain/ast/targets.test.mjs +50 -0
- package/dist/lib/arrange/domain/constants.mjs +2 -0
- package/dist/lib/arrange/domain/errors.mjs +2 -0
- package/dist/lib/arrange/domain/grouping.mjs +2 -0
- package/dist/lib/arrange/domain/grouping.test.mjs +186 -0
- package/dist/lib/arrange/domain/imports.mjs +2 -0
- package/dist/lib/arrange/domain/imports.test.mjs +48 -0
- package/dist/lib/arrange/domain/tokenizer.mjs +2 -0
- package/dist/lib/arrange/domain/tokenizer.test.mjs +165 -0
- package/dist/lib/arrange/domain/types.mjs +1 -0
- package/dist/lib/arrange/index.mjs +16 -0
- package/dist/lib/arrange/infra/walk.mjs +2 -0
- package/dist/lib/arrange/infra/walk.test.mjs +50 -0
- package/dist/lib/arrange/presentation/formatters.mjs +2 -0
- package/dist/lib/arrange/presentation/formatters.test.mjs +73 -0
- package/dist/lib/arrange/presentation/report.mjs +2 -0
- package/dist/lib/config/domain/schema.mjs +2 -0
- package/dist/lib/config/index.mjs +4 -0
- package/dist/lib/config/infra/loader.mjs +2 -0
- package/dist/lib/config/infra/loader.test.mjs +116 -0
- package/dist/lib/infra/caught-unknown-message.mjs +2 -0
- package/dist/lib/infra/config-reporter.mjs +2 -0
- package/dist/lib/infra/fs-contract.mjs +1 -0
- package/dist/lib/infra/node-io.mjs +2 -0
- package/dist/lib/infra/workspace/repo-root.mjs +2 -0
- package/dist/lib/infra/workspace/repo-root.test.mjs +45 -0
- package/dist/lib/mirror/application/engine.mjs +2 -0
- package/dist/lib/mirror/application/sync.mjs +2 -0
- package/dist/lib/mirror/application/sync.test.mjs +482 -0
- package/dist/lib/mirror/domain/constants.mjs +2 -0
- package/dist/lib/mirror/domain/errors.mjs +2 -0
- package/dist/lib/mirror/domain/types.mjs +1 -0
- package/dist/lib/mirror/index.mjs +3 -0
- package/dist/lib/mirror/infra/dirent-list.mjs +2 -0
- package/dist/lib/mirror/infra/dirent-list.test.mjs +18 -0
- package/dist/lib/mirror/infra/package-filter.mjs +2 -0
- package/dist/lib/mirror/infra/package-filter.test.mjs +32 -0
- package/dist/lib/mirror/infra/path-normalizer.mjs +2 -0
- package/dist/lib/mirror/infra/update-pkg.mjs +2 -0
- package/dist/lib/mirror/infra/workspace-packages.mjs +2 -0
- package/dist/lib/mirror/infra/workspace-packages.test.mjs +167 -0
- package/dist/lib/mirror/presentation/reporter.mjs +2 -0
- package/dist/lib/tag/application/engine.mjs +2 -0
- package/dist/lib/tag/application/engine.test.mjs +159 -0
- package/dist/lib/tag/domain/types.mjs +1 -0
- package/dist/lib/tag/index.mjs +5 -0
- package/dist/lib/tag/infra/target-resolver.mjs +2 -0
- package/dist/lib/tag/presentation/colors.mjs +2 -0
- package/dist/lib/tag/presentation/tag-presenter.mjs +2 -0
- package/dist/lib/tag/presentation/tag-presenter.test.mjs +29 -0
- package/dist/loader-DpN-zl5g.mjs +78 -0
- package/dist/mirror-CbGiLXKT.mjs +2 -0
- package/dist/mirror-DqJE3ejL.mjs +66 -0
- package/dist/node-io-DFRbHO6m.mjs +30 -0
- package/dist/package-filter-C_8ryqvz.mjs +26 -0
- package/dist/path-normalizer-D4ceBO3l.mjs +7 -0
- package/dist/program-DR-H4tH3.mjs +34 -0
- package/dist/program.mjs +2 -0
- package/dist/repo-root-HmtIEm6J.mjs +22 -0
- package/dist/report-DYxvw34T.mjs +24 -0
- package/dist/reporter-CwIe_tAM.mjs +108 -0
- package/dist/run-target-CJWrqNT4.mjs +65 -0
- package/dist/schema-Or8RAARd.mjs +25 -0
- package/dist/sync-e1Sp-shi.mjs +157 -0
- package/dist/tag-Cd9JujU5.mjs +2 -0
- package/dist/tag-hEKVZ_-7.mjs +58 -0
- package/dist/tag-presenter-Cs66UBsH.mjs +63 -0
- package/dist/target-resolver-CR3FXMTy.mjs +170 -0
- package/dist/targets-pCTslnFh.mjs +106 -0
- package/dist/tokenizer-CeZx73Zu.mjs +216 -0
- package/dist/update-pkg-CnGfjWBy.mjs +37 -0
- package/dist/walk-DjgQn66n.mjs +33 -0
- package/dist/workspace-packages-K2NYTHJq.mjs +159 -0
- package/package.json +42 -53
- package/CHANGELOG.md +0 -15
- package/dist/cjs/index.cjs +0 -2
- package/dist/cjs/src/index.d.ts +0 -3
- package/dist/cjs/src/index.d.ts.map +0 -1
- package/dist/esm/index.js +0 -2
- package/dist/esm/src/index.d.ts +0 -3
- 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
|
-
|
|
3
|
+
A focused CLI for two recurring maintenance tasks in monorepos:
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
|
|
9
|
+
```mermaid
|
|
10
|
+
flowchart LR
|
|
11
|
+
R[codefast]
|
|
12
|
+
R --> A[arrange]
|
|
13
|
+
R --> M[mirror]
|
|
14
|
+
R --> T[tag]
|
|
8
15
|
|
|
9
|
-
|
|
16
|
+
A --> A0[analyze]
|
|
17
|
+
A --> A1[preview]
|
|
18
|
+
A --> A2[apply]
|
|
19
|
+
A --> A3[group]
|
|
10
20
|
|
|
11
|
-
|
|
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
|
-
|
|
24
|
+
---
|
|
28
25
|
|
|
29
|
-
|
|
26
|
+
## Requirements
|
|
30
27
|
|
|
31
|
-
|
|
28
|
+
- Node.js `>=22.0.0`
|
|
29
|
+
- pnpm (recommended)
|
|
32
30
|
|
|
33
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
#### Analyze Command
|
|
33
|
+
## Installation
|
|
45
34
|
|
|
46
35
|
```bash
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
73
|
-
codefast
|
|
74
|
-
codefast check-component-types --packages-dir "packages"
|
|
39
|
+
# Or run without installing
|
|
40
|
+
pnpm dlx @codefast/cli --help
|
|
75
41
|
```
|
|
76
42
|
|
|
77
|
-
|
|
43
|
+
---
|
|
78
44
|
|
|
79
|
-
|
|
45
|
+
## Quick start
|
|
80
46
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
47
|
+
```bash
|
|
48
|
+
# 1. Preview proposed changes — no files written
|
|
49
|
+
codefast arrange preview packages/ui/src/components
|
|
84
50
|
|
|
85
|
-
|
|
51
|
+
# 2. Apply after reviewing the diff
|
|
52
|
+
codefast arrange apply packages/ui/src/components
|
|
86
53
|
|
|
87
|
-
|
|
54
|
+
# 3. Regenerate package exports from built dist/
|
|
55
|
+
codefast mirror sync
|
|
88
56
|
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
#### 1. Core/Application Layer
|
|
61
|
+
---
|
|
112
62
|
|
|
113
|
-
|
|
63
|
+
## `arrange`
|
|
114
64
|
|
|
115
|
-
|
|
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
|
-
|
|
118
|
-
- `CheckComponentTypesUseCase`: Handles React component type validation
|
|
119
|
-
- `GreetUserUseCase`: Simple greeting functionality
|
|
67
|
+
### Workflow
|
|
120
68
|
|
|
121
|
-
|
|
69
|
+
Run the three subcommands in order:
|
|
122
70
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
77
|
+
The default `target` when omitted is `packages/ui/src/components`, resolved from `process.cwd()`.
|
|
128
78
|
|
|
129
|
-
|
|
79
|
+
### Flags
|
|
130
80
|
|
|
131
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
94
|
+
---
|
|
151
95
|
|
|
152
|
-
|
|
153
|
-
- `applicationModule`: Binds use cases and application services
|
|
154
|
-
- `commandsModule`: Binds command handlers
|
|
96
|
+
## `mirror sync`
|
|
155
97
|
|
|
156
|
-
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
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
|
-
|
|
139
|
+
Use your real package names from `package.json#name` (for example `@acme/ui`) and adjust entries to match your workspace.
|
|
167
140
|
|
|
168
|
-
-
|
|
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
|
-
|
|
143
|
+
> **Security note:** `.js`, `.mjs`, and `.cjs` config files are loaded via `import()`. Only run `mirror sync` in repositories you trust.
|
|
172
144
|
|
|
173
|
-
|
|
174
|
-
# Install dependencies
|
|
175
|
-
pnpm install
|
|
145
|
+
---
|
|
176
146
|
|
|
177
|
-
|
|
178
|
-
pnpm build
|
|
147
|
+
## Lifecycle hooks (`codefast.config.mjs`)
|
|
179
148
|
|
|
180
|
-
|
|
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
|
-
|
|
151
|
+
```javascript
|
|
152
|
+
import { execSync } from "node:child_process";
|
|
185
153
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
169
|
+
Hook contract:
|
|
207
170
|
|
|
208
|
-
|
|
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
|
-
|
|
176
|
+
---
|
|
211
177
|
|
|
212
|
-
|
|
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
|
-
|
|
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
|
-
```
|
|
219
|
-
src
|
|
220
|
-
|
|
221
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
198
|
+
## Grouping philosophy — Render Pipeline Order
|
|
281
199
|
|
|
282
|
-
|
|
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
|
-
|
|
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
|
-
|
|
294
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
226
|
+
---
|
|
307
227
|
|
|
308
|
-
|
|
228
|
+
## Troubleshooting
|
|
309
229
|
|
|
310
|
-
|
|
311
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
321
|
-
|
|
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
|
-
|
|
239
|
+
---
|
|
327
240
|
|
|
328
|
-
|
|
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
|
-
|
|
243
|
+
```bash
|
|
244
|
+
# Build the local CLI (produces dist/bin.js)
|
|
245
|
+
pnpm --filter @codefast/cli build
|
|
334
246
|
|
|
335
|
-
|
|
247
|
+
# Run the local entrypoint
|
|
248
|
+
pnpm exec codefast --help
|
|
249
|
+
```
|
|
336
250
|
|
|
337
|
-
|
|
251
|
+
A few naming conventions to keep in mind:
|
|
338
252
|
|
|
339
|
-
-
|
|
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 };
|