tailwind-unwind 0.1.0 → 0.1.1

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 (2) hide show
  1. package/README.md +160 -54
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,28 +1,57 @@
1
1
  # tailwind-unwind
2
2
 
3
- Analyze Tailwind CSS class usage patterns in React and Next.js projects. Find repeated utility combinations and opportunities to extract reusable component classes.
3
+ CLI tool to analyze, extract, and refactor repeated Tailwind CSS utility patterns in React and Next.js projects.
4
4
 
5
5
  **Repository:** [github.com/AVPletnev/tailwind-unwind](https://github.com/AVPletnev/tailwind-unwind)
6
6
 
7
+ ## Features
8
+
9
+ - **analyze** — find frequent `className` patterns with file locations
10
+ - **generate** — create `@layer components` CSS with `@apply`
11
+ - **apply** — auto-replace repeated class strings in `.tsx`/`.jsx` source files
12
+ - Parses static strings, template literals, `cn()` / `clsx()` / `classnames()`
13
+ - Human-readable class names (`twu-page-header`, `twu-media-cover`) with `twu-` namespace prefix
14
+
7
15
  ## Installation
8
16
 
9
17
  ```bash
10
18
  npm install -g tailwind-unwind
19
+
11
20
  # or run without installing
12
21
  npx tailwind-unwind analyze ./src
13
22
  ```
14
23
 
15
- ## Usage
24
+ Requires **Node.js 18+**.
25
+
26
+ ## Quick start
16
27
 
17
28
  ```bash
18
- npx tailwind-unwind analyze <path>
29
+ # 1. See what repeats in your project
30
+ npx tailwind-unwind analyze ./src
31
+
32
+ # 2. Generate component CSS
33
+ npx tailwind-unwind generate ./src --output styles.css
34
+
35
+ # 3. Import styles.css in your global CSS (e.g. globals.css), then apply replacements
36
+ npx tailwind-unwind apply ./src --output styles.css --dry-run # preview
37
+ npx tailwind-unwind apply ./src --output styles.css # write changes
19
38
  ```
20
39
 
21
- ### CLI options
40
+ ---
41
+
42
+ ## Commands
43
+
44
+ ### `analyze`
45
+
46
+ Scan a directory and report the most frequent Tailwind class combinations.
47
+
48
+ ```bash
49
+ npx tailwind-unwind analyze <path>
50
+ ```
22
51
 
23
52
  | Flag | Default | Description |
24
53
  |------|---------|-------------|
25
- | `--min-occurrences <n>` | `5` | Minimum occurrences for a combination |
54
+ | `--min-occurrences <n>` | `5` | Minimum occurrences (combinations must appear **more than** n times) |
26
55
  | `--min-size <n>` | `2` | Minimum classes per combination |
27
56
  | `--max-size <n>` | `5` | Maximum classes per combination |
28
57
  | `--top <n>` | `10` | Number of top combinations to show |
@@ -37,121 +66,198 @@ npx tailwind-unwind analyze ./src --format json
37
66
  npx tailwind-unwind analyze ./src --min-occurrences 10 --top 5
38
67
  ```
39
68
 
40
- Scan a directory recursively for `.tsx`, `.jsx`, `.ts`, and `.js` files. The tool ignores `node_modules`, `.next`, `dist`, `build`, and `.git`.
69
+ **Example output:**
41
70
 
42
- ### Generate CSS
71
+ ```
72
+ 📊 Tailwind Analysis Report
73
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
74
+ Files scanned: 47
75
+ Components with className: 312
76
+ Unique class combinations: 89
77
+
78
+ 🏆 Top 10 most frequent combinations:
43
79
 
44
- Extract **exact duplicate className strings** into reusable component classes:
80
+ 1. "flex items-center justify-between p-4"
81
+ Occurrences: 24
82
+ Suggestion: .page-header
83
+ Found in: src/components/Header.tsx:12, src/layout/Toolbar.tsx:5 (+18 more)
84
+
85
+ 💡 Potential code reduction: 38%
86
+ 💡 Generate CSS: npx tailwind-unwind generate <path> --output styles.css
87
+ 💡 Apply classes: npx tailwind-unwind apply <path> --output styles.css
88
+ ```
89
+
90
+ `analyze` finds frequent **subsets** of classes (2–5 utilities) and deduplicates subsets by default.
91
+
92
+ ---
93
+
94
+ ### `generate`
95
+
96
+ Extract **exact duplicate `className` strings** into reusable component classes.
97
+
98
+ ```bash
99
+ npx tailwind-unwind generate <path> --output <file.css>
100
+ ```
101
+
102
+ | Flag | Default | Description |
103
+ |------|---------|-------------|
104
+ | `--output <file>` | *(required)* | Output CSS file path |
105
+ | `--min-occurrences <n>` | `3` | Minimum occurrences (must appear **≥ n** times) |
106
+ | `--min-size <n>` | `2` | Minimum classes per set |
107
+ | `--max-size <n>` | `5` | Maximum classes per set |
108
+ | `--top <n>` | `10` | Max number of component classes to generate |
109
+ | `--prefix <name>` | `twu-` | Namespace prefix for generated classes |
45
110
 
46
111
  ```bash
47
112
  npx tailwind-unwind generate ./src --output styles.css
48
- # Custom namespace (default: twu-)
49
113
  npx tailwind-unwind generate ./src --output styles.css --prefix app-
114
+ npx tailwind-unwind generate ./src --output styles.css --min-occurrences 2 --top 20
50
115
  ```
51
116
 
52
- Unlike `analyze` (which finds frequent subsets), `generate` looks for identical full class lists on JSX elements. Default `--min-occurrences` is `3`.
53
-
54
- Example output file:
117
+ **Example output (`styles.css`):**
55
118
 
56
119
  ```css
120
+ /**
121
+ * Generated by tailwind-unwind
122
+ * Source: ./src
123
+ * Class prefix: twu-
124
+ */
57
125
  @layer components {
58
- .twu-toolbar {
126
+ .twu-page-header {
59
127
  @apply flex items-center justify-between p-4;
60
128
  }
61
129
 
62
130
  .twu-media-cover {
63
131
  @apply w-full h-auto object-cover rounded-lg;
64
132
  }
133
+
134
+ .twu-primary-button {
135
+ @apply bg-blue-500 text-white px-4 py-2 rounded-lg;
136
+ }
65
137
  }
66
138
  ```
67
139
 
68
- Import the generated file in your global CSS (e.g. `globals.css`), then replace repeated `className` strings with the new classes.
140
+ Import `styles.css` in your global CSS, then use the generated classes in JSX.
69
141
 
70
- Supports filter flags: `--min-occurrences`, `--min-size`, `--max-size`, `--top`.
142
+ ---
71
143
 
72
- ### Apply (replace className in source)
144
+ ### `apply`
73
145
 
74
- Generate CSS **and** replace matching `className` strings in your `.tsx`/`.jsx` files:
146
+ Generate CSS **and** replace matching `className` strings in source files.
75
147
 
76
148
  ```bash
77
- # Preview changes without writing files
78
- npx tailwind-unwind apply ./src --output styles.css --dry-run
149
+ npx tailwind-unwind apply <path> --output <file.css>
150
+ ```
151
+
152
+ Supports the same flags as `generate`, plus:
79
153
 
80
- # Apply replacements and write styles.css
154
+ | Flag | Description |
155
+ |------|-------------|
156
+ | `--dry-run` | Preview replacements without writing files |
157
+
158
+ ```bash
159
+ npx tailwind-unwind apply ./src --output styles.css --dry-run
81
160
  npx tailwind-unwind apply ./src --output styles.css
82
161
  ```
83
162
 
84
- Only **exact static matches** are replaced (string literals and static `cn()`/`clsx()` calls). Dynamic expressions are left unchanged.
163
+ **What gets replaced:**
164
+
165
+ | Pattern | Replaced? |
166
+ |---------|-----------|
167
+ | `className="flex items-center p-4"` | ✅ |
168
+ | `className={cn('flex', 'items-center', 'p-4')}` | ✅ (static args only) |
169
+ | `className={getClasses()}` | ❌ skipped |
170
+ | `` className={`flex ${active ? 'p-4' : ''}`} `` | ❌ skipped (dynamic) |
171
+
172
+ **Before → After:**
85
173
 
86
- ### Example output
174
+ ```tsx
175
+ // before
176
+ <div className="flex items-center justify-between p-4">Header</div>
87
177
 
178
+ // after
179
+ <div className="twu-page-header">Header</div>
88
180
  ```
89
- 📊 Tailwind Analysis Report
90
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
91
- Files scanned: 5
92
- Components with className: 18
93
- Unique class combinations: 4
94
181
 
95
- 🏆 Top 10 most frequent combinations:
182
+ ---
96
183
 
97
- 1. "flex items-center justify-between p-4"
98
- Occurrences: 8
99
- Suggestion: .flex-items-center-justify-between
184
+ ## Class naming
100
185
 
101
- 2. "w-full h-auto object-cover rounded-lg"
102
- Occurrences: 7
103
- Suggestion: .w-full-h-auto-object-cover
186
+ Generated classes use a **`twu-` prefix** by default to avoid conflicts with existing project styles. Names are derived from semantic rules, not utility concatenation:
104
187
 
105
- 💡 Potential code reduction: 42%
106
- 💡 Generate CSS: npx tailwind-unwind generate <path> --output styles.css
107
- ```
188
+ | Utilities | Generated class |
189
+ |-----------|-----------------|
190
+ | `flex items-center justify-between p-4` | `twu-page-header` |
191
+ | `w-full object-cover rounded-lg` | `twu-media-cover` |
192
+ | `bg-blue-500 px-4 py-2 rounded-lg` | `twu-primary-button` |
193
+ | `grid grid-cols-3 gap-4` | `twu-card-grid` |
194
+ | `fixed inset-0 bg-black/50` | `twu-backdrop` |
195
+
196
+ Customize with `--prefix app-` → `.app-page-header`, etc.
197
+
198
+ ---
108
199
 
109
- ## What it analyzes
200
+ ## What gets parsed
110
201
 
111
- - **Static strings:** `className="flex p-4"` and `class="flex p-4"`
112
- - **Template literals:** `className={\`flex p-4 ${active ? 'bg-blue' : ''}\`}` — static segments extracted
113
- - **Class merge utilities:** `cn()`, `clsx()`, `classnames()`, `twMerge()`, `cx()` — string arguments and conditionals extracted
114
- - **Fully dynamic expressions:** `className={getClasses()}` — skipped with a warning
202
+ Scans `.tsx`, `.jsx`, `.ts`, `.js` recursively. Ignores `node_modules`, `.next`, `dist`, `build`, `.git`.
115
203
 
116
- Class combinations are normalized (order-independent): `flex p-4` and `p-4 flex` count as the same pattern.
204
+ - **Static strings:** `className="flex p-4"` / `class="flex p-4"`
205
+ - **Template literals:** static segments extracted; `${...}` expressions flagged
206
+ - **Merge utilities:** `cn()`, `clsx()`, `classnames()`, `twMerge()`, `cx()`
207
+ - **Dynamic expressions:** `className={getClasses()}` — warning, skipped
117
208
 
118
- Subset combinations are deduplicated by default (e.g. `flex p-4` is hidden when `flex items-center p-4` is present). Each result includes file locations.
209
+ Class order is normalized: `flex p-4` and `p-4 flex` are treated as the same set.
210
+
211
+ ---
119
212
 
120
213
  ## Programmatic API
121
214
 
122
215
  ```typescript
123
216
  import {
124
217
  analyzeCommand,
218
+ generateCommand,
219
+ applyCommand,
125
220
  walkSourceFiles,
126
221
  parseFile,
127
222
  findFrequentPatterns,
223
+ findRepeatedClassSets,
224
+ buildComponents,
128
225
  normalizeClasses,
129
226
  } from 'tailwind-unwind';
130
227
 
228
+ // Analyze
229
+ await analyzeCommand('./src', { format: 'json' });
230
+
231
+ // Build component map
131
232
  const files = await walkSourceFiles('./src');
132
233
  const result = await parseFile(files[0]);
133
- const extraction = result.extractions[0];
134
-
135
- const patterns = findFrequentPatterns([
136
- {
137
- classes: extraction.classes,
138
- filePath: files[0],
139
- line: extraction.line,
140
- },
141
- ]);
234
+
235
+ const { components, css, replacementMap } = buildComponents(
236
+ [{ classes: result.extractions[0].classes, filePath: files[0] }],
237
+ { sourcePath: './src', prefix: 'twu-' },
238
+ );
142
239
  ```
143
240
 
241
+ ---
242
+
144
243
  ## Development
145
244
 
146
245
  ```bash
246
+ git clone https://github.com/AVPletnev/tailwind-unwind.git
247
+ cd tailwind-unwind
147
248
  npm install
148
- npm run build
249
+ npm run build # required before running CLI locally
149
250
  npm test
251
+
150
252
  node bin/index.js analyze ./test-project
151
253
  node bin/index.js generate ./test-project --output styles.css
152
254
  node bin/index.js apply ./test-project --output styles.css --dry-run
153
255
  ```
154
256
 
257
+ > **Note:** `bin/index.js` runs compiled code from `dist/`. Always run `npm run build` after changing source files.
258
+
259
+ ---
260
+
155
261
  ## License
156
262
 
157
263
  MIT — see [LICENSE](LICENSE).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tailwind-unwind",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Analyze Tailwind CSS class usage patterns in React/Next.js projects",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",