tailwind-unwind 0.2.0 → 0.4.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 +98 -243
- package/dist/{chunk-FASYIEVZ.js → chunk-UXXIEFP4.js} +853 -168
- package/dist/chunk-UXXIEFP4.js.map +1 -0
- package/dist/cli/index.js +83 -11
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +189 -74
- package/dist/index.js +31 -1
- package/package.json +12 -2
- package/tailwind-unwind.config.example.json +5 -2
- package/dist/chunk-FASYIEVZ.js.map +0 -1
package/README.md
CHANGED
|
@@ -2,325 +2,180 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/tailwind-unwind)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**Find repeated Tailwind classes in your React code and turn them into reusable component classes.**
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
If you copy-paste the same `className="flex items-center p-4 ..."` across dozens of files, this tool helps you clean that up — without doing it by hand.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Works with React / Next.js projects. Node.js 18+.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
- **generate** — create `@layer components` CSS with `@apply`
|
|
13
|
-
- **apply** — auto-replace repeated class strings in `.tsx`/`.jsx` source files
|
|
14
|
-
- Parses static strings, template literals, `cn()` / `clsx()` / `classnames()`
|
|
15
|
-
- Human-readable class names (`twu-page-header`, `twu-media-cover`) with `twu-` namespace prefix
|
|
11
|
+
## The problem it solves
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
# or run without installing
|
|
23
|
-
npx tailwind-unwind analyze ./src
|
|
13
|
+
```tsx
|
|
14
|
+
// Same utilities repeated everywhere
|
|
15
|
+
<div className="flex items-center justify-between p-4">...</div>
|
|
16
|
+
<div className="flex items-center justify-between p-4">...</div>
|
|
17
|
+
<div className="flex items-center justify-between p-4">...</div>
|
|
24
18
|
```
|
|
25
19
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
## Configuration
|
|
29
|
-
|
|
30
|
-
Copy [`tailwind-unwind.config.example.json`](tailwind-unwind.config.example.json) to your project root:
|
|
20
|
+
**tailwind-unwind** finds these duplicates and can replace them with one class:
|
|
31
21
|
|
|
32
|
-
```
|
|
33
|
-
|
|
22
|
+
```tsx
|
|
23
|
+
<div className="twu-page-header">...</div>
|
|
34
24
|
```
|
|
35
25
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
{
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"names": {
|
|
43
|
-
"flex items-center justify-between p-4": "page-header",
|
|
44
|
-
"w-full h-auto object-cover rounded-lg": "media-cover"
|
|
45
|
-
},
|
|
46
|
-
"analyze": {
|
|
47
|
-
"minOccurrences": 5,
|
|
48
|
-
"top": 10
|
|
49
|
-
},
|
|
50
|
-
"generate": {
|
|
51
|
-
"minOccurrences": 3,
|
|
52
|
-
"prefix": "twu-",
|
|
53
|
-
"output": "src/styles/components.css"
|
|
54
|
-
},
|
|
55
|
-
"apply": {
|
|
56
|
-
"output": "src/styles/components.css"
|
|
26
|
+
And generates the CSS for you:
|
|
27
|
+
|
|
28
|
+
```css
|
|
29
|
+
@layer components {
|
|
30
|
+
.twu-page-header {
|
|
31
|
+
@apply flex items-center justify-between p-4;
|
|
57
32
|
}
|
|
58
33
|
}
|
|
59
34
|
```
|
|
60
35
|
|
|
61
|
-
| Key | Description |
|
|
62
|
-
|-----|-------------|
|
|
63
|
-
| `include` / `exclude` | Glob patterns for file scanning |
|
|
64
|
-
| `names` | Custom class names (utilities string → base name, prefix added automatically) |
|
|
65
|
-
| `analyze` / `generate` / `apply` | Per-command overrides (`minOccurrences`, `top`, `prefix`, `output`, …) |
|
|
66
|
-
|
|
67
|
-
Config is discovered from the current directory **and** ancestors of `<path>`. CLI flags override config values.
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
npx tailwind-unwind analyze ./src --config ./tailwind-unwind.config.json
|
|
71
|
-
npx tailwind-unwind generate ./src --include "src/components/**/*.tsx"
|
|
72
|
-
```
|
|
73
|
-
|
|
74
36
|
## Quick start
|
|
75
37
|
|
|
76
38
|
```bash
|
|
77
|
-
# 1. See what repeats
|
|
39
|
+
# 1. See what repeats
|
|
78
40
|
npx tailwind-unwind analyze ./src
|
|
79
41
|
|
|
80
|
-
# 2. Generate
|
|
42
|
+
# 2. Generate CSS
|
|
81
43
|
npx tailwind-unwind generate ./src --output styles.css
|
|
82
44
|
|
|
83
|
-
# 3. Import styles.css in
|
|
84
|
-
npx tailwind-unwind apply ./src --output styles.css --dry-run # preview
|
|
45
|
+
# 3. Import styles.css in globals.css, then replace in source files
|
|
46
|
+
npx tailwind-unwind apply ./src --output styles.css --dry-run # preview first
|
|
85
47
|
npx tailwind-unwind apply ./src --output styles.css # write changes
|
|
86
48
|
```
|
|
87
49
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
## Commands
|
|
91
|
-
|
|
92
|
-
### `analyze`
|
|
93
|
-
|
|
94
|
-
Scan a directory and report the most frequent Tailwind class combinations.
|
|
95
|
-
|
|
96
|
-
```bash
|
|
97
|
-
npx tailwind-unwind analyze <path>
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
| Flag | Default | Description |
|
|
101
|
-
|------|---------|-------------|
|
|
102
|
-
| `--min-occurrences <n>` | `5` | Minimum occurrences (combinations must appear **more than** n times) |
|
|
103
|
-
| `--min-size <n>` | `2` | Minimum classes per combination |
|
|
104
|
-
| `--max-size <n>` | `5` | Maximum classes per combination |
|
|
105
|
-
| `--top <n>` | `10` | Number of top combinations to show |
|
|
106
|
-
| `--format <type>` | `console` | Output format: `console` or `json` |
|
|
107
|
-
| `--no-dedupe-subsets` | — | Include subset combinations in results |
|
|
108
|
-
| `--config <file>` | — | Path to config file |
|
|
109
|
-
| `--include <patterns>` | all `src` | Comma-separated glob include patterns |
|
|
110
|
-
| `--exclude <patterns>` | — | Comma-separated glob exclude patterns |
|
|
50
|
+
Install globally (optional):
|
|
111
51
|
|
|
112
52
|
```bash
|
|
113
|
-
|
|
114
|
-
npx tailwind-unwind analyze ./src --format json
|
|
115
|
-
|
|
116
|
-
# Stricter filters
|
|
117
|
-
npx tailwind-unwind analyze ./src --min-occurrences 10 --top 5
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
**Example output:**
|
|
121
|
-
|
|
122
|
-
```
|
|
123
|
-
📊 Tailwind Analysis Report
|
|
124
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
125
|
-
Files scanned: 47
|
|
126
|
-
Components with className: 312
|
|
127
|
-
Unique class combinations: 89
|
|
128
|
-
|
|
129
|
-
🏆 Top 10 most frequent combinations:
|
|
130
|
-
|
|
131
|
-
1. "flex items-center justify-between p-4"
|
|
132
|
-
Occurrences: 24
|
|
133
|
-
Suggestion: .page-header
|
|
134
|
-
Extractable: yes — use generate/apply
|
|
135
|
-
Found in: src/components/Header.tsx:12, src/layout/Toolbar.tsx:5 (+18 more)
|
|
136
|
-
|
|
137
|
-
💡 Potential code reduction: 38%
|
|
138
|
-
💡 Generate CSS: npx tailwind-unwind generate <path> --output styles.css
|
|
139
|
-
💡 Apply classes: npx tailwind-unwind apply <path> --output styles.css
|
|
53
|
+
npm install -g tailwind-unwind
|
|
140
54
|
```
|
|
141
55
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
---
|
|
56
|
+
## How the three commands differ
|
|
145
57
|
|
|
146
|
-
|
|
58
|
+
| Command | What it does |
|
|
59
|
+
|---------|--------------|
|
|
60
|
+
| `analyze` | Shows which class combinations repeat and where. Safe — read-only. |
|
|
61
|
+
| `generate` | Creates a CSS file with `@layer components` + `@apply`. Does not touch your `.tsx` files. |
|
|
62
|
+
| `apply` | Does what `generate` does **and** rewrites matching `className` in source files. |
|
|
147
63
|
|
|
148
|
-
|
|
64
|
+
**Important:** `analyze` looks for frequent patterns (including subsets). `generate` and `apply` only work with **exact duplicate** class strings that appear multiple times.
|
|
149
65
|
|
|
150
|
-
|
|
151
|
-
npx tailwind-unwind generate <path> --output <file.css>
|
|
152
|
-
```
|
|
66
|
+
In the analyze report, look for `Extractable: yes` — those patterns can be passed to `generate` / `apply`.
|
|
153
67
|
|
|
154
|
-
|
|
155
|
-
|------|---------|-------------|
|
|
156
|
-
| `--output <file>` | *(required)* | Output CSS file path |
|
|
157
|
-
| `--min-occurrences <n>` | `3` | Minimum occurrences (must appear **≥ n** times) |
|
|
158
|
-
| `--min-size <n>` | `2` | Minimum classes per set |
|
|
159
|
-
| `--max-size <n>` | `5` | Maximum classes per set |
|
|
160
|
-
| `--top <n>` | `10` | Max number of component classes to generate |
|
|
161
|
-
| `--prefix <name>` | `twu-` | Namespace prefix for generated classes |
|
|
68
|
+
## Typical workflow
|
|
162
69
|
|
|
163
70
|
```bash
|
|
164
|
-
|
|
165
|
-
npx tailwind-unwind
|
|
166
|
-
npx tailwind-unwind generate ./src --output styles.css --min-occurrences 2 --top 20
|
|
167
|
-
```
|
|
71
|
+
# Optional: create config from your project
|
|
72
|
+
npx tailwind-unwind init ./src
|
|
168
73
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
```css
|
|
172
|
-
/**
|
|
173
|
-
* Generated by tailwind-unwind
|
|
174
|
-
* Source: ./src
|
|
175
|
-
* Class prefix: twu-
|
|
176
|
-
*/
|
|
177
|
-
@layer components {
|
|
178
|
-
.twu-page-header {
|
|
179
|
-
@apply flex items-center justify-between p-4;
|
|
180
|
-
}
|
|
74
|
+
# Analyze → save report
|
|
75
|
+
npx tailwind-unwind analyze ./src --format json > report.json
|
|
181
76
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
77
|
+
# Generate only extractable patterns from the report
|
|
78
|
+
npx tailwind-unwind generate --from-report report.json --output src/styles/components.css
|
|
185
79
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
80
|
+
# Preview replacements, then apply
|
|
81
|
+
npx tailwind-unwind apply ./src --output src/styles/components.css --dry-run
|
|
82
|
+
npx tailwind-unwind apply ./src --output src/styles/components.css --prettier
|
|
190
83
|
```
|
|
191
84
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
---
|
|
195
|
-
|
|
196
|
-
### `apply`
|
|
85
|
+
## Configuration
|
|
197
86
|
|
|
198
|
-
|
|
87
|
+
Create `tailwind-unwind.config.json` manually or run `init`:
|
|
199
88
|
|
|
200
89
|
```bash
|
|
201
|
-
npx tailwind-unwind
|
|
90
|
+
npx tailwind-unwind init ./src
|
|
202
91
|
```
|
|
203
92
|
|
|
204
|
-
|
|
93
|
+
Also supported: `.tailwind-unwindrc`, `tailwind-unwind.config.ts` / `.js`.
|
|
205
94
|
|
|
206
|
-
|
|
207
|
-
|------|-------------|
|
|
208
|
-
| `--dry-run` | Preview replacements without writing files |
|
|
209
|
-
|
|
210
|
-
```bash
|
|
211
|
-
npx tailwind-unwind apply ./src --output styles.css --dry-run
|
|
212
|
-
npx tailwind-unwind apply ./src --output styles.css
|
|
213
|
-
```
|
|
95
|
+
Example — see [`tailwind-unwind.config.example.json`](tailwind-unwind.config.example.json).
|
|
214
96
|
|
|
215
|
-
|
|
97
|
+
Key options:
|
|
216
98
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
| `className={cn('flex', 'items-center', 'p-4')}` | ✅ (static args only) |
|
|
221
|
-
| `className={getClasses()}` | ❌ skipped |
|
|
222
|
-
| `` className={`flex ${active ? 'p-4' : ''}`} `` | ❌ skipped (dynamic) |
|
|
99
|
+
- `include` / `exclude` — which files to scan
|
|
100
|
+
- `names` — map utilities to your class names (`"flex p-4"` → `"toolbar"`)
|
|
101
|
+
- `analyze` / `generate` / `apply` — per-command settings
|
|
223
102
|
|
|
224
|
-
|
|
103
|
+
CLI flags override config values.
|
|
225
104
|
|
|
226
|
-
|
|
227
|
-
// before
|
|
228
|
-
<div className="flex items-center justify-between p-4">Header</div>
|
|
105
|
+
## What `apply` can replace
|
|
229
106
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
107
|
+
| Pattern | Supported? |
|
|
108
|
+
|---------|------------|
|
|
109
|
+
| `className="flex p-4 bg-blue"` | Yes |
|
|
110
|
+
| `className={cn('flex', 'p-4')}` | Yes |
|
|
111
|
+
| `className={cn('flex p-4', isActive && 'bg-blue')}` | Yes (static part only) |
|
|
112
|
+
| `` className={`flex p-4 ${x}`} `` | Yes (static part only) |
|
|
113
|
+
| `className={buttonVariants()}` | Yes (cva/tv, no arguments) |
|
|
114
|
+
| `className={getClasses()}` | No — skipped |
|
|
233
115
|
|
|
234
|
-
|
|
116
|
+
Parsed: `cn`, `clsx`, `classnames`, `twMerge`, `cva`, `tv`, template literals.
|
|
117
|
+
Class order does not matter (`flex p-4` = `p-4 flex`).
|
|
235
118
|
|
|
236
|
-
##
|
|
119
|
+
## Generated class names
|
|
237
120
|
|
|
238
|
-
|
|
121
|
+
Default prefix is `twu-` to avoid clashes with your existing styles:
|
|
239
122
|
|
|
240
|
-
|
|
|
241
|
-
|
|
123
|
+
| Repeated utilities | Becomes |
|
|
124
|
+
|--------------------|---------|
|
|
242
125
|
| `flex items-center justify-between p-4` | `twu-page-header` |
|
|
243
126
|
| `w-full object-cover rounded-lg` | `twu-media-cover` |
|
|
244
|
-
| `bg-blue-500 px-4 py-2 rounded-lg` | `twu-primary-button` |
|
|
245
|
-
| `grid grid-cols-3 gap-4` | `twu-card-grid` |
|
|
246
|
-
| `fixed inset-0 bg-black/50` | `twu-backdrop` |
|
|
247
|
-
|
|
248
|
-
Customize with `--prefix app-` or the `names` field in config:
|
|
249
|
-
|
|
250
|
-
```json
|
|
251
|
-
{
|
|
252
|
-
"names": {
|
|
253
|
-
"flex items-center justify-between p-4": "page-header"
|
|
254
|
-
},
|
|
255
|
-
"generate": { "prefix": "app-" }
|
|
256
|
-
}
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
→ `.app-page-header`
|
|
260
127
|
|
|
261
|
-
|
|
128
|
+
Override with `--prefix app-` or the `names` field in config.
|
|
262
129
|
|
|
263
|
-
##
|
|
130
|
+
## Useful flags
|
|
264
131
|
|
|
265
|
-
|
|
132
|
+
| Flag | Commands | Purpose |
|
|
133
|
+
|------|----------|---------|
|
|
134
|
+
| `--dry-run` | apply | Preview without writing files |
|
|
135
|
+
| `--prettier` | apply | Format changed files with Prettier |
|
|
136
|
+
| `--format json` | analyze, generate, apply | Output for CI / scripts |
|
|
137
|
+
| `--changed [ref]` | all | Only git-changed files |
|
|
138
|
+
| `--from-report <file>` | generate, apply | Use analyze JSON output |
|
|
139
|
+
| `--extractable-only` | generate, apply | Only patterns marked extractable |
|
|
140
|
+
| `--config <file>` | all | Custom config path |
|
|
141
|
+
| `--include` / `--exclude` | all | Filter files by glob |
|
|
266
142
|
|
|
267
|
-
|
|
268
|
-
- **Template literals:** static segments extracted; `${...}` expressions flagged
|
|
269
|
-
- **Merge utilities:** `cn()`, `clsx()`, `classnames()`, `twMerge()`, `cx()`
|
|
270
|
-
- **Dynamic expressions:** `className={getClasses()}` — warning, skipped
|
|
143
|
+
### Defaults
|
|
271
144
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
145
|
+
| | analyze | generate / apply |
|
|
146
|
+
|--|---------|------------------|
|
|
147
|
+
| `--min-occurrences` | 5 | 3 |
|
|
148
|
+
| `--prefix` | — | `twu-` |
|
|
275
149
|
|
|
276
150
|
## Programmatic API
|
|
277
151
|
|
|
278
152
|
```typescript
|
|
279
|
-
import {
|
|
280
|
-
|
|
281
|
-
generateCommand,
|
|
282
|
-
applyCommand,
|
|
283
|
-
walkSourceFiles,
|
|
284
|
-
parseFile,
|
|
285
|
-
findFrequentPatterns,
|
|
286
|
-
findRepeatedClassSets,
|
|
287
|
-
buildComponents,
|
|
288
|
-
normalizeClasses,
|
|
289
|
-
} from 'tailwind-unwind';
|
|
290
|
-
|
|
291
|
-
// Analyze
|
|
153
|
+
import { analyzeCommand, generateCommand, applyCommand, buildComponents } from 'tailwind-unwind';
|
|
154
|
+
|
|
292
155
|
await analyzeCommand('./src', { format: 'json' });
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Full exports: `walkSourceFiles`, `parseFile`, `findRepeatedClassSets`, `buildComponents`, `loadCommandOptions`, and more.
|
|
293
159
|
|
|
294
|
-
|
|
295
|
-
const files = await walkSourceFiles('./src');
|
|
296
|
-
const result = await parseFile(files[0]);
|
|
160
|
+
## GitHub Action
|
|
297
161
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
162
|
+
```yaml
|
|
163
|
+
- uses: AVPletnev/tailwind-unwind@v0.4.0
|
|
164
|
+
with:
|
|
165
|
+
command: analyze
|
|
166
|
+
path: ./src
|
|
167
|
+
format: json
|
|
302
168
|
```
|
|
303
169
|
|
|
304
|
-
|
|
170
|
+
See [`action.yml`](action.yml) for inputs.
|
|
305
171
|
|
|
306
172
|
## Development
|
|
307
173
|
|
|
308
174
|
```bash
|
|
309
175
|
git clone https://github.com/AVPletnev/tailwind-unwind.git
|
|
310
|
-
cd tailwind-unwind
|
|
311
|
-
npm install
|
|
312
|
-
npm run build # required before running CLI locally
|
|
313
|
-
npm test
|
|
314
|
-
|
|
315
|
-
node bin/index.js analyze ./test-project
|
|
316
|
-
node bin/index.js generate ./test-project --output styles.css
|
|
317
|
-
node bin/index.js apply ./test-project --output styles.css --dry-run
|
|
176
|
+
cd tailwind-unwind && npm install && npm run build && npm test
|
|
318
177
|
```
|
|
319
178
|
|
|
320
|
-
> **Note:** `bin/index.js` runs compiled code from `dist/`. Always run `npm run build` after changing source files.
|
|
321
|
-
|
|
322
|
-
---
|
|
323
|
-
|
|
324
179
|
## License
|
|
325
180
|
|
|
326
181
|
MIT — see [LICENSE](LICENSE).
|