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.
Files changed (194) hide show
  1. package/CHANGELOG.md +398 -0
  2. package/LICENSE +21 -0
  3. package/README.md +532 -0
  4. package/dist/analyzer.d.mts +114 -0
  5. package/dist/analyzer.d.ts +114 -0
  6. package/dist/analyzer.js +1555 -0
  7. package/dist/analyzer.js.map +1 -0
  8. package/dist/analyzer.mjs +1544 -0
  9. package/dist/analyzer.mjs.map +1 -0
  10. package/dist/animate.d.mts +46 -0
  11. package/dist/animate.d.ts +41 -112
  12. package/dist/animate.js +792 -235
  13. package/dist/animate.js.map +1 -1
  14. package/dist/animate.mjs +782 -0
  15. package/dist/animate.mjs.map +1 -0
  16. package/dist/atomic.d.mts +18 -0
  17. package/dist/atomic.d.ts +18 -0
  18. package/dist/atomic.js +191 -0
  19. package/dist/atomic.js.map +1 -0
  20. package/dist/atomic.mjs +185 -0
  21. package/dist/atomic.mjs.map +1 -0
  22. package/dist/cli.d.mts +1 -0
  23. package/dist/cli.d.ts +1 -0
  24. package/dist/cli.js +6063 -0
  25. package/dist/cli.js.map +1 -0
  26. package/dist/cli.mjs +6053 -0
  27. package/dist/cli.mjs.map +1 -0
  28. package/dist/{compiler.d.cts → compiler.d.mts} +503 -210
  29. package/dist/compiler.d.ts +503 -210
  30. package/dist/compiler.js +1549 -566
  31. package/dist/compiler.js.map +1 -1
  32. package/dist/{compiler.cjs → compiler.mjs} +1476 -627
  33. package/dist/compiler.mjs.map +1 -0
  34. package/dist/dashboard.d.mts +272 -0
  35. package/dist/dashboard.d.ts +272 -0
  36. package/dist/dashboard.js +249 -0
  37. package/dist/dashboard.js.map +1 -0
  38. package/dist/dashboard.mjs +239 -0
  39. package/dist/dashboard.mjs.map +1 -0
  40. package/dist/devtools.js +336 -211
  41. package/dist/devtools.js.map +1 -1
  42. package/dist/{devtools.cjs → devtools.mjs} +331 -220
  43. package/dist/devtools.mjs.map +1 -0
  44. package/dist/engine.d.mts +84 -0
  45. package/dist/engine.d.ts +84 -0
  46. package/dist/engine.js +3014 -0
  47. package/dist/engine.js.map +1 -0
  48. package/dist/engine.mjs +3005 -0
  49. package/dist/engine.mjs.map +1 -0
  50. package/dist/{index.d.cts → index.d.mts} +75 -4
  51. package/dist/index.d.ts +75 -4
  52. package/dist/index.js +1341 -149
  53. package/dist/index.js.map +1 -1
  54. package/dist/index.mjs +2162 -0
  55. package/dist/index.mjs.map +1 -0
  56. package/dist/liveTokenEngine-DYN3Zale.d.mts +34 -0
  57. package/dist/liveTokenEngine-DYN3Zale.d.ts +34 -0
  58. package/dist/next.d.mts +55 -0
  59. package/dist/next.d.ts +30 -20
  60. package/dist/next.js +6947 -149
  61. package/dist/next.js.map +1 -1
  62. package/dist/next.mjs +7050 -0
  63. package/dist/next.mjs.map +1 -0
  64. package/dist/plugin.d.mts +90 -0
  65. package/dist/plugin.d.ts +90 -0
  66. package/dist/plugin.js +185 -0
  67. package/dist/plugin.js.map +1 -0
  68. package/dist/plugin.mjs +174 -0
  69. package/dist/plugin.mjs.map +1 -0
  70. package/dist/pluginRegistry.d.mts +83 -0
  71. package/dist/pluginRegistry.d.ts +83 -0
  72. package/dist/pluginRegistry.js +303 -0
  73. package/dist/pluginRegistry.js.map +1 -0
  74. package/dist/pluginRegistry.mjs +298 -0
  75. package/dist/pluginRegistry.mjs.map +1 -0
  76. package/dist/{preset.d.cts → preset.d.mts} +29 -2
  77. package/dist/preset.d.ts +29 -2
  78. package/dist/preset.js +318 -21
  79. package/dist/preset.js.map +1 -1
  80. package/dist/preset.mjs +414 -0
  81. package/dist/preset.mjs.map +1 -0
  82. package/dist/rspack.d.mts +33 -0
  83. package/dist/rspack.d.ts +33 -0
  84. package/dist/rspack.js +55 -0
  85. package/dist/rspack.js.map +1 -0
  86. package/dist/rspack.mjs +45 -0
  87. package/dist/rspack.mjs.map +1 -0
  88. package/dist/runtime.d.mts +62 -0
  89. package/dist/runtime.d.ts +62 -0
  90. package/dist/runtime.js +207 -0
  91. package/dist/runtime.js.map +1 -0
  92. package/dist/runtime.mjs +188 -0
  93. package/dist/runtime.mjs.map +1 -0
  94. package/dist/runtimeCss.d.mts +65 -0
  95. package/dist/runtimeCss.d.ts +65 -0
  96. package/dist/runtimeCss.js +188 -0
  97. package/dist/runtimeCss.js.map +1 -0
  98. package/dist/runtimeCss.mjs +173 -0
  99. package/dist/runtimeCss.mjs.map +1 -0
  100. package/dist/scanner.d.mts +25 -0
  101. package/dist/scanner.d.ts +25 -0
  102. package/dist/scanner.js +717 -0
  103. package/dist/scanner.js.map +1 -0
  104. package/dist/scanner.mjs +703 -0
  105. package/dist/scanner.mjs.map +1 -0
  106. package/dist/shared.d.mts +85 -0
  107. package/dist/shared.d.ts +85 -0
  108. package/dist/shared.js +255 -0
  109. package/dist/shared.js.map +1 -0
  110. package/dist/shared.mjs +233 -0
  111. package/dist/shared.mjs.map +1 -0
  112. package/dist/storybookAddon.d.mts +108 -0
  113. package/dist/storybookAddon.d.ts +108 -0
  114. package/dist/storybookAddon.js +95 -0
  115. package/dist/storybookAddon.js.map +1 -0
  116. package/dist/storybookAddon.mjs +88 -0
  117. package/dist/storybookAddon.mjs.map +1 -0
  118. package/dist/svelte.d.mts +114 -0
  119. package/dist/svelte.d.ts +114 -0
  120. package/dist/svelte.js +67 -0
  121. package/dist/svelte.js.map +1 -0
  122. package/dist/svelte.mjs +59 -0
  123. package/dist/svelte.mjs.map +1 -0
  124. package/dist/testing.d.mts +185 -0
  125. package/dist/testing.d.ts +185 -0
  126. package/dist/testing.js +173 -0
  127. package/dist/testing.js.map +1 -0
  128. package/dist/testing.mjs +158 -0
  129. package/dist/testing.mjs.map +1 -0
  130. package/dist/{theme.d.cts → theme.d.mts} +18 -11
  131. package/dist/theme.d.ts +18 -11
  132. package/dist/theme.js +205 -19
  133. package/dist/theme.js.map +1 -1
  134. package/dist/theme.mjs +311 -0
  135. package/dist/theme.mjs.map +1 -0
  136. package/dist/types-DXr2PmGP.d.mts +31 -0
  137. package/dist/types-DXr2PmGP.d.ts +31 -0
  138. package/dist/vite.d.mts +51 -0
  139. package/dist/vite.d.ts +35 -6
  140. package/dist/vite.js +4254 -57
  141. package/dist/vite.js.map +1 -1
  142. package/dist/vite.mjs +4281 -0
  143. package/dist/vite.mjs.map +1 -0
  144. package/dist/vue.d.mts +89 -0
  145. package/dist/vue.d.ts +89 -0
  146. package/dist/vue.js +104 -0
  147. package/dist/vue.js.map +1 -0
  148. package/dist/vue.mjs +96 -0
  149. package/dist/vue.mjs.map +1 -0
  150. package/package.json +173 -67
  151. package/dist/animate.cjs +0 -252
  152. package/dist/animate.cjs.map +0 -1
  153. package/dist/animate.d.cts +0 -117
  154. package/dist/astTransform-ua-eapqs.d.cts +0 -41
  155. package/dist/astTransform-ua-eapqs.d.ts +0 -41
  156. package/dist/compiler.cjs.map +0 -1
  157. package/dist/css.cjs +0 -71
  158. package/dist/css.cjs.map +0 -1
  159. package/dist/css.d.cts +0 -45
  160. package/dist/css.d.ts +0 -45
  161. package/dist/css.js +0 -62
  162. package/dist/css.js.map +0 -1
  163. package/dist/devtools.cjs.map +0 -1
  164. package/dist/index.cjs +0 -1058
  165. package/dist/index.cjs.map +0 -1
  166. package/dist/next.cjs +0 -268
  167. package/dist/next.cjs.map +0 -1
  168. package/dist/next.d.cts +0 -45
  169. package/dist/plugins.cjs +0 -396
  170. package/dist/plugins.cjs.map +0 -1
  171. package/dist/plugins.d.cts +0 -231
  172. package/dist/plugins.d.ts +0 -231
  173. package/dist/plugins.js +0 -381
  174. package/dist/plugins.js.map +0 -1
  175. package/dist/preset.cjs +0 -129
  176. package/dist/preset.cjs.map +0 -1
  177. package/dist/theme.cjs +0 -154
  178. package/dist/theme.cjs.map +0 -1
  179. package/dist/turbopackLoader.cjs +0 -2689
  180. package/dist/turbopackLoader.cjs.map +0 -1
  181. package/dist/turbopackLoader.d.cts +0 -22
  182. package/dist/turbopackLoader.d.ts +0 -22
  183. package/dist/turbopackLoader.js +0 -2681
  184. package/dist/turbopackLoader.js.map +0 -1
  185. package/dist/vite.cjs +0 -105
  186. package/dist/vite.cjs.map +0 -1
  187. package/dist/vite.d.cts +0 -22
  188. package/dist/webpackLoader.cjs +0 -2670
  189. package/dist/webpackLoader.cjs.map +0 -1
  190. package/dist/webpackLoader.d.cts +0 -24
  191. package/dist/webpackLoader.d.ts +0 -24
  192. package/dist/webpackLoader.js +0 -2662
  193. package/dist/webpackLoader.js.map +0 -1
  194. /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
+ [![npm](https://img.shields.io/npm/v/tailwind-styled-v4?color=blue)](https://npmjs.com/package/tailwind-styled-v4)
9
+ [![license](https://img.shields.io/npm/l/tailwind-styled-v4)](LICENSE)
10
+ [![Rust](https://img.shields.io/badge/Rust-1.75+-orange?logo=rust)](https://rust-lang.org)
11
+ [![Node](https://img.shields.io/badge/Node.js-20+-green?logo=node.js)](https://nodejs.org)
12
+ [![test](https://img.shields.io/badge/tests-84%2F86%20passing-brightgreen)](#)
13
+ [![bundle](https://img.shields.io/badge/runtime-~4.5kb-green)](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 };