@cwcss/crosswind 0.1.5 → 0.1.6
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.md +21 -0
- package/README.md +390 -0
- package/dist/build.d.ts +24 -0
- package/dist/config.d.ts +5 -0
- package/dist/generator.d.ts +31 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +12798 -0
- package/dist/parser.d.ts +42 -0
- package/dist/plugin.d.ts +22 -0
- package/dist/preflight-forms.d.ts +5 -0
- package/dist/preflight.d.ts +2 -0
- package/dist/rules-advanced.d.ts +27 -0
- package/dist/rules-effects.d.ts +25 -0
- package/dist/rules-forms.d.ts +7 -0
- package/dist/rules-grid.d.ts +13 -0
- package/dist/rules-interactivity.d.ts +41 -0
- package/dist/rules-layout.d.ts +26 -0
- package/dist/rules-transforms.d.ts +33 -0
- package/dist/rules-typography.d.ts +41 -0
- package/dist/rules.d.ts +39 -0
- package/dist/scanner.d.ts +18 -0
- package/dist/transformer-compile-class.d.ts +37 -0
- package/{src/types.ts → dist/types.d.ts} +17 -86
- package/package.json +1 -1
- package/PLUGIN.md +0 -235
- package/benchmark/framework-comparison.bench.ts +0 -850
- package/bin/cli.ts +0 -365
- package/bin/crosswind +0 -0
- package/bin/headwind +0 -0
- package/build.ts +0 -8
- package/crosswind.config.ts +0 -9
- package/example/comprehensive.html +0 -70
- package/example/index.html +0 -21
- package/example/output.css +0 -236
- package/examples/plugin/README.md +0 -112
- package/examples/plugin/build.ts +0 -32
- package/examples/plugin/src/index.html +0 -34
- package/examples/plugin/src/index.ts +0 -7
- package/headwind +0 -2
- package/src/build.ts +0 -101
- package/src/config.ts +0 -529
- package/src/generator.ts +0 -2173
- package/src/index.ts +0 -10
- package/src/parser.ts +0 -1471
- package/src/plugin.ts +0 -118
- package/src/preflight-forms.ts +0 -229
- package/src/preflight.ts +0 -388
- package/src/rules-advanced.ts +0 -477
- package/src/rules-effects.ts +0 -461
- package/src/rules-forms.ts +0 -103
- package/src/rules-grid.ts +0 -241
- package/src/rules-interactivity.ts +0 -525
- package/src/rules-layout.ts +0 -385
- package/src/rules-transforms.ts +0 -412
- package/src/rules-typography.ts +0 -486
- package/src/rules.ts +0 -809
- package/src/scanner.ts +0 -84
- package/src/transformer-compile-class.ts +0 -275
- package/test/advanced-features.test.ts +0 -911
- package/test/arbitrary.test.ts +0 -396
- package/test/attributify.test.ts +0 -592
- package/test/bracket-syntax.test.ts +0 -1133
- package/test/build.test.ts +0 -99
- package/test/colors.test.ts +0 -934
- package/test/flexbox.test.ts +0 -669
- package/test/generator.test.ts +0 -597
- package/test/grid.test.ts +0 -584
- package/test/layout.test.ts +0 -404
- package/test/modifiers.test.ts +0 -417
- package/test/parser.test.ts +0 -564
- package/test/performance-regression.test.ts +0 -376
- package/test/performance.test.ts +0 -568
- package/test/plugin.test.ts +0 -160
- package/test/scanner.test.ts +0 -94
- package/test/sizing.test.ts +0 -481
- package/test/spacing.test.ts +0 -394
- package/test/transformer-compile-class.test.ts +0 -287
- package/test/transforms.test.ts +0 -448
- package/test/typography.test.ts +0 -632
- package/test/variants-form-states.test.ts +0 -225
- package/test/variants-group-peer.test.ts +0 -66
- package/test/variants-media.test.ts +0 -213
- package/test/variants-positional.test.ts +0 -58
- package/test/variants-pseudo-elements.test.ts +0 -47
- package/test/variants-state.test.ts +0 -62
- package/tsconfig.json +0 -18
package/test/attributify.test.ts
DELETED
|
@@ -1,592 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'bun:test'
|
|
2
|
-
import { extractAttributifyClasses, extractClasses } from '../src/parser'
|
|
3
|
-
|
|
4
|
-
describe('Attributify Mode', () => {
|
|
5
|
-
describe('extractAttributifyClasses', () => {
|
|
6
|
-
describe('with hw- prefix (default)', () => {
|
|
7
|
-
it('should extract prefixed boolean utility attributes', () => {
|
|
8
|
-
const html = '<div hw-flex hw-items-center></div>'
|
|
9
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
10
|
-
expect(result.has('flex')).toBe(true)
|
|
11
|
-
expect(result.has('items-center')).toBe(true)
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
it('should extract prefixed value attributes', () => {
|
|
15
|
-
const html = '<div hw-bg="blue-500" hw-text="white"></div>'
|
|
16
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
17
|
-
expect(result.has('bg-blue-500')).toBe(true)
|
|
18
|
-
expect(result.has('text-white')).toBe(true)
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
it('should extract multiple prefixed attributes', () => {
|
|
22
|
-
const html = '<div hw-flex hw-grid hw-block></div>'
|
|
23
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
24
|
-
expect(result.has('flex')).toBe(true)
|
|
25
|
-
expect(result.has('grid')).toBe(true)
|
|
26
|
-
expect(result.has('block')).toBe(true)
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('should extract prefixed padding and margin attributes', () => {
|
|
30
|
-
const html = '<div hw-p="4" hw-m="2" hw-px="8" hw-py="4"></div>'
|
|
31
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
32
|
-
expect(result.has('p-4')).toBe(true)
|
|
33
|
-
expect(result.has('m-2')).toBe(true)
|
|
34
|
-
expect(result.has('px-8')).toBe(true)
|
|
35
|
-
expect(result.has('py-4')).toBe(true)
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
it('should extract prefixed width and height attributes', () => {
|
|
39
|
-
const html = '<div hw-w="full" hw-h="screen"></div>'
|
|
40
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
41
|
-
expect(result.has('w-full')).toBe(true)
|
|
42
|
-
expect(result.has('h-screen')).toBe(true)
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
it('should extract multiple values from single attribute', () => {
|
|
46
|
-
const html = '<div hw-p="4 8"></div>'
|
|
47
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
48
|
-
expect(result.has('p-4')).toBe(true)
|
|
49
|
-
expect(result.has('p-8')).toBe(true)
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it('should not extract non-prefixed attributes', () => {
|
|
53
|
-
const html = '<div flex bg="blue-500"></div>'
|
|
54
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
55
|
-
expect(result.has('flex')).toBe(false)
|
|
56
|
-
expect(result.has('bg-blue-500')).toBe(false)
|
|
57
|
-
})
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
describe('with custom prefix', () => {
|
|
61
|
-
it('should extract custom prefixed boolean attributes', () => {
|
|
62
|
-
const html = '<div x-flex x-items-center></div>'
|
|
63
|
-
const result = extractAttributifyClasses(html, { enabled: true, prefix: 'x-' })
|
|
64
|
-
expect(result.has('flex')).toBe(true)
|
|
65
|
-
expect(result.has('items-center')).toBe(true)
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
it('should extract custom prefixed value attributes', () => {
|
|
69
|
-
const html = '<div x-bg="blue-500" x-text="white"></div>'
|
|
70
|
-
const result = extractAttributifyClasses(html, { enabled: true, prefix: 'x-' })
|
|
71
|
-
expect(result.has('bg-blue-500')).toBe(true)
|
|
72
|
-
expect(result.has('text-white')).toBe(true)
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
it('should not extract wrong prefix', () => {
|
|
76
|
-
const html = '<div hw-flex y-bg="blue-500"></div>'
|
|
77
|
-
const result = extractAttributifyClasses(html, { enabled: true, prefix: 'x-' })
|
|
78
|
-
expect(result.has('flex')).toBe(false)
|
|
79
|
-
expect(result.has('bg-blue-500')).toBe(false)
|
|
80
|
-
})
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
describe('with empty prefix', () => {
|
|
84
|
-
it('should extract boolean utility attributes without prefix', () => {
|
|
85
|
-
const html = '<div flex items-center></div>'
|
|
86
|
-
const result = extractAttributifyClasses(html, { enabled: true, prefix: '' })
|
|
87
|
-
expect(result.has('flex')).toBe(true)
|
|
88
|
-
expect(result.has('items-center')).toBe(true)
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
it('should extract value attributes without prefix', () => {
|
|
92
|
-
const html = '<div bg="blue-500" text="white"></div>'
|
|
93
|
-
const result = extractAttributifyClasses(html, { enabled: true, prefix: '' })
|
|
94
|
-
expect(result.has('bg-blue-500')).toBe(true)
|
|
95
|
-
expect(result.has('text-white')).toBe(true)
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
it('should still ignore standard HTML attributes', () => {
|
|
99
|
-
const html = '<div class="flex" id="main" style="color: red" name="test"></div>'
|
|
100
|
-
const result = extractAttributifyClasses(html, { enabled: true, prefix: '' })
|
|
101
|
-
expect(result.has('class-flex')).toBe(false)
|
|
102
|
-
expect(result.has('id-main')).toBe(false)
|
|
103
|
-
expect(result.has('style-color')).toBe(false)
|
|
104
|
-
expect(result.has('name-test')).toBe(false)
|
|
105
|
-
})
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
describe('disabled mode', () => {
|
|
109
|
-
it('should not extract when disabled', () => {
|
|
110
|
-
const html = '<div hw-flex hw-items-center></div>'
|
|
111
|
-
const result = extractAttributifyClasses(html, { enabled: false })
|
|
112
|
-
expect(result.size).toBe(0)
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
it('should not extract with undefined config', () => {
|
|
116
|
-
const html = '<div hw-flex hw-items-center></div>'
|
|
117
|
-
const result = extractAttributifyClasses(html, undefined)
|
|
118
|
-
expect(result.size).toBe(0)
|
|
119
|
-
})
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
describe('ignore attributes', () => {
|
|
123
|
-
it('should ignore data-* attributes', () => {
|
|
124
|
-
const html = '<div data-test="value" data-id="123"></div>'
|
|
125
|
-
const result = extractAttributifyClasses(html, { enabled: true, prefix: '' })
|
|
126
|
-
expect(result.size).toBe(0)
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
it('should ignore aria-* attributes', () => {
|
|
130
|
-
const html = '<div aria-label="label" aria-hidden="true"></div>'
|
|
131
|
-
const result = extractAttributifyClasses(html, { enabled: true, prefix: '' })
|
|
132
|
-
expect(result.size).toBe(0)
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
it('should ignore event handlers', () => {
|
|
136
|
-
const html = '<div onclick="handler()" onmouseover="hover()"></div>'
|
|
137
|
-
const result = extractAttributifyClasses(html, { enabled: true, prefix: '' })
|
|
138
|
-
expect(result.size).toBe(0)
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
it('should ignore standard HTML attributes', () => {
|
|
142
|
-
const html = '<div id="main" class="container" style="color:red" name="form"></div>'
|
|
143
|
-
const result = extractAttributifyClasses(html, { enabled: true, prefix: '' })
|
|
144
|
-
expect(result.size).toBe(0)
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
it('should ignore href, src, alt, title', () => {
|
|
148
|
-
const html = '<a href="/about" title="About"><img src="image.jpg" alt="Image"></a>'
|
|
149
|
-
const result = extractAttributifyClasses(html, { enabled: true, prefix: '' })
|
|
150
|
-
expect(result.size).toBe(0)
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
it('should ignore form-related attributes', () => {
|
|
154
|
-
const html = '<input type="text" value="hello" placeholder="Enter" required disabled>'
|
|
155
|
-
const result = extractAttributifyClasses(html, { enabled: true, prefix: '' })
|
|
156
|
-
expect(result.size).toBe(0)
|
|
157
|
-
})
|
|
158
|
-
|
|
159
|
-
it('should use custom ignore list', () => {
|
|
160
|
-
const html = '<div custom="value" hw-flex></div>'
|
|
161
|
-
const result = extractAttributifyClasses(html, {
|
|
162
|
-
enabled: true,
|
|
163
|
-
ignoreAttributes: ['custom'],
|
|
164
|
-
})
|
|
165
|
-
expect(result.has('custom-value')).toBe(false)
|
|
166
|
-
expect(result.has('flex')).toBe(true)
|
|
167
|
-
})
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
describe('complex scenarios', () => {
|
|
171
|
-
it('should extract from multiple elements', () => {
|
|
172
|
-
const html = `
|
|
173
|
-
<div hw-flex hw-p="4">
|
|
174
|
-
<span hw-text="white" hw-font="bold"></span>
|
|
175
|
-
<p hw-bg="gray-100"></p>
|
|
176
|
-
</div>
|
|
177
|
-
`
|
|
178
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
179
|
-
expect(result.has('flex')).toBe(true)
|
|
180
|
-
expect(result.has('p-4')).toBe(true)
|
|
181
|
-
expect(result.has('text-white')).toBe(true)
|
|
182
|
-
expect(result.has('font-bold')).toBe(true)
|
|
183
|
-
expect(result.has('bg-gray-100')).toBe(true)
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
it('should handle self-closing tags', () => {
|
|
187
|
-
const html = '<input hw-w="full" hw-p="2" />'
|
|
188
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
189
|
-
expect(result.has('w-full')).toBe(true)
|
|
190
|
-
expect(result.has('p-2')).toBe(true)
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
it('should handle mixed prefixed and standard attributes', () => {
|
|
194
|
-
const html = '<div class="container" id="main" hw-flex hw-bg="blue-500"></div>'
|
|
195
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
196
|
-
expect(result.has('flex')).toBe(true)
|
|
197
|
-
expect(result.has('bg-blue-500')).toBe(true)
|
|
198
|
-
expect(result.has('class-container')).toBe(false)
|
|
199
|
-
expect(result.has('id-main')).toBe(false)
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
it('should handle compound utility names', () => {
|
|
203
|
-
const html = '<div hw-justify-center hw-items-start hw-gap="4"></div>'
|
|
204
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
205
|
-
expect(result.has('justify-center')).toBe(true)
|
|
206
|
-
expect(result.has('items-start')).toBe(true)
|
|
207
|
-
expect(result.has('gap-4')).toBe(true)
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
it('should handle color values', () => {
|
|
211
|
-
const html = '<div hw-bg="blue-500" hw-text="gray-700" hw-border="red-300"></div>'
|
|
212
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
213
|
-
expect(result.has('bg-blue-500')).toBe(true)
|
|
214
|
-
expect(result.has('text-gray-700')).toBe(true)
|
|
215
|
-
expect(result.has('border-red-300')).toBe(true)
|
|
216
|
-
})
|
|
217
|
-
|
|
218
|
-
it('should handle spacing values', () => {
|
|
219
|
-
const html = '<div hw-m="4" hw-p="8" hw-gap="2"></div>'
|
|
220
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
221
|
-
expect(result.has('m-4')).toBe(true)
|
|
222
|
-
expect(result.has('p-8')).toBe(true)
|
|
223
|
-
expect(result.has('gap-2')).toBe(true)
|
|
224
|
-
})
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
describe('edge cases', () => {
|
|
228
|
-
it('should handle empty attribute values', () => {
|
|
229
|
-
const html = '<div hw-flex="" hw-grid=""></div>'
|
|
230
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
231
|
-
// Empty values should not create invalid classes
|
|
232
|
-
expect(result.has('flex-')).toBe(false)
|
|
233
|
-
})
|
|
234
|
-
|
|
235
|
-
it('should handle attributes with special characters in values', () => {
|
|
236
|
-
const html = '<div hw-w="1/2" hw-h="3/4"></div>'
|
|
237
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
238
|
-
expect(result.has('w-1/2')).toBe(true)
|
|
239
|
-
expect(result.has('h-3/4')).toBe(true)
|
|
240
|
-
})
|
|
241
|
-
|
|
242
|
-
it('should handle single quotes', () => {
|
|
243
|
-
const html = "<div hw-bg='blue-500' hw-text='white'></div>"
|
|
244
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
245
|
-
expect(result.has('bg-blue-500')).toBe(true)
|
|
246
|
-
expect(result.has('text-white')).toBe(true)
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
it('should handle newlines in HTML', () => {
|
|
250
|
-
const html = `<div
|
|
251
|
-
hw-flex
|
|
252
|
-
hw-items-center
|
|
253
|
-
hw-bg="blue-500"
|
|
254
|
-
></div>`
|
|
255
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
256
|
-
expect(result.has('flex')).toBe(true)
|
|
257
|
-
expect(result.has('items-center')).toBe(true)
|
|
258
|
-
expect(result.has('bg-blue-500')).toBe(true)
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
it('should handle tabs in HTML', () => {
|
|
262
|
-
const html = '<div\thw-flex\thw-items-center\thw-bg="blue-500"></div>'
|
|
263
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
264
|
-
expect(result.has('flex')).toBe(true)
|
|
265
|
-
expect(result.has('items-center')).toBe(true)
|
|
266
|
-
expect(result.has('bg-blue-500')).toBe(true)
|
|
267
|
-
})
|
|
268
|
-
})
|
|
269
|
-
})
|
|
270
|
-
|
|
271
|
-
describe('extractClasses with attributify', () => {
|
|
272
|
-
it('should extract both class and attributify utilities', () => {
|
|
273
|
-
const html = '<div class="flex p-4" hw-bg="blue-500" hw-items-center></div>'
|
|
274
|
-
const result = extractClasses(html, {
|
|
275
|
-
attributify: { enabled: true },
|
|
276
|
-
})
|
|
277
|
-
// From class attribute
|
|
278
|
-
expect(result.has('flex')).toBe(true)
|
|
279
|
-
expect(result.has('p-4')).toBe(true)
|
|
280
|
-
// From attributify
|
|
281
|
-
expect(result.has('bg-blue-500')).toBe(true)
|
|
282
|
-
expect(result.has('items-center')).toBe(true)
|
|
283
|
-
})
|
|
284
|
-
|
|
285
|
-
it('should work with complex HTML', () => {
|
|
286
|
-
const html = `
|
|
287
|
-
<div class="container mx-auto" hw-p="4" hw-bg="gray-100">
|
|
288
|
-
<header hw-flex hw-items-center hw-justify-between hw-p="2">
|
|
289
|
-
<h1 class="text-2xl font-bold">Title</h1>
|
|
290
|
-
<nav hw-flex hw-gap="4">
|
|
291
|
-
<a href="/" hw-text="blue-500">Home</a>
|
|
292
|
-
<a href="/about" hw-text="gray-700">About</a>
|
|
293
|
-
</nav>
|
|
294
|
-
</header>
|
|
295
|
-
</div>
|
|
296
|
-
`
|
|
297
|
-
const result = extractClasses(html, {
|
|
298
|
-
attributify: { enabled: true },
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
// Class attributes
|
|
302
|
-
expect(result.has('container')).toBe(true)
|
|
303
|
-
expect(result.has('mx-auto')).toBe(true)
|
|
304
|
-
expect(result.has('text-2xl')).toBe(true)
|
|
305
|
-
expect(result.has('font-bold')).toBe(true)
|
|
306
|
-
|
|
307
|
-
// Attributify
|
|
308
|
-
expect(result.has('p-4')).toBe(true)
|
|
309
|
-
expect(result.has('p-2')).toBe(true)
|
|
310
|
-
expect(result.has('bg-gray-100')).toBe(true)
|
|
311
|
-
expect(result.has('flex')).toBe(true)
|
|
312
|
-
expect(result.has('items-center')).toBe(true)
|
|
313
|
-
expect(result.has('justify-between')).toBe(true)
|
|
314
|
-
expect(result.has('gap-4')).toBe(true)
|
|
315
|
-
expect(result.has('text-blue-500')).toBe(true)
|
|
316
|
-
expect(result.has('text-gray-700')).toBe(true)
|
|
317
|
-
})
|
|
318
|
-
|
|
319
|
-
it('should not extract attributify when disabled', () => {
|
|
320
|
-
const html = '<div hw-flex hw-bg="blue-500"></div>'
|
|
321
|
-
const result = extractClasses(html)
|
|
322
|
-
expect(result.has('flex')).toBe(false)
|
|
323
|
-
expect(result.has('bg-blue-500')).toBe(false)
|
|
324
|
-
})
|
|
325
|
-
|
|
326
|
-
it('should work with custom prefix', () => {
|
|
327
|
-
const html = '<div class="container" x-flex x-bg="blue-500"></div>'
|
|
328
|
-
const result = extractClasses(html, {
|
|
329
|
-
attributify: { enabled: true, prefix: 'x-' },
|
|
330
|
-
})
|
|
331
|
-
expect(result.has('container')).toBe(true)
|
|
332
|
-
expect(result.has('flex')).toBe(true)
|
|
333
|
-
expect(result.has('bg-blue-500')).toBe(true)
|
|
334
|
-
})
|
|
335
|
-
|
|
336
|
-
it('should work in JSX/TSX files', () => {
|
|
337
|
-
const jsx = '<Button className="btn" hw-px="4" hw-py="2" hw-bg="blue-500" />'
|
|
338
|
-
const result = extractClasses(jsx, {
|
|
339
|
-
attributify: { enabled: true },
|
|
340
|
-
})
|
|
341
|
-
expect(result.has('btn')).toBe(true)
|
|
342
|
-
expect(result.has('px-4')).toBe(true)
|
|
343
|
-
expect(result.has('py-2')).toBe(true)
|
|
344
|
-
expect(result.has('bg-blue-500')).toBe(true)
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
it('should work with template literals', () => {
|
|
348
|
-
const jsx = '<div className={`container ${active && "active"}`} hw-flex hw-p="4"></div>'
|
|
349
|
-
const result = extractClasses(jsx, {
|
|
350
|
-
attributify: { enabled: true },
|
|
351
|
-
})
|
|
352
|
-
expect(result.has('container')).toBe(true)
|
|
353
|
-
expect(result.has('flex')).toBe(true)
|
|
354
|
-
expect(result.has('p-4')).toBe(true)
|
|
355
|
-
})
|
|
356
|
-
})
|
|
357
|
-
|
|
358
|
-
describe('attributify + bracket syntax combined', () => {
|
|
359
|
-
it('should support both features together', () => {
|
|
360
|
-
const html = `
|
|
361
|
-
<div class="flex[col jc-center] bg:black" hw-p="4" hw-items-center>
|
|
362
|
-
<span class="text[white 2rem]">Content</span>
|
|
363
|
-
</div>
|
|
364
|
-
`
|
|
365
|
-
const result = extractClasses(html, {
|
|
366
|
-
attributify: { enabled: true },
|
|
367
|
-
bracketSyntax: { enabled: true, colonSyntax: true },
|
|
368
|
-
})
|
|
369
|
-
|
|
370
|
-
// Bracket syntax expansions
|
|
371
|
-
expect(result.has('flex-col')).toBe(true)
|
|
372
|
-
expect(result.has('justify-center')).toBe(true)
|
|
373
|
-
expect(result.has('bg-black')).toBe(true)
|
|
374
|
-
expect(result.has('text-white')).toBe(true)
|
|
375
|
-
expect(result.has('text-[2rem]')).toBe(true)
|
|
376
|
-
|
|
377
|
-
// Attributify
|
|
378
|
-
expect(result.has('p-4')).toBe(true)
|
|
379
|
-
expect(result.has('items-center')).toBe(true)
|
|
380
|
-
})
|
|
381
|
-
|
|
382
|
-
it('should handle complex combined usage', () => {
|
|
383
|
-
const html = `
|
|
384
|
-
<main class="reset:meyer">
|
|
385
|
-
<div class="flex[col jc-center ai-center]" hw-bg="slate-900" hw-w="full" hw-h="screen">
|
|
386
|
-
<h1 class="text[4rem 700]" hw-text="white">Hello Crosswind!</h1>
|
|
387
|
-
<p class="text[lg]" hw-text="gray-400" hw-mt="4">Welcome to the future of CSS</p>
|
|
388
|
-
</div>
|
|
389
|
-
</main>
|
|
390
|
-
`
|
|
391
|
-
const result = extractClasses(html, {
|
|
392
|
-
attributify: { enabled: true },
|
|
393
|
-
bracketSyntax: { enabled: true, colonSyntax: true },
|
|
394
|
-
})
|
|
395
|
-
|
|
396
|
-
// From bracket syntax
|
|
397
|
-
expect(result.has('reset-meyer')).toBe(true)
|
|
398
|
-
expect(result.has('flex-col')).toBe(true)
|
|
399
|
-
expect(result.has('justify-center')).toBe(true)
|
|
400
|
-
expect(result.has('items-center')).toBe(true)
|
|
401
|
-
expect(result.has('text-[4rem]')).toBe(true)
|
|
402
|
-
expect(result.has('font-bold')).toBe(true)
|
|
403
|
-
expect(result.has('text-lg')).toBe(true)
|
|
404
|
-
|
|
405
|
-
// From attributify
|
|
406
|
-
expect(result.has('bg-slate-900')).toBe(true)
|
|
407
|
-
expect(result.has('w-full')).toBe(true)
|
|
408
|
-
expect(result.has('h-screen')).toBe(true)
|
|
409
|
-
expect(result.has('text-white')).toBe(true)
|
|
410
|
-
expect(result.has('text-gray-400')).toBe(true)
|
|
411
|
-
expect(result.has('mt-4')).toBe(true)
|
|
412
|
-
})
|
|
413
|
-
})
|
|
414
|
-
|
|
415
|
-
describe('state variants in attributify', () => {
|
|
416
|
-
it('should extract hover:bg attribute', () => {
|
|
417
|
-
const html = '<div hw-hover:bg="blue-600"></div>'
|
|
418
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
419
|
-
expect(result.has('hover:bg-blue-600')).toBe(true)
|
|
420
|
-
})
|
|
421
|
-
|
|
422
|
-
it('should extract dark:text attribute', () => {
|
|
423
|
-
const html = '<div hw-dark:text="white"></div>'
|
|
424
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
425
|
-
expect(result.has('dark:text-white')).toBe(true)
|
|
426
|
-
})
|
|
427
|
-
|
|
428
|
-
it('should extract focus:ring attribute', () => {
|
|
429
|
-
const html = '<div hw-focus:ring="2"></div>'
|
|
430
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
431
|
-
expect(result.has('focus:ring-2')).toBe(true)
|
|
432
|
-
})
|
|
433
|
-
|
|
434
|
-
it('should extract sm:flex attribute', () => {
|
|
435
|
-
const html = '<div hw-sm:flex></div>'
|
|
436
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
437
|
-
expect(result.has('sm:flex')).toBe(true)
|
|
438
|
-
})
|
|
439
|
-
|
|
440
|
-
it('should extract md:grid attribute', () => {
|
|
441
|
-
const html = '<div hw-md:grid></div>'
|
|
442
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
443
|
-
expect(result.has('md:grid')).toBe(true)
|
|
444
|
-
})
|
|
445
|
-
|
|
446
|
-
it('should extract dark:hover:bg attribute (multiple variants)', () => {
|
|
447
|
-
const html = '<div hw-dark:hover:bg="gray-800"></div>'
|
|
448
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
449
|
-
expect(result.has('dark:hover:bg-gray-800')).toBe(true)
|
|
450
|
-
})
|
|
451
|
-
|
|
452
|
-
it('should extract focus-visible:outline attribute', () => {
|
|
453
|
-
const html = '<div hw-focus-visible:outline="none"></div>'
|
|
454
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
455
|
-
expect(result.has('focus-visible:outline-none')).toBe(true)
|
|
456
|
-
})
|
|
457
|
-
|
|
458
|
-
it('should extract active:scale attribute', () => {
|
|
459
|
-
const html = '<div hw-active:scale="95"></div>'
|
|
460
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
461
|
-
expect(result.has('active:scale-95')).toBe(true)
|
|
462
|
-
})
|
|
463
|
-
|
|
464
|
-
it('should extract disabled:opacity attribute', () => {
|
|
465
|
-
const html = '<div hw-disabled:opacity="50"></div>'
|
|
466
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
467
|
-
expect(result.has('disabled:opacity-50')).toBe(true)
|
|
468
|
-
})
|
|
469
|
-
|
|
470
|
-
it('should extract group-hover:text attribute', () => {
|
|
471
|
-
const html = '<div hw-group-hover:text="blue-500"></div>'
|
|
472
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
473
|
-
expect(result.has('group-hover:text-blue-500')).toBe(true)
|
|
474
|
-
})
|
|
475
|
-
|
|
476
|
-
it('should extract multiple variant attributes from same element', () => {
|
|
477
|
-
const html = '<div hw-bg="gray-100" hw-hover:bg="gray-200" hw-dark:bg="gray-800" hw-dark:hover:bg="gray-700"></div>'
|
|
478
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
479
|
-
expect(result.has('bg-gray-100')).toBe(true)
|
|
480
|
-
expect(result.has('hover:bg-gray-200')).toBe(true)
|
|
481
|
-
expect(result.has('dark:bg-gray-800')).toBe(true)
|
|
482
|
-
expect(result.has('dark:hover:bg-gray-700')).toBe(true)
|
|
483
|
-
})
|
|
484
|
-
|
|
485
|
-
it('should extract boolean variant attributes', () => {
|
|
486
|
-
const html = '<div hw-hover:underline hw-focus:outline-none></div>'
|
|
487
|
-
const result = extractAttributifyClasses(html, { enabled: true })
|
|
488
|
-
expect(result.has('hover:underline')).toBe(true)
|
|
489
|
-
expect(result.has('focus:outline-none')).toBe(true)
|
|
490
|
-
})
|
|
491
|
-
|
|
492
|
-
it('should work with extractClasses', () => {
|
|
493
|
-
const html = '<div class="p-4" hw-hover:bg="blue-500" hw-dark:text="white"></div>'
|
|
494
|
-
const result = extractClasses(html, {
|
|
495
|
-
attributify: { enabled: true },
|
|
496
|
-
})
|
|
497
|
-
expect(result.has('p-4')).toBe(true)
|
|
498
|
-
expect(result.has('hover:bg-blue-500')).toBe(true)
|
|
499
|
-
expect(result.has('dark:text-white')).toBe(true)
|
|
500
|
-
})
|
|
501
|
-
|
|
502
|
-
it('should work with custom prefix and variants', () => {
|
|
503
|
-
const html = '<div x-hover:bg="red-500" x-sm:flex></div>'
|
|
504
|
-
const result = extractAttributifyClasses(html, { enabled: true, prefix: 'x-' })
|
|
505
|
-
expect(result.has('hover:bg-red-500')).toBe(true)
|
|
506
|
-
expect(result.has('sm:flex')).toBe(true)
|
|
507
|
-
})
|
|
508
|
-
})
|
|
509
|
-
|
|
510
|
-
describe('comprehensive real-world examples', () => {
|
|
511
|
-
it('should handle a button component', () => {
|
|
512
|
-
const html = `
|
|
513
|
-
<button
|
|
514
|
-
class="flex items-center"
|
|
515
|
-
hw-px="4"
|
|
516
|
-
hw-py="2"
|
|
517
|
-
hw-bg="blue-500"
|
|
518
|
-
hw-hover:bg="blue-600"
|
|
519
|
-
hw-active:bg="blue-700"
|
|
520
|
-
hw-text="white"
|
|
521
|
-
hw-rounded="lg"
|
|
522
|
-
hw-font="semibold"
|
|
523
|
-
hw-transition="colors"
|
|
524
|
-
hw-duration="150"
|
|
525
|
-
hw-focus:ring="2"
|
|
526
|
-
hw-focus:ring-offset="2"
|
|
527
|
-
hw-disabled:opacity="50"
|
|
528
|
-
hw-disabled:cursor="not-allowed"
|
|
529
|
-
>
|
|
530
|
-
Click me
|
|
531
|
-
</button>
|
|
532
|
-
`
|
|
533
|
-
const result = extractClasses(html, {
|
|
534
|
-
attributify: { enabled: true },
|
|
535
|
-
})
|
|
536
|
-
|
|
537
|
-
expect(result.has('flex')).toBe(true)
|
|
538
|
-
expect(result.has('items-center')).toBe(true)
|
|
539
|
-
expect(result.has('px-4')).toBe(true)
|
|
540
|
-
expect(result.has('py-2')).toBe(true)
|
|
541
|
-
expect(result.has('bg-blue-500')).toBe(true)
|
|
542
|
-
expect(result.has('hover:bg-blue-600')).toBe(true)
|
|
543
|
-
expect(result.has('active:bg-blue-700')).toBe(true)
|
|
544
|
-
expect(result.has('text-white')).toBe(true)
|
|
545
|
-
expect(result.has('rounded-lg')).toBe(true)
|
|
546
|
-
expect(result.has('font-semibold')).toBe(true)
|
|
547
|
-
expect(result.has('transition-colors')).toBe(true)
|
|
548
|
-
expect(result.has('duration-150')).toBe(true)
|
|
549
|
-
expect(result.has('focus:ring-2')).toBe(true)
|
|
550
|
-
expect(result.has('focus:ring-offset-2')).toBe(true)
|
|
551
|
-
expect(result.has('disabled:opacity-50')).toBe(true)
|
|
552
|
-
expect(result.has('disabled:cursor-not-allowed')).toBe(true)
|
|
553
|
-
})
|
|
554
|
-
|
|
555
|
-
it('should handle a dark mode card component', () => {
|
|
556
|
-
const html = `
|
|
557
|
-
<div
|
|
558
|
-
hw-bg="white"
|
|
559
|
-
hw-dark:bg="gray-800"
|
|
560
|
-
hw-p="6"
|
|
561
|
-
hw-rounded="xl"
|
|
562
|
-
hw-shadow="lg"
|
|
563
|
-
hw-dark:shadow="none"
|
|
564
|
-
hw-border="gray-200"
|
|
565
|
-
hw-dark:border="gray-700"
|
|
566
|
-
>
|
|
567
|
-
<h2 hw-text="gray-900" hw-dark:text="white" hw-font="bold" hw-text="xl">Card Title</h2>
|
|
568
|
-
<p hw-text="gray-600" hw-dark:text="gray-300" hw-mt="2">Card content goes here.</p>
|
|
569
|
-
</div>
|
|
570
|
-
`
|
|
571
|
-
const result = extractClasses(html, {
|
|
572
|
-
attributify: { enabled: true },
|
|
573
|
-
})
|
|
574
|
-
|
|
575
|
-
expect(result.has('bg-white')).toBe(true)
|
|
576
|
-
expect(result.has('dark:bg-gray-800')).toBe(true)
|
|
577
|
-
expect(result.has('p-6')).toBe(true)
|
|
578
|
-
expect(result.has('rounded-xl')).toBe(true)
|
|
579
|
-
expect(result.has('shadow-lg')).toBe(true)
|
|
580
|
-
expect(result.has('dark:shadow-none')).toBe(true)
|
|
581
|
-
expect(result.has('border-gray-200')).toBe(true)
|
|
582
|
-
expect(result.has('dark:border-gray-700')).toBe(true)
|
|
583
|
-
expect(result.has('text-gray-900')).toBe(true)
|
|
584
|
-
expect(result.has('dark:text-white')).toBe(true)
|
|
585
|
-
expect(result.has('font-bold')).toBe(true)
|
|
586
|
-
expect(result.has('text-xl')).toBe(true)
|
|
587
|
-
expect(result.has('text-gray-600')).toBe(true)
|
|
588
|
-
expect(result.has('dark:text-gray-300')).toBe(true)
|
|
589
|
-
expect(result.has('mt-2')).toBe(true)
|
|
590
|
-
})
|
|
591
|
-
})
|
|
592
|
-
})
|