tailwind-unwind 0.1.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/LICENSE +21 -0
- package/README.md +157 -0
- package/bin/index.js +6 -0
- package/dist/chunk-N7HD4T2I.js +1469 -0
- package/dist/chunk-N7HD4T2I.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +63 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +265 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AVPletnev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# tailwind-unwind
|
|
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.
|
|
4
|
+
|
|
5
|
+
**Repository:** [github.com/AVPletnev/tailwind-unwind](https://github.com/AVPletnev/tailwind-unwind)
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g tailwind-unwind
|
|
11
|
+
# or run without installing
|
|
12
|
+
npx tailwind-unwind analyze ./src
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx tailwind-unwind analyze <path>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### CLI options
|
|
22
|
+
|
|
23
|
+
| Flag | Default | Description |
|
|
24
|
+
|------|---------|-------------|
|
|
25
|
+
| `--min-occurrences <n>` | `5` | Minimum occurrences for a combination |
|
|
26
|
+
| `--min-size <n>` | `2` | Minimum classes per combination |
|
|
27
|
+
| `--max-size <n>` | `5` | Maximum classes per combination |
|
|
28
|
+
| `--top <n>` | `10` | Number of top combinations to show |
|
|
29
|
+
| `--format <type>` | `console` | Output format: `console` or `json` |
|
|
30
|
+
| `--no-dedupe-subsets` | — | Include subset combinations in results |
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# JSON report for CI
|
|
34
|
+
npx tailwind-unwind analyze ./src --format json
|
|
35
|
+
|
|
36
|
+
# Stricter filters
|
|
37
|
+
npx tailwind-unwind analyze ./src --min-occurrences 10 --top 5
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Scan a directory recursively for `.tsx`, `.jsx`, `.ts`, and `.js` files. The tool ignores `node_modules`, `.next`, `dist`, `build`, and `.git`.
|
|
41
|
+
|
|
42
|
+
### Generate CSS
|
|
43
|
+
|
|
44
|
+
Extract **exact duplicate className strings** into reusable component classes:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx tailwind-unwind generate ./src --output styles.css
|
|
48
|
+
# Custom namespace (default: twu-)
|
|
49
|
+
npx tailwind-unwind generate ./src --output styles.css --prefix app-
|
|
50
|
+
```
|
|
51
|
+
|
|
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:
|
|
55
|
+
|
|
56
|
+
```css
|
|
57
|
+
@layer components {
|
|
58
|
+
.twu-toolbar {
|
|
59
|
+
@apply flex items-center justify-between p-4;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.twu-media-cover {
|
|
63
|
+
@apply w-full h-auto object-cover rounded-lg;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Import the generated file in your global CSS (e.g. `globals.css`), then replace repeated `className` strings with the new classes.
|
|
69
|
+
|
|
70
|
+
Supports filter flags: `--min-occurrences`, `--min-size`, `--max-size`, `--top`.
|
|
71
|
+
|
|
72
|
+
### Apply (replace className in source)
|
|
73
|
+
|
|
74
|
+
Generate CSS **and** replace matching `className` strings in your `.tsx`/`.jsx` files:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# Preview changes without writing files
|
|
78
|
+
npx tailwind-unwind apply ./src --output styles.css --dry-run
|
|
79
|
+
|
|
80
|
+
# Apply replacements and write styles.css
|
|
81
|
+
npx tailwind-unwind apply ./src --output styles.css
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Only **exact static matches** are replaced (string literals and static `cn()`/`clsx()` calls). Dynamic expressions are left unchanged.
|
|
85
|
+
|
|
86
|
+
### Example output
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
📊 Tailwind Analysis Report
|
|
90
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
91
|
+
Files scanned: 5
|
|
92
|
+
Components with className: 18
|
|
93
|
+
Unique class combinations: 4
|
|
94
|
+
|
|
95
|
+
🏆 Top 10 most frequent combinations:
|
|
96
|
+
|
|
97
|
+
1. "flex items-center justify-between p-4"
|
|
98
|
+
Occurrences: 8
|
|
99
|
+
Suggestion: .flex-items-center-justify-between
|
|
100
|
+
|
|
101
|
+
2. "w-full h-auto object-cover rounded-lg"
|
|
102
|
+
Occurrences: 7
|
|
103
|
+
Suggestion: .w-full-h-auto-object-cover
|
|
104
|
+
|
|
105
|
+
💡 Potential code reduction: 42%
|
|
106
|
+
💡 Generate CSS: npx tailwind-unwind generate <path> --output styles.css
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## What it analyzes
|
|
110
|
+
|
|
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
|
|
115
|
+
|
|
116
|
+
Class combinations are normalized (order-independent): `flex p-4` and `p-4 flex` count as the same pattern.
|
|
117
|
+
|
|
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.
|
|
119
|
+
|
|
120
|
+
## Programmatic API
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import {
|
|
124
|
+
analyzeCommand,
|
|
125
|
+
walkSourceFiles,
|
|
126
|
+
parseFile,
|
|
127
|
+
findFrequentPatterns,
|
|
128
|
+
normalizeClasses,
|
|
129
|
+
} from 'tailwind-unwind';
|
|
130
|
+
|
|
131
|
+
const files = await walkSourceFiles('./src');
|
|
132
|
+
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
|
+
]);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Development
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
npm install
|
|
148
|
+
npm run build
|
|
149
|
+
npm test
|
|
150
|
+
node bin/index.js analyze ./test-project
|
|
151
|
+
node bin/index.js generate ./test-project --output styles.css
|
|
152
|
+
node bin/index.js apply ./test-project --output styles.css --dry-run
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
MIT — see [LICENSE](LICENSE).
|