tailwind-styled-v4 4.0.0 → 5.0.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.
- package/CHANGELOG.md +398 -0
- package/LICENSE +21 -0
- package/README.md +532 -0
- package/dist/analyzer.d.mts +114 -0
- package/dist/analyzer.d.ts +114 -0
- package/dist/analyzer.js +1555 -0
- package/dist/analyzer.js.map +1 -0
- package/dist/analyzer.mjs +1544 -0
- package/dist/analyzer.mjs.map +1 -0
- package/dist/animate.d.mts +46 -0
- package/dist/animate.d.ts +41 -112
- package/dist/animate.js +792 -235
- package/dist/animate.js.map +1 -1
- package/dist/animate.mjs +782 -0
- package/dist/animate.mjs.map +1 -0
- package/dist/atomic.d.mts +18 -0
- package/dist/atomic.d.ts +18 -0
- package/dist/atomic.js +191 -0
- package/dist/atomic.js.map +1 -0
- package/dist/atomic.mjs +185 -0
- package/dist/atomic.mjs.map +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +6063 -0
- package/dist/cli.js.map +1 -0
- package/dist/cli.mjs +6053 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/{compiler.d.cts → compiler.d.mts} +503 -210
- package/dist/compiler.d.ts +503 -210
- package/dist/compiler.js +1549 -566
- package/dist/compiler.js.map +1 -1
- package/dist/{compiler.cjs → compiler.mjs} +1476 -627
- package/dist/compiler.mjs.map +1 -0
- package/dist/dashboard.d.mts +272 -0
- package/dist/dashboard.d.ts +272 -0
- package/dist/dashboard.js +249 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/dashboard.mjs +239 -0
- package/dist/dashboard.mjs.map +1 -0
- package/dist/devtools.js +336 -211
- package/dist/devtools.js.map +1 -1
- package/dist/{devtools.cjs → devtools.mjs} +331 -220
- package/dist/devtools.mjs.map +1 -0
- package/dist/engine.d.mts +84 -0
- package/dist/engine.d.ts +84 -0
- package/dist/engine.js +3014 -0
- package/dist/engine.js.map +1 -0
- package/dist/engine.mjs +3005 -0
- package/dist/engine.mjs.map +1 -0
- package/dist/{index.d.cts → index.d.mts} +75 -4
- package/dist/index.d.ts +75 -4
- package/dist/index.js +1341 -149
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2162 -0
- package/dist/index.mjs.map +1 -0
- package/dist/liveTokenEngine-DYN3Zale.d.mts +34 -0
- package/dist/liveTokenEngine-DYN3Zale.d.ts +34 -0
- package/dist/next.d.mts +55 -0
- package/dist/next.d.ts +30 -20
- package/dist/next.js +6947 -149
- package/dist/next.js.map +1 -1
- package/dist/next.mjs +7050 -0
- package/dist/next.mjs.map +1 -0
- package/dist/plugin.d.mts +90 -0
- package/dist/plugin.d.ts +90 -0
- package/dist/plugin.js +185 -0
- package/dist/plugin.js.map +1 -0
- package/dist/plugin.mjs +174 -0
- package/dist/plugin.mjs.map +1 -0
- package/dist/pluginRegistry.d.mts +83 -0
- package/dist/pluginRegistry.d.ts +83 -0
- package/dist/pluginRegistry.js +303 -0
- package/dist/pluginRegistry.js.map +1 -0
- package/dist/pluginRegistry.mjs +298 -0
- package/dist/pluginRegistry.mjs.map +1 -0
- package/dist/{preset.d.cts → preset.d.mts} +29 -2
- package/dist/preset.d.ts +29 -2
- package/dist/preset.js +318 -21
- package/dist/preset.js.map +1 -1
- package/dist/preset.mjs +414 -0
- package/dist/preset.mjs.map +1 -0
- package/dist/rspack.d.mts +33 -0
- package/dist/rspack.d.ts +33 -0
- package/dist/rspack.js +55 -0
- package/dist/rspack.js.map +1 -0
- package/dist/rspack.mjs +45 -0
- package/dist/rspack.mjs.map +1 -0
- package/dist/runtime.d.mts +62 -0
- package/dist/runtime.d.ts +62 -0
- package/dist/runtime.js +207 -0
- package/dist/runtime.js.map +1 -0
- package/dist/runtime.mjs +188 -0
- package/dist/runtime.mjs.map +1 -0
- package/dist/runtimeCss.d.mts +65 -0
- package/dist/runtimeCss.d.ts +65 -0
- package/dist/runtimeCss.js +188 -0
- package/dist/runtimeCss.js.map +1 -0
- package/dist/runtimeCss.mjs +173 -0
- package/dist/runtimeCss.mjs.map +1 -0
- package/dist/scanner.d.mts +25 -0
- package/dist/scanner.d.ts +25 -0
- package/dist/scanner.js +717 -0
- package/dist/scanner.js.map +1 -0
- package/dist/scanner.mjs +703 -0
- package/dist/scanner.mjs.map +1 -0
- package/dist/shared.d.mts +85 -0
- package/dist/shared.d.ts +85 -0
- package/dist/shared.js +255 -0
- package/dist/shared.js.map +1 -0
- package/dist/shared.mjs +233 -0
- package/dist/shared.mjs.map +1 -0
- package/dist/storybookAddon.d.mts +108 -0
- package/dist/storybookAddon.d.ts +108 -0
- package/dist/storybookAddon.js +95 -0
- package/dist/storybookAddon.js.map +1 -0
- package/dist/storybookAddon.mjs +88 -0
- package/dist/storybookAddon.mjs.map +1 -0
- package/dist/svelte.d.mts +114 -0
- package/dist/svelte.d.ts +114 -0
- package/dist/svelte.js +67 -0
- package/dist/svelte.js.map +1 -0
- package/dist/svelte.mjs +59 -0
- package/dist/svelte.mjs.map +1 -0
- package/dist/testing.d.mts +185 -0
- package/dist/testing.d.ts +185 -0
- package/dist/testing.js +173 -0
- package/dist/testing.js.map +1 -0
- package/dist/testing.mjs +158 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/{theme.d.cts → theme.d.mts} +18 -11
- package/dist/theme.d.ts +18 -11
- package/dist/theme.js +205 -19
- package/dist/theme.js.map +1 -1
- package/dist/theme.mjs +311 -0
- package/dist/theme.mjs.map +1 -0
- package/dist/types-DXr2PmGP.d.mts +31 -0
- package/dist/types-DXr2PmGP.d.ts +31 -0
- package/dist/vite.d.mts +51 -0
- package/dist/vite.d.ts +35 -6
- package/dist/vite.js +4254 -57
- package/dist/vite.js.map +1 -1
- package/dist/vite.mjs +4281 -0
- package/dist/vite.mjs.map +1 -0
- package/dist/vue.d.mts +89 -0
- package/dist/vue.d.ts +89 -0
- package/dist/vue.js +104 -0
- package/dist/vue.js.map +1 -0
- package/dist/vue.mjs +96 -0
- package/dist/vue.mjs.map +1 -0
- package/package.json +173 -67
- package/dist/animate.cjs +0 -252
- package/dist/animate.cjs.map +0 -1
- package/dist/animate.d.cts +0 -117
- package/dist/astTransform-ua-eapqs.d.cts +0 -41
- package/dist/astTransform-ua-eapqs.d.ts +0 -41
- package/dist/compiler.cjs.map +0 -1
- package/dist/css.cjs +0 -71
- package/dist/css.cjs.map +0 -1
- package/dist/css.d.cts +0 -45
- package/dist/css.d.ts +0 -45
- package/dist/css.js +0 -62
- package/dist/css.js.map +0 -1
- package/dist/devtools.cjs.map +0 -1
- package/dist/index.cjs +0 -1058
- package/dist/index.cjs.map +0 -1
- package/dist/next.cjs +0 -268
- package/dist/next.cjs.map +0 -1
- package/dist/next.d.cts +0 -45
- package/dist/plugins.cjs +0 -396
- package/dist/plugins.cjs.map +0 -1
- package/dist/plugins.d.cts +0 -231
- package/dist/plugins.d.ts +0 -231
- package/dist/plugins.js +0 -381
- package/dist/plugins.js.map +0 -1
- package/dist/preset.cjs +0 -129
- package/dist/preset.cjs.map +0 -1
- package/dist/theme.cjs +0 -154
- package/dist/theme.cjs.map +0 -1
- package/dist/turbopackLoader.cjs +0 -2689
- package/dist/turbopackLoader.cjs.map +0 -1
- package/dist/turbopackLoader.d.cts +0 -22
- package/dist/turbopackLoader.d.ts +0 -22
- package/dist/turbopackLoader.js +0 -2681
- package/dist/turbopackLoader.js.map +0 -1
- package/dist/vite.cjs +0 -105
- package/dist/vite.cjs.map +0 -1
- package/dist/vite.d.cts +0 -22
- package/dist/webpackLoader.cjs +0 -2670
- package/dist/webpackLoader.cjs.map +0 -1
- package/dist/webpackLoader.d.cts +0 -24
- package/dist/webpackLoader.d.ts +0 -24
- package/dist/webpackLoader.js +0 -2662
- package/dist/webpackLoader.js.map +0 -1
- /package/dist/{devtools.d.cts → devtools.d.mts} +0 -0
package/README.md
ADDED
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# tailwind-styled-v4
|
|
4
|
+
|
|
5
|
+
### ⚡ Rust-powered Tailwind CSS untuk React
|
|
6
|
+
**Build-time compiler · Zero runtime overhead · RSC-aware · Next.js / Vite / Rspack**
|
|
7
|
+
|
|
8
|
+
[](https://npmjs.com/package/tailwind-styled-v4)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
[](https://rust-lang.org)
|
|
11
|
+
[](https://nodejs.org)
|
|
12
|
+
[](#)
|
|
13
|
+
[](https://bundlephobia.com/package/tailwind-styled-v4)
|
|
14
|
+
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Apa ini?
|
|
20
|
+
|
|
21
|
+
`tailwind-styled-v4` adalah library styling untuk React yang menggabungkan **utility-first** Tailwind CSS dengan **engine berbasis Rust**. Tulis komponen dengan `tw.button` atau `tw.div({ variants })` — compiler extract dan optimasi CSS di build time, bukan runtime.
|
|
22
|
+
|
|
23
|
+
**Perbandingan singkat:**
|
|
24
|
+
|
|
25
|
+
| | tailwind-styled-v4 | styled-components | Tailwind CSS biasa |
|
|
26
|
+
|---|---|---|---|
|
|
27
|
+
| Build-time CSS | ✅ | ❌ | ✅ |
|
|
28
|
+
| Runtime overhead | ~0 | ~15KB | ~0 |
|
|
29
|
+
| Variants API | ✅ | terbatas | ❌ |
|
|
30
|
+
| RSC support | ✅ otomatis | ❌ | ✅ manual |
|
|
31
|
+
| Engine | 🦀 Rust | JS | JS |
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Instalasi
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# npm
|
|
39
|
+
npm install tailwind-styled-v4
|
|
40
|
+
|
|
41
|
+
# Lalu jalankan setup otomatis
|
|
42
|
+
npx tw setup
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`npx tw setup` akan otomatis:
|
|
46
|
+
- Mendeteksi bundler (Next.js / Vite / Rspack)
|
|
47
|
+
- Meng-inject plugin ke `next.config.ts` / `vite.config.ts`
|
|
48
|
+
- Membuat `tailwind-styled.config.json`
|
|
49
|
+
- Menambahkan `@import "tailwindcss"` ke CSS entry
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Quick Start
|
|
54
|
+
|
|
55
|
+
### 1. Template Literal
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
import { tw } from "tailwind-styled-v4"
|
|
59
|
+
|
|
60
|
+
const Button = tw.button`
|
|
61
|
+
inline-flex items-center rounded-lg px-4 py-2
|
|
62
|
+
bg-blue-600 text-white font-medium
|
|
63
|
+
hover:bg-blue-700 transition
|
|
64
|
+
`
|
|
65
|
+
|
|
66
|
+
// Pakai seperti komponen biasa
|
|
67
|
+
<Button onClick={handleClick}>Klik saya</Button>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 2. Object Config + Variants
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
const Button = tw.button({
|
|
74
|
+
base: "inline-flex items-center rounded-lg px-4 py-2 font-medium transition",
|
|
75
|
+
variants: {
|
|
76
|
+
intent: {
|
|
77
|
+
primary: "bg-blue-600 text-white hover:bg-blue-700",
|
|
78
|
+
secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200",
|
|
79
|
+
danger: "bg-red-600 text-white hover:bg-red-700",
|
|
80
|
+
},
|
|
81
|
+
size: {
|
|
82
|
+
sm: "text-sm px-3 py-1.5",
|
|
83
|
+
md: "text-base px-4 py-2",
|
|
84
|
+
lg: "text-lg px-6 py-3",
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
defaultVariants: { intent: "primary", size: "md" },
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
// TypeScript tahu variant apa yang valid
|
|
91
|
+
<Button intent="primary" size="lg">Submit</Button>
|
|
92
|
+
<Button intent="danger">Hapus</Button>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 3. `.extend()` — Inheritance
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
// Turunan dari Button, tambah styling tanpa override base
|
|
99
|
+
const IconButton = Button.extend`
|
|
100
|
+
aspect-square justify-center rounded-full p-2
|
|
101
|
+
`
|
|
102
|
+
|
|
103
|
+
const LoadingButton = Button.extend`
|
|
104
|
+
opacity-60 cursor-wait
|
|
105
|
+
`
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 4. `cx()` — Conditional Class Merge
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
import { cx } from "tailwind-styled-v4"
|
|
112
|
+
|
|
113
|
+
function StatusDot({ online }: { online: boolean }) {
|
|
114
|
+
return (
|
|
115
|
+
<span className={cx(
|
|
116
|
+
"h-2.5 w-2.5 rounded-full",
|
|
117
|
+
online ? "bg-green-500" : "bg-gray-300"
|
|
118
|
+
)} />
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 5. `cv()` — Class Variants (headless)
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
import { cv } from "tailwind-styled-v4"
|
|
127
|
+
|
|
128
|
+
// Tanpa element HTML — untuk komponen custom
|
|
129
|
+
const alertStyles = cv({
|
|
130
|
+
base: "rounded-lg border p-4 text-sm",
|
|
131
|
+
variants: {
|
|
132
|
+
type: {
|
|
133
|
+
info: "border-blue-200 bg-blue-50 text-blue-800",
|
|
134
|
+
success: "border-green-200 bg-green-50 text-green-800",
|
|
135
|
+
error: "border-red-200 bg-red-50 text-red-800",
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
function Alert({ type, children }) {
|
|
141
|
+
return <div className={alertStyles({ type })}>{children}</div>
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 6. Sub-components (Inline)
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
// Semua dalam satu definition — tapi TIDAK mewarisi style base
|
|
149
|
+
const Card = tw.div`
|
|
150
|
+
flex flex-col p-4 rounded-xl
|
|
151
|
+
header { font-bold text-lg }
|
|
152
|
+
body { text-gray-600 }
|
|
153
|
+
footer { border-t pt-4 }
|
|
154
|
+
`
|
|
155
|
+
// Usage: <Card.header>Title</Card.header>
|
|
156
|
+
// CATATAN: Sub-components TIDAK mewarisi style base secara otomatis
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### 7. State Engine — Zero-JS State Management
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
// Tanpa React state — pakai data attribute langsung
|
|
163
|
+
const Button = tw.button({
|
|
164
|
+
base: "transition transform",
|
|
165
|
+
state: {
|
|
166
|
+
active: "bg-blue-600 scale-95",
|
|
167
|
+
loading: "opacity-70 cursor-wait",
|
|
168
|
+
disabled: "opacity-50 cursor-not-allowed",
|
|
169
|
+
},
|
|
170
|
+
})
|
|
171
|
+
// Usage: <Button data-active="true">Click</Button>
|
|
172
|
+
// Ketika data-active="true", style aktif otomatis tanpa re-render
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### 8. Container Query Engine
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
const Card = tw.div({
|
|
179
|
+
container: {
|
|
180
|
+
sm: "flex-col",
|
|
181
|
+
md: "flex-row",
|
|
182
|
+
lg: "grid grid-cols-2",
|
|
183
|
+
},
|
|
184
|
+
})
|
|
185
|
+
// Auto-generate container queries, tidak perlu tulis @container sendiri
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### 9. Live Token Engine — Dynamic CSS Variables
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
import { liveToken } from "tailwind-styled-v4"
|
|
192
|
+
|
|
193
|
+
const tokens = liveToken({
|
|
194
|
+
primary: "#3b82f6",
|
|
195
|
+
secondary: "#64748b",
|
|
196
|
+
radius: "0.5rem",
|
|
197
|
+
})
|
|
198
|
+
// Auto-generate CSS variables: --tw-primary, --tw-secondary, dll
|
|
199
|
+
// Subscribe perubahan: tokens.subscribe(callback)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### 10. tw.server — RSC-only Components
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
const Avatar = tw.server`rounded-full object-cover`
|
|
206
|
+
// Hanya render di server (Next.js App Router)
|
|
207
|
+
// Auto-inject 'use client' kalo diperlukan
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### 11. Component Wrapping — tw(ExistingComponent)
|
|
211
|
+
|
|
212
|
+
```tsx
|
|
213
|
+
// Wrap komponen manapun dengan styling tambahan
|
|
214
|
+
const StyledCard = tw(Card)`shadow-lg border`
|
|
215
|
+
const BigButton = tw(Button)`text-xl px-8 py-4`
|
|
216
|
+
// Bisa juga pakai .extend():
|
|
217
|
+
const IconButton = Button.extend`p-2 rounded-full`
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
### Pattern untuk "4 Anak yang Mewarisi Style Base"
|
|
223
|
+
|
|
224
|
+
Karena sub-components TIDAK mewarisi style base, gunakan salah satu pattern ini:
|
|
225
|
+
|
|
226
|
+
**Pattern A: Base Variable**
|
|
227
|
+
```tsx
|
|
228
|
+
const base = "flex flex-col p-4 rounded-xl"
|
|
229
|
+
const Card = tw.div`${base} bg-white shadow`
|
|
230
|
+
const CardHeader = tw.div`${base} font-bold`
|
|
231
|
+
const CardBody = tw.div`${base} text-gray-600`
|
|
232
|
+
const CardFooter = tw.div`${base} border-t`
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Pattern B: .extend()**
|
|
236
|
+
```tsx
|
|
237
|
+
const Card = tw.div`flex flex-col p-4 bg-white shadow`
|
|
238
|
+
const CardHeader = Card.extend`font-bold text-lg`
|
|
239
|
+
const CardBody = Card.extend`text-gray-600`
|
|
240
|
+
const CardFooter = Card.extend`border-t pt-4`
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Pattern C: tw() Wrapper**
|
|
244
|
+
```tsx
|
|
245
|
+
const base = "flex flex-col p-4 rounded-xl"
|
|
246
|
+
const Card = tw.div`${base} bg-white shadow`
|
|
247
|
+
const CardHeader = tw(Card)`font-bold text-lg`
|
|
248
|
+
const CardBody = tw(Card)`text-gray-600`
|
|
249
|
+
const CardFooter = tw(Card)`border-t pt-4`
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Bundler Integration
|
|
255
|
+
|
|
256
|
+
### Next.js (App Router)
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
// next.config.ts
|
|
260
|
+
import type { NextConfig } from "next"
|
|
261
|
+
import { withTailwindStyled } from "@tailwind-styled/next"
|
|
262
|
+
|
|
263
|
+
const nextConfig: NextConfig = {
|
|
264
|
+
reactStrictMode: true,
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export default withTailwindStyled(nextConfig)
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
```tsx
|
|
271
|
+
// app/page.tsx — Server Component, tidak perlu 'use client'
|
|
272
|
+
import { tw } from "tailwind-styled-v4"
|
|
273
|
+
|
|
274
|
+
// Compiler otomatis deteksi RSC boundary
|
|
275
|
+
const Hero = tw.section`py-24 text-center`
|
|
276
|
+
const Title = tw.h1`text-5xl font-bold text-gray-900`
|
|
277
|
+
|
|
278
|
+
export default function Page() {
|
|
279
|
+
return (
|
|
280
|
+
<Hero>
|
|
281
|
+
<Title>Hello from RSC</Title>
|
|
282
|
+
</Hero>
|
|
283
|
+
)
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Vite
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
// vite.config.ts
|
|
291
|
+
import { defineConfig } from "vite"
|
|
292
|
+
import react from "@vitejs/plugin-react"
|
|
293
|
+
import { tailwindStyled } from "@tailwind-styled/vite"
|
|
294
|
+
|
|
295
|
+
export default defineConfig({
|
|
296
|
+
plugins: [react(), tailwindStyled()],
|
|
297
|
+
})
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Rspack
|
|
301
|
+
|
|
302
|
+
```js
|
|
303
|
+
// rspack.config.mjs
|
|
304
|
+
import { defineConfig } from "@rspack/cli"
|
|
305
|
+
import { tailwindStyled } from "@tailwind-styled/rspack"
|
|
306
|
+
|
|
307
|
+
export default defineConfig({
|
|
308
|
+
entry: "./src/index.ts",
|
|
309
|
+
plugins: [tailwindStyled()],
|
|
310
|
+
})
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## CLI
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
# Setup otomatis (interaktif — pilih project type)
|
|
319
|
+
npx tw setup
|
|
320
|
+
|
|
321
|
+
# Verifikasi setup
|
|
322
|
+
npx tw preflight
|
|
323
|
+
|
|
324
|
+
# Analisis workspace
|
|
325
|
+
npx tw audit
|
|
326
|
+
|
|
327
|
+
# Benchmark performa
|
|
328
|
+
npx tw benchmark
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## Benchmark
|
|
334
|
+
|
|
335
|
+
Diukur di Debian Linux, Node.js 22, Rust 1.75.
|
|
336
|
+
|
|
337
|
+
| Operasi | tailwind-styled-v4 | Tailwind CSS (JS) | Speedup |
|
|
338
|
+
|---|---|---|---|
|
|
339
|
+
| Scan 1000 file | **0.8 ms** | ~340 ms | **~425×** |
|
|
340
|
+
| Compile 500 class | **0.02 ms** | ~1.2 ms | **~60×** |
|
|
341
|
+
| Parse class string | **0.010 ms** | ~0.8 ms | **~80×** |
|
|
342
|
+
| Cache read/write | **0.009 ms** | ~0.5 ms | **~55×** |
|
|
343
|
+
| Watch mode rebuild | **< 5 ms** | ~85 ms | **~17×** |
|
|
344
|
+
|
|
345
|
+
*Benchmark dijalankan via `npm run bench` (scripts/benchmark/run.mjs)*
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
## Arsitektur
|
|
350
|
+
|
|
351
|
+
```
|
|
352
|
+
tailwind-styled-v4/
|
|
353
|
+
├── native/ # Rust engine (N-API, 27 functions)
|
|
354
|
+
│ ├── src/lib.rs # Entry, 27 N-API exports
|
|
355
|
+
│ ├── src/oxc_parser.rs # Oxc AST + regex hybrid parser
|
|
356
|
+
│ ├── src/scan_cache.rs # DashMap in-memory cache
|
|
357
|
+
│ └── src/watcher.rs # notify v6 file watcher
|
|
358
|
+
│
|
|
359
|
+
├── packages/
|
|
360
|
+
│ ├── core/ # tw, cx, cv, cn — core API
|
|
361
|
+
│ ├── compiler/ # AST transform, CSS generation
|
|
362
|
+
│ ├── scanner/ # File scanner (Rust-backed)
|
|
363
|
+
│ ├── engine/ # Build engine, incremental diff
|
|
364
|
+
│ ├── animate/ # Animation DSL
|
|
365
|
+
│ ├── theme/ # Multi-theme engine
|
|
366
|
+
│ ├── plugin/ # Plugin system
|
|
367
|
+
│ ├── cli/ # CLI (tw setup, tw audit, dll)
|
|
368
|
+
│ ├── next/ # Next.js adapter
|
|
369
|
+
│ ├── vite/ # Vite adapter
|
|
370
|
+
│ └── rspack/ # Rspack adapter
|
|
371
|
+
│
|
|
372
|
+
└── examples/
|
|
373
|
+
├── vite/ # Vite demo
|
|
374
|
+
├── vite-react/ # Vite React demo (dark mode)
|
|
375
|
+
├── standar-config-next-js-app/ # Next.js App Router demo
|
|
376
|
+
└── rspack/ # Rspack + Node.js demo
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## Development
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
# Clone
|
|
385
|
+
git clone https://github.com/Dictionar32/tailwind-styled-v4.git
|
|
386
|
+
cd tailwind-styled-v4
|
|
387
|
+
|
|
388
|
+
# Install dependencies
|
|
389
|
+
npm install
|
|
390
|
+
|
|
391
|
+
# Build Rust binary + semua packages
|
|
392
|
+
npm run build
|
|
393
|
+
|
|
394
|
+
# Test (Rust + JS)
|
|
395
|
+
npm run test
|
|
396
|
+
|
|
397
|
+
# Dev mode (watch all packages)
|
|
398
|
+
npm run dev
|
|
399
|
+
|
|
400
|
+
# Benchmark
|
|
401
|
+
npm run bench
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
**Requirements:**
|
|
405
|
+
- Node.js 20+
|
|
406
|
+
- Rust 1.75+ (untuk build dari source)
|
|
407
|
+
- Pre-built binary sudah disertakan untuk Linux x64
|
|
408
|
+
|
|
409
|
+
---
|
|
410
|
+
|
|
411
|
+
## Contributing
|
|
412
|
+
|
|
413
|
+
PR dan issue sangat welcome! Baca [CONTRIBUTING.md](CONTRIBUTING.md) untuk panduan.
|
|
414
|
+
|
|
415
|
+
Prioritas saat ini:
|
|
416
|
+
- [ ] macOS & Windows pre-built binary
|
|
417
|
+
- [ ] Docs website
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## License
|
|
422
|
+
|
|
423
|
+
[MIT](LICENSE) © Dictionar32
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## Engine Architecture (v4.5 Platform Overhaul)
|
|
428
|
+
|
|
429
|
+
Sprint 6–10 membawa perombakan arsitektur besar pada engine. Berikut ringkasannya.
|
|
430
|
+
|
|
431
|
+
### 🦀 Rust-backed pipeline
|
|
432
|
+
|
|
433
|
+
| Komponen | Implementasi | Fallback |
|
|
434
|
+
|---|---|---|
|
|
435
|
+
| Class scanner | `scan_workspace` (Rust) | JS `scanWorkspace` |
|
|
436
|
+
| Persistent cache | `cache_read/write` (Rust) | JS `ScanCache` |
|
|
437
|
+
| Incremental diff | `process_file_change` + DashMap | JS class set diff |
|
|
438
|
+
| File watcher | `notify` crate via polling IPC | Node.js `fs.watch` |
|
|
439
|
+
| Class analyzer | `analyze_classes` (Rust) | — |
|
|
440
|
+
|
|
441
|
+
### ⚡ Performance
|
|
442
|
+
|
|
443
|
+
- Cold start scan: **<10ms** (was >100ms) via persistent Rust cache
|
|
444
|
+
- Incremental update: **~0ms** untuk unchanged files
|
|
445
|
+
- Watch idle CPU: **~66% reduction** (500ms poll interval)
|
|
446
|
+
- Cache hit rate: typically **>95%** pada incremental dev
|
|
447
|
+
|
|
448
|
+
### 🔌 Platform Adapters
|
|
449
|
+
|
|
450
|
+
Semua adapter (Next.js, Vite, Rspack) sekarang **bundle compiler secara inline** — user tidak perlu menginstall `@tailwind-styled/compiler` secara terpisah.
|
|
451
|
+
|
|
452
|
+
```ts
|
|
453
|
+
// next.config.ts
|
|
454
|
+
import { withTailwindStyled } from 'tailwind-styled-v4/next'
|
|
455
|
+
export default withTailwindStyled()(nextConfig)
|
|
456
|
+
|
|
457
|
+
// vite.config.ts
|
|
458
|
+
import { tailwindStyledPlugin } from 'tailwind-styled-v4/vite'
|
|
459
|
+
export default defineConfig({ plugins: [tailwindStyledPlugin()] })
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
`preserveImports: true` diset di semua loader — `cv`, `cx`, `cn` tetap tersedia di output.
|
|
463
|
+
|
|
464
|
+
### 📊 Developer Tooling
|
|
465
|
+
|
|
466
|
+
```bash
|
|
467
|
+
# Analisis workspace (Rust engine)
|
|
468
|
+
tw analyze .
|
|
469
|
+
tw stats .
|
|
470
|
+
|
|
471
|
+
# Dashboard metrics real-time
|
|
472
|
+
tw dashboard
|
|
473
|
+
|
|
474
|
+
# Watch mode incremental
|
|
475
|
+
tw watch .
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
DevTools overlay (`Ctrl+Shift+D`) menampilkan:
|
|
479
|
+
- **Inspector** — hover element → lihat variant props & classes
|
|
480
|
+
- **State** — reactive state components
|
|
481
|
+
- **Container** — container query breakpoints aktif
|
|
482
|
+
- **Tokens** — live token editor (perubahan instan)
|
|
483
|
+
- **Analyzer** — DOM scan + engine metrics dari dashboard
|
|
484
|
+
|
|
485
|
+
### 🖥️ Studio Desktop (Electron)
|
|
486
|
+
|
|
487
|
+
```bash
|
|
488
|
+
# Dev mode
|
|
489
|
+
npm run dev -w @tailwind-styled/studio-desktop
|
|
490
|
+
|
|
491
|
+
# Build distribusi
|
|
492
|
+
npm run build:mac # macOS DMG + ZIP
|
|
493
|
+
npm run build:win # Windows NSIS installer
|
|
494
|
+
npm run build:linux # AppImage + DEB
|
|
495
|
+
npm run build:all # Semua platform sekaligus
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
Engine tersedia langsung dari renderer via `window.studioDesktop`:
|
|
499
|
+
|
|
500
|
+
```js
|
|
501
|
+
const result = await window.studioDesktop.engineBuild()
|
|
502
|
+
// { ok: true, totalFiles: 42, uniqueClasses: 312, cssLength: 18540 }
|
|
503
|
+
|
|
504
|
+
window.studioDesktop.onEngineEvent((event) => {
|
|
505
|
+
if (event.type === 'change') console.log('Rebuilt:', event.result.css.length, 'bytes')
|
|
506
|
+
})
|
|
507
|
+
await window.studioDesktop.engineWatchStart()
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### 🧪 Testing
|
|
511
|
+
|
|
512
|
+
```ts
|
|
513
|
+
import { expectEngineMetrics, toHaveEngineMetrics, tailwindMatchersWithMetrics } from '@tailwind-styled/testing'
|
|
514
|
+
|
|
515
|
+
// Assertion langsung
|
|
516
|
+
expectEngineMetrics(metrics, { minFiles: 10, maxBuildTimeMs: 500, cacheHitRateMin: 0.9 })
|
|
517
|
+
|
|
518
|
+
// Vitest/Jest custom matcher
|
|
519
|
+
expect.extend(tailwindMatchersWithMetrics)
|
|
520
|
+
expect(metrics).toHaveEngineMetrics({ minFiles: 5 })
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### 🔧 Environment Variables
|
|
524
|
+
|
|
525
|
+
| Variable | Default | Deskripsi |
|
|
526
|
+
|---|---|---|
|
|
527
|
+
| `TWS_LOG_LEVEL` | `info` | `debug\|info\|warn\|error\|silent` |
|
|
528
|
+
| `TWS_NO_NATIVE` | `0` | `1` = paksa JS fallback |
|
|
529
|
+
| `TWS_NO_RUST` | `0` | Alias untuk `TWS_NO_NATIVE` |
|
|
530
|
+
| `TWS_DEBUG_SCANNER` | `0` | `1` = aktifkan scanner debug logs |
|
|
531
|
+
| `STUDIO_PORT` | `3030` | Port studio server |
|
|
532
|
+
| `STUDIO_VERBOSE` | tidak ada | Tampilkan stdout/stderr studio server |
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { ScanWorkspaceOptions, ScanWorkspaceResult } from './scanner.mjs';
|
|
2
|
+
|
|
3
|
+
interface ClassUsage {
|
|
4
|
+
name: string;
|
|
5
|
+
count: number;
|
|
6
|
+
isUnused?: boolean;
|
|
7
|
+
isConflict?: boolean;
|
|
8
|
+
}
|
|
9
|
+
interface ClassConflict {
|
|
10
|
+
className: string;
|
|
11
|
+
variants: string[];
|
|
12
|
+
classes: string[];
|
|
13
|
+
message: string;
|
|
14
|
+
}
|
|
15
|
+
interface AnalyzerSemanticReport {
|
|
16
|
+
unusedClasses: ClassUsage[];
|
|
17
|
+
unknownClasses: ClassUsage[];
|
|
18
|
+
conflicts: ClassConflict[];
|
|
19
|
+
tailwindConfig?: {
|
|
20
|
+
path: string;
|
|
21
|
+
loaded: boolean;
|
|
22
|
+
safelistCount: number;
|
|
23
|
+
customUtilityCount: number;
|
|
24
|
+
warning?: string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
interface AnalyzerReport {
|
|
28
|
+
root: string;
|
|
29
|
+
totalFiles: number;
|
|
30
|
+
uniqueClassCount: number;
|
|
31
|
+
totalClassOccurrences: number;
|
|
32
|
+
classStats: {
|
|
33
|
+
all: ClassUsage[];
|
|
34
|
+
top: ClassUsage[];
|
|
35
|
+
frequent: ClassUsage[];
|
|
36
|
+
unique: ClassUsage[];
|
|
37
|
+
distribution: Record<string, number>;
|
|
38
|
+
};
|
|
39
|
+
/** All classes found, useful for Tailwind safelist. */
|
|
40
|
+
safelist: string[];
|
|
41
|
+
semantic?: AnalyzerSemanticReport;
|
|
42
|
+
}
|
|
43
|
+
interface AnalyzerOptions {
|
|
44
|
+
scanner?: ScanWorkspaceOptions;
|
|
45
|
+
classStats?: {
|
|
46
|
+
top?: number;
|
|
47
|
+
frequentThreshold?: number;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Enable semantic reporting. Provide `tailwindConfigPath` to override config lookup.
|
|
51
|
+
* Relative paths are resolved from `root` in `analyzeWorkspace`.
|
|
52
|
+
*/
|
|
53
|
+
semantic?: boolean | {
|
|
54
|
+
tailwindConfigPath?: string;
|
|
55
|
+
};
|
|
56
|
+
includeClass?: (className: string) => boolean;
|
|
57
|
+
}
|
|
58
|
+
interface ClassToCssOptions {
|
|
59
|
+
prefix?: string | null;
|
|
60
|
+
strict?: boolean;
|
|
61
|
+
}
|
|
62
|
+
interface ClassToCssResult {
|
|
63
|
+
inputClasses: string[];
|
|
64
|
+
css: string;
|
|
65
|
+
declarations: string;
|
|
66
|
+
resolvedClasses: string[];
|
|
67
|
+
unknownClasses: string[];
|
|
68
|
+
sizeBytes: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
declare function collectClassCounts(scan: ScanWorkspaceResult): Map<string, number>;
|
|
72
|
+
declare function buildDistribution(usages: ClassUsage[]): Record<string, number>;
|
|
73
|
+
/**
|
|
74
|
+
* Analyze Tailwind class usage in a workspace and return usage statistics.
|
|
75
|
+
* Set `semantic.tailwindConfigPath` to override Tailwind config lookup.
|
|
76
|
+
* @example
|
|
77
|
+
* const report = await analyzeWorkspace("./src", {
|
|
78
|
+
* classStats: { top: 20, frequentThreshold: 2 },
|
|
79
|
+
* semantic: { tailwindConfigPath: "tailwind.config.js" },
|
|
80
|
+
* })
|
|
81
|
+
*/
|
|
82
|
+
declare function analyzeWorkspace(root: string, options?: AnalyzerOptions): Promise<AnalyzerReport>;
|
|
83
|
+
|
|
84
|
+
declare function normalizeClassInput(input: string | string[]): string[];
|
|
85
|
+
/**
|
|
86
|
+
* Convert Tailwind class input into atomic CSS output via native binding.
|
|
87
|
+
* @example
|
|
88
|
+
* const css = await classToCss("opacity-0 translate-y-2", { strict: true })
|
|
89
|
+
*/
|
|
90
|
+
declare function classToCss(input: string | string[], options?: ClassToCssOptions): Promise<ClassToCssResult>;
|
|
91
|
+
|
|
92
|
+
declare function splitVariantAndBase(className: string): {
|
|
93
|
+
variantKey: string;
|
|
94
|
+
base: string;
|
|
95
|
+
};
|
|
96
|
+
declare function resolveConflictGroup(base: string): string | null;
|
|
97
|
+
declare function utilityPrefix(baseClass: string): string;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @tailwind-styled/analyzer v5
|
|
101
|
+
*
|
|
102
|
+
* Native-first async analyzer with semantic reporting.
|
|
103
|
+
*/
|
|
104
|
+
|
|
105
|
+
declare const __internal: {
|
|
106
|
+
normalizeClassInput: typeof normalizeClassInput;
|
|
107
|
+
splitVariantAndBase: typeof splitVariantAndBase;
|
|
108
|
+
resolveConflictGroup: typeof resolveConflictGroup;
|
|
109
|
+
collectClassCounts: typeof collectClassCounts;
|
|
110
|
+
buildDistribution: typeof buildDistribution;
|
|
111
|
+
utilityPrefix: typeof utilityPrefix;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export { type AnalyzerOptions, type AnalyzerReport, type AnalyzerSemanticReport, type ClassConflict, type ClassToCssOptions, type ClassToCssResult, type ClassUsage, __internal, analyzeWorkspace, classToCss };
|