@cwcss/crosswind 0.1.5 → 0.2.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.
Files changed (87) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +52 -0
  3. package/dist/bin/cli.js +14615 -0
  4. package/dist/build.d.ts +24 -0
  5. package/dist/config.d.ts +5 -0
  6. package/dist/generator.d.ts +31 -0
  7. package/dist/index.d.ts +10 -0
  8. package/dist/parser.d.ts +42 -0
  9. package/dist/plugin.d.ts +22 -0
  10. package/dist/preflight-forms.d.ts +5 -0
  11. package/dist/preflight.d.ts +2 -0
  12. package/dist/rules-advanced.d.ts +27 -0
  13. package/dist/rules-effects.d.ts +25 -0
  14. package/dist/rules-forms.d.ts +7 -0
  15. package/dist/rules-grid.d.ts +13 -0
  16. package/dist/rules-interactivity.d.ts +41 -0
  17. package/dist/rules-layout.d.ts +26 -0
  18. package/dist/rules-transforms.d.ts +33 -0
  19. package/dist/rules-typography.d.ts +41 -0
  20. package/dist/rules.d.ts +39 -0
  21. package/dist/scanner.d.ts +18 -0
  22. package/dist/src/index.js +12848 -0
  23. package/dist/transformer-compile-class.d.ts +37 -0
  24. package/{src/types.ts → dist/types.d.ts} +17 -86
  25. package/package.json +2 -16
  26. package/PLUGIN.md +0 -235
  27. package/benchmark/framework-comparison.bench.ts +0 -850
  28. package/bin/cli.ts +0 -365
  29. package/bin/crosswind +0 -0
  30. package/bin/headwind +0 -0
  31. package/build.ts +0 -8
  32. package/crosswind.config.ts +0 -9
  33. package/example/comprehensive.html +0 -70
  34. package/example/index.html +0 -21
  35. package/example/output.css +0 -236
  36. package/examples/plugin/README.md +0 -112
  37. package/examples/plugin/build.ts +0 -32
  38. package/examples/plugin/src/index.html +0 -34
  39. package/examples/plugin/src/index.ts +0 -7
  40. package/headwind +0 -2
  41. package/src/build.ts +0 -101
  42. package/src/config.ts +0 -529
  43. package/src/generator.ts +0 -2173
  44. package/src/index.ts +0 -10
  45. package/src/parser.ts +0 -1471
  46. package/src/plugin.ts +0 -118
  47. package/src/preflight-forms.ts +0 -229
  48. package/src/preflight.ts +0 -388
  49. package/src/rules-advanced.ts +0 -477
  50. package/src/rules-effects.ts +0 -461
  51. package/src/rules-forms.ts +0 -103
  52. package/src/rules-grid.ts +0 -241
  53. package/src/rules-interactivity.ts +0 -525
  54. package/src/rules-layout.ts +0 -385
  55. package/src/rules-transforms.ts +0 -412
  56. package/src/rules-typography.ts +0 -486
  57. package/src/rules.ts +0 -809
  58. package/src/scanner.ts +0 -84
  59. package/src/transformer-compile-class.ts +0 -275
  60. package/test/advanced-features.test.ts +0 -911
  61. package/test/arbitrary.test.ts +0 -396
  62. package/test/attributify.test.ts +0 -592
  63. package/test/bracket-syntax.test.ts +0 -1133
  64. package/test/build.test.ts +0 -99
  65. package/test/colors.test.ts +0 -934
  66. package/test/flexbox.test.ts +0 -669
  67. package/test/generator.test.ts +0 -597
  68. package/test/grid.test.ts +0 -584
  69. package/test/layout.test.ts +0 -404
  70. package/test/modifiers.test.ts +0 -417
  71. package/test/parser.test.ts +0 -564
  72. package/test/performance-regression.test.ts +0 -376
  73. package/test/performance.test.ts +0 -568
  74. package/test/plugin.test.ts +0 -160
  75. package/test/scanner.test.ts +0 -94
  76. package/test/sizing.test.ts +0 -481
  77. package/test/spacing.test.ts +0 -394
  78. package/test/transformer-compile-class.test.ts +0 -287
  79. package/test/transforms.test.ts +0 -448
  80. package/test/typography.test.ts +0 -632
  81. package/test/variants-form-states.test.ts +0 -225
  82. package/test/variants-group-peer.test.ts +0 -66
  83. package/test/variants-media.test.ts +0 -213
  84. package/test/variants-positional.test.ts +0 -58
  85. package/test/variants-pseudo-elements.test.ts +0 -47
  86. package/test/variants-state.test.ts +0 -62
  87. package/tsconfig.json +0 -18
@@ -1,394 +0,0 @@
1
- import { describe, expect, it } from 'bun:test'
2
- import { defaultConfig } from '../src/config'
3
- import { CSSGenerator } from '../src/generator'
4
- import { parseClass } from '../src/parser'
5
-
6
- describe('Spacing Utilities', () => {
7
- describe('Padding', () => {
8
- it('should generate p-4', () => {
9
- const gen = new CSSGenerator(defaultConfig)
10
- gen.generate('p-4')
11
- expect(gen.toCSS(false)).toContain('padding: 1rem;')
12
- })
13
-
14
- it('should generate px-4', () => {
15
- const gen = new CSSGenerator(defaultConfig)
16
- gen.generate('px-4')
17
- const css = gen.toCSS(false)
18
- expect(css).toContain('padding-left: 1rem;')
19
- expect(css).toContain('padding-right: 1rem;')
20
- })
21
-
22
- it('should generate py-2', () => {
23
- const gen = new CSSGenerator(defaultConfig)
24
- gen.generate('py-2')
25
- const css = gen.toCSS(false)
26
- expect(css).toContain('padding-top: 0.5rem;')
27
- expect(css).toContain('padding-bottom: 0.5rem;')
28
- })
29
-
30
- it('should generate pt-4', () => {
31
- const gen = new CSSGenerator(defaultConfig)
32
- gen.generate('pt-4')
33
- expect(gen.toCSS(false)).toContain('padding-top: 1rem;')
34
- })
35
-
36
- it('should generate pr-8', () => {
37
- const gen = new CSSGenerator(defaultConfig)
38
- gen.generate('pr-8')
39
- expect(gen.toCSS(false)).toContain('padding-right: 2rem;')
40
- })
41
-
42
- it('should generate pb-0', () => {
43
- const gen = new CSSGenerator(defaultConfig)
44
- gen.generate('pb-0')
45
- expect(gen.toCSS(false)).toContain('padding-bottom: 0;')
46
- })
47
-
48
- it('should generate pl-1', () => {
49
- const gen = new CSSGenerator(defaultConfig)
50
- gen.generate('pl-1')
51
- expect(gen.toCSS(false)).toContain('padding-left: 0.25rem;')
52
- })
53
- })
54
-
55
- describe('Margin', () => {
56
- it('should generate m-4', () => {
57
- const gen = new CSSGenerator(defaultConfig)
58
- gen.generate('m-4')
59
- expect(gen.toCSS(false)).toContain('margin: 1rem;')
60
- })
61
-
62
- it('should generate mx-auto', () => {
63
- const gen = new CSSGenerator(defaultConfig)
64
- gen.generate('mx-auto')
65
- const css = gen.toCSS(false)
66
- expect(css).toContain('margin-left: auto;')
67
- expect(css).toContain('margin-right: auto;')
68
- })
69
-
70
- it('should generate my-4', () => {
71
- const gen = new CSSGenerator(defaultConfig)
72
- gen.generate('my-4')
73
- const css = gen.toCSS(false)
74
- expect(css).toContain('margin-top: 1rem;')
75
- expect(css).toContain('margin-bottom: 1rem;')
76
- })
77
-
78
- it('should generate mt-8', () => {
79
- const gen = new CSSGenerator(defaultConfig)
80
- gen.generate('mt-8')
81
- expect(gen.toCSS(false)).toContain('margin-top: 2rem;')
82
- })
83
-
84
- it('should generate mr-2', () => {
85
- const gen = new CSSGenerator(defaultConfig)
86
- gen.generate('mr-2')
87
- expect(gen.toCSS(false)).toContain('margin-right: 0.5rem;')
88
- })
89
-
90
- it('should generate mb-0', () => {
91
- const gen = new CSSGenerator(defaultConfig)
92
- gen.generate('mb-0')
93
- expect(gen.toCSS(false)).toContain('margin-bottom: 0;')
94
- })
95
-
96
- it('should generate ml-1', () => {
97
- const gen = new CSSGenerator(defaultConfig)
98
- gen.generate('ml-1')
99
- expect(gen.toCSS(false)).toContain('margin-left: 0.25rem;')
100
- })
101
- })
102
-
103
- describe('Negative margins', () => {
104
- it('should parse negative margin', () => {
105
- const result = parseClass('-m-4')
106
- expect(result).toEqual({
107
- raw: '-m-4',
108
- variants: [],
109
- utility: 'm',
110
- value: '-4',
111
- important: false,
112
- arbitrary: false,
113
- })
114
- })
115
-
116
- it('should generate negative margin CSS', () => {
117
- const gen = new CSSGenerator(defaultConfig)
118
- gen.generate('-m-4')
119
- expect(gen.toCSS(false)).toContain('margin: -1rem;')
120
- })
121
-
122
- it('should generate negative margin-top', () => {
123
- const gen = new CSSGenerator(defaultConfig)
124
- gen.generate('-mt-2')
125
- expect(gen.toCSS(false)).toContain('margin-top: -0.5rem;')
126
- })
127
-
128
- it('should generate negative margin-left', () => {
129
- const gen = new CSSGenerator(defaultConfig)
130
- gen.generate('-ml-8')
131
- expect(gen.toCSS(false)).toContain('margin-left: -2rem;')
132
- })
133
-
134
- it('should handle very large negative margin', () => {
135
- const gen = new CSSGenerator(defaultConfig)
136
- gen.generate('-m-999')
137
- expect(gen.toCSS(false)).toContain('margin: -999;')
138
- })
139
-
140
- it('should handle negative arbitrary values', () => {
141
- const gen = new CSSGenerator(defaultConfig)
142
- gen.generate('-m-[100px]')
143
- const css = gen.toCSS(false)
144
- expect(css).toBeDefined()
145
- })
146
- })
147
-
148
- describe('Arbitrary spacing values', () => {
149
- it('should support arbitrary padding', () => {
150
- const gen = new CSSGenerator(defaultConfig)
151
- gen.generate('p-[2.5rem]')
152
- expect(gen.toCSS(false)).toContain('padding: 2.5rem;')
153
- })
154
-
155
- it('should support arbitrary margin', () => {
156
- const gen = new CSSGenerator(defaultConfig)
157
- gen.generate('m-[15px]')
158
- expect(gen.toCSS(false)).toContain('margin: 15px;')
159
- })
160
- })
161
- })
162
-
163
- describe('Edge Cases', () => {
164
- describe('Zero and auto values', () => {
165
- it('should handle p-0', () => {
166
- const gen = new CSSGenerator(defaultConfig)
167
- gen.generate('p-0')
168
- expect(gen.toCSS(false)).toContain('padding: 0;')
169
- })
170
-
171
- it('should handle m-auto', () => {
172
- const gen = new CSSGenerator(defaultConfig)
173
- gen.generate('m-auto')
174
- expect(gen.toCSS(false)).toContain('margin: auto;')
175
- })
176
-
177
- it('should handle px-0 and py-0', () => {
178
- const gen = new CSSGenerator(defaultConfig)
179
- gen.generate('px-0')
180
- gen.generate('py-0')
181
- const css = gen.toCSS(false)
182
- expect(css).toContain('padding-left: 0;')
183
- expect(css).toContain('padding-right: 0;')
184
- expect(css).toContain('padding-top: 0;')
185
- expect(css).toContain('padding-bottom: 0;')
186
- })
187
- })
188
-
189
- describe('Extreme values', () => {
190
- it('should handle very large padding', () => {
191
- const gen = new CSSGenerator(defaultConfig)
192
- gen.generate('p-[500px]')
193
- expect(gen.toCSS(false)).toContain('padding: 500px;')
194
- })
195
-
196
- it('should handle very large negative margin', () => {
197
- const gen = new CSSGenerator(defaultConfig)
198
- gen.generate('-m-[500px]')
199
- const css = gen.toCSS(false)
200
- expect(css).toBeDefined()
201
- })
202
-
203
- it('should handle padding with decimal values', () => {
204
- const gen = new CSSGenerator(defaultConfig)
205
- gen.generate('p-[2.5rem]')
206
- expect(gen.toCSS(false)).toContain('padding: 2.5rem;')
207
- })
208
-
209
- it('should handle margin with decimal values', () => {
210
- const gen = new CSSGenerator(defaultConfig)
211
- gen.generate('m-[1.25rem]')
212
- expect(gen.toCSS(false)).toContain('margin: 1.25rem;')
213
- })
214
- })
215
-
216
- describe('CSS functions', () => {
217
- it('should handle padding with calc()', () => {
218
- const gen = new CSSGenerator(defaultConfig)
219
- gen.generate('p-[calc(100%-2rem)]')
220
- expect(gen.toCSS(false)).toContain('padding: calc(100%-2rem);')
221
- })
222
-
223
- it('should handle margin with calc()', () => {
224
- const gen = new CSSGenerator(defaultConfig)
225
- gen.generate('m-[calc(50%+10px)]')
226
- expect(gen.toCSS(false)).toContain('margin: calc(50%+10px);')
227
- })
228
-
229
- it('should handle padding with CSS variables', () => {
230
- const gen = new CSSGenerator(defaultConfig)
231
- gen.generate('p-[var(--spacing)]')
232
- expect(gen.toCSS(false)).toContain('padding: var(--spacing);')
233
- })
234
-
235
- it('should handle margin with CSS variables', () => {
236
- const gen = new CSSGenerator(defaultConfig)
237
- gen.generate('m-[var(--margin-size)]')
238
- expect(gen.toCSS(false)).toContain('margin: var(--margin-size);')
239
- })
240
- })
241
-
242
- describe('Negative values comprehensive', () => {
243
- it('should handle negative px', () => {
244
- const gen = new CSSGenerator(defaultConfig)
245
- gen.generate('-px-4')
246
- const css = gen.toCSS(false)
247
- expect(css).toContain('padding-left: -1rem;')
248
- expect(css).toContain('padding-right: -1rem;')
249
- })
250
-
251
- it('should handle negative py', () => {
252
- const gen = new CSSGenerator(defaultConfig)
253
- gen.generate('-py-4')
254
- const css = gen.toCSS(false)
255
- expect(css).toContain('padding-top: -1rem;')
256
- expect(css).toContain('padding-bottom: -1rem;')
257
- })
258
-
259
- it('should handle negative mx', () => {
260
- const gen = new CSSGenerator(defaultConfig)
261
- gen.generate('-mx-8')
262
- const css = gen.toCSS(false)
263
- expect(css).toContain('margin-left: -2rem;')
264
- expect(css).toContain('margin-right: -2rem;')
265
- })
266
-
267
- it('should handle negative my', () => {
268
- const gen = new CSSGenerator(defaultConfig)
269
- gen.generate('-my-8')
270
- const css = gen.toCSS(false)
271
- expect(css).toContain('margin-top: -2rem;')
272
- expect(css).toContain('margin-bottom: -2rem;')
273
- })
274
-
275
- it('should handle -m-0 (negative zero)', () => {
276
- const gen = new CSSGenerator(defaultConfig)
277
- gen.generate('-m-0')
278
- const css = gen.toCSS(false)
279
- expect(css).toContain('margin: 0;')
280
- })
281
- })
282
-
283
- describe('Percentage and viewport units', () => {
284
- it('should handle padding with percentage', () => {
285
- const gen = new CSSGenerator(defaultConfig)
286
- gen.generate('p-[10%]')
287
- expect(gen.toCSS(false)).toContain('padding: 10%;')
288
- })
289
-
290
- it('should handle margin with vw', () => {
291
- const gen = new CSSGenerator(defaultConfig)
292
- gen.generate('m-[5vw]')
293
- expect(gen.toCSS(false)).toContain('margin: 5vw;')
294
- })
295
-
296
- it('should handle padding with vh', () => {
297
- const gen = new CSSGenerator(defaultConfig)
298
- gen.generate('p-[10vh]')
299
- expect(gen.toCSS(false)).toContain('padding: 10vh;')
300
- })
301
- })
302
-
303
- describe('With variants', () => {
304
- it('should handle spacing with important', () => {
305
- const gen = new CSSGenerator(defaultConfig)
306
- gen.generate('!p-4')
307
- expect(gen.toCSS(false)).toContain('padding: 1rem !important;')
308
- })
309
-
310
- it('should handle negative margin with important', () => {
311
- const gen = new CSSGenerator(defaultConfig)
312
- gen.generate('!-m-4')
313
- expect(gen.toCSS(false)).toContain('margin: -1rem !important;')
314
- })
315
-
316
- it('should handle spacing with hover', () => {
317
- const gen = new CSSGenerator(defaultConfig)
318
- gen.generate('hover:p-8')
319
- const css = gen.toCSS(false)
320
- expect(css).toContain(':hover')
321
- expect(css).toContain('padding: 2rem;')
322
- })
323
-
324
- it('should handle spacing with responsive', () => {
325
- const gen = new CSSGenerator(defaultConfig)
326
- gen.generate('md:p-6')
327
- const css = gen.toCSS(false)
328
- expect(css).toContain('@media (min-width: 768px)')
329
- expect(css).toContain('padding: 1.5rem;')
330
- })
331
- })
332
-
333
- describe('Individual side combinations', () => {
334
- it('should handle all four sides independently', () => {
335
- const gen = new CSSGenerator(defaultConfig)
336
- gen.generate('pt-1')
337
- gen.generate('pr-2')
338
- gen.generate('pb-3')
339
- gen.generate('pl-4')
340
- const css = gen.toCSS(false)
341
- expect(css).toContain('padding-top: 0.25rem;')
342
- expect(css).toContain('padding-right: 0.5rem;')
343
- expect(css).toContain('padding-bottom: 0.75rem;')
344
- expect(css).toContain('padding-left: 1rem;')
345
- })
346
-
347
- it('should handle mixed negative margins', () => {
348
- const gen = new CSSGenerator(defaultConfig)
349
- gen.generate('-mt-4')
350
- gen.generate('mr-4')
351
- gen.generate('-mb-4')
352
- gen.generate('ml-4')
353
- const css = gen.toCSS(false)
354
- expect(css).toContain('margin-top: -1rem;')
355
- expect(css).toContain('margin-right: 1rem;')
356
- expect(css).toContain('margin-bottom: -1rem;')
357
- expect(css).toContain('margin-left: 1rem;')
358
- })
359
- })
360
-
361
- describe('Edge cases', () => {
362
- it('should handle zero spacing', () => {
363
- const gen = new CSSGenerator(defaultConfig)
364
- gen.generate('p-0')
365
- gen.generate('m-0')
366
- const css = gen.toCSS(false)
367
- expect(css).toContain('0')
368
- })
369
-
370
- it('should handle negative zero', () => {
371
- const gen = new CSSGenerator(defaultConfig)
372
- gen.generate('-m-0')
373
- gen.generate('-p-0')
374
- const css = gen.toCSS(false)
375
- expect(css).toContain('margin')
376
- })
377
-
378
- it('should handle decimal spacing values', () => {
379
- const gen = new CSSGenerator(defaultConfig)
380
- gen.generate('p-0.5')
381
- gen.generate('m-2.5')
382
- const css = gen.toCSS(false)
383
- expect(css.length).toBeGreaterThan(0)
384
- })
385
-
386
- it('should handle negative fractional spacing', () => {
387
- const gen = new CSSGenerator(defaultConfig)
388
- gen.generate('-m-1/2')
389
- gen.generate('-translate-x-1/2')
390
- const css = gen.toCSS(false)
391
- expect(css).toContain('-')
392
- })
393
- })
394
- })
@@ -1,287 +0,0 @@
1
- import { describe, expect, it } from 'bun:test'
2
- import {
3
- CompileClassTransformer,
4
- extractCompileClasses,
5
- generateCompiledClassNames,
6
- transformContent,
7
- } from '../src/transformer-compile-class'
8
-
9
- describe('Compile Class Transformer', () => {
10
- describe('extractCompileClasses', () => {
11
- it('should extract classes with default trigger', () => {
12
- const content = '<div class=":hw: text-center sm:text-left">Content</div>'
13
- const result = extractCompileClasses(content)
14
-
15
- expect(result.size).toBe(1)
16
- const classes = Array.from(result.values())[0]
17
- expect(classes.sort()).toEqual(['sm:text-left', 'text-center'])
18
- })
19
-
20
- it('should extract classes with custom trigger', () => {
21
- const content = '<div class=":hw: p-4 m-2">Content</div>'
22
- const result = extractCompileClasses(content, { trigger: ':hw:' })
23
-
24
- expect(result.size).toBe(1)
25
- const classes = Array.from(result.values())[0]
26
- expect(classes.sort()).toEqual(['m-2', 'p-4'])
27
- })
28
-
29
- it('should handle multiple elements', () => {
30
- const content = `
31
- <div class=":hw: text-center sm:text-left">
32
- <span class=":hw: font-bold text-red-500">Text</span>
33
- </div>
34
- `
35
- const result = extractCompileClasses(content)
36
-
37
- expect(result.size).toBe(2)
38
- })
39
-
40
- it('should ignore elements without trigger', () => {
41
- const content = `
42
- <div class="regular-class another-class">
43
- <span class=":hw: compiled-class">Compiled</span>
44
- </div>
45
- `
46
- const result = extractCompileClasses(content)
47
-
48
- expect(result.size).toBe(1)
49
- const classes = Array.from(result.values())[0]
50
- expect(classes.sort()).toEqual(['compiled-class'])
51
- })
52
-
53
- it('should handle className attribute (React)', () => {
54
- const content = '<div className=":hw: flex items-center">Content</div>'
55
- const result = extractCompileClasses(content)
56
-
57
- expect(result.size).toBe(1)
58
- const classes = Array.from(result.values())[0]
59
- expect(classes.sort()).toEqual(['flex', 'items-center'])
60
- })
61
-
62
- it('should deduplicate identical class groups', () => {
63
- const content = `
64
- <div class=":hw: p-4 m-2">First</div>
65
- <div class=":hw: m-2 p-4">Second</div>
66
- `
67
- const result = extractCompileClasses(content)
68
-
69
- // Should be 1 because both have same classes (order doesn't matter after sorting)
70
- expect(result.size).toBe(1)
71
- })
72
- })
73
-
74
- describe('generateCompiledClassNames', () => {
75
- it('should generate unique class names', () => {
76
- const compiledClasses = new Map([
77
- ['text-center sm:text-left', ['text-center', 'sm:text-left']],
78
- ['p-4 m-2', ['p-4', 'm-2']],
79
- ])
80
-
81
- const result = generateCompiledClassNames(compiledClasses)
82
-
83
- expect(result.size).toBe(2)
84
- for (const className of result.values()) {
85
- expect(className).toMatch(/^hw-[a-z0-9]+$/)
86
- }
87
- })
88
-
89
- it('should use custom prefix', () => {
90
- const compiledClasses = new Map([
91
- ['text-center', ['text-center']],
92
- ])
93
-
94
- const result = generateCompiledClassNames(compiledClasses, {
95
- classPrefix: 'hw-',
96
- })
97
-
98
- const className = Array.from(result.values())[0]
99
- expect(className).toMatch(/^hw-[a-z0-9]+$/)
100
- })
101
-
102
- it('should generate same hash for same content', () => {
103
- const compiledClasses = new Map([
104
- ['text-center sm:text-left', ['text-center', 'sm:text-left']],
105
- ])
106
-
107
- const result1 = generateCompiledClassNames(compiledClasses)
108
- const result2 = generateCompiledClassNames(compiledClasses)
109
-
110
- expect(Array.from(result1.values())[0]).toBe(Array.from(result2.values())[0])
111
- })
112
- })
113
-
114
- describe('transformContent', () => {
115
- it('should replace compile markers with generated class names', () => {
116
- const content = '<div class=":hw: text-center sm:text-left">Content</div>'
117
- const classMap = new Map([
118
- ['sm:text-left text-center', 'hw-abc123'],
119
- ])
120
-
121
- const result = transformContent(content, classMap)
122
-
123
- expect(result).toBe('<div class="hw-abc123">Content</div>')
124
- })
125
-
126
- it('should handle multiple replacements', () => {
127
- const content = `
128
- <div class=":hw: p-4 m-2">First</div>
129
- <span class=":hw: font-bold">Second</span>
130
- `
131
- const classMap = new Map([
132
- ['m-2 p-4', 'hw-abc'],
133
- ['font-bold', 'hw-def'],
134
- ])
135
-
136
- const result = transformContent(content, classMap)
137
-
138
- expect(result).toContain('class="hw-abc"')
139
- expect(result).toContain('class="hw-def"')
140
- expect(result).not.toContain(':hw:')
141
- })
142
-
143
- it('should preserve non-compile classes', () => {
144
- const content = '<div class="regular-class another-class">Content</div>'
145
- const classMap = new Map()
146
-
147
- const result = transformContent(content, classMap)
148
-
149
- expect(result).toBe(content)
150
- })
151
-
152
- it('should handle className attribute', () => {
153
- const content = '<div className=":hw: flex items-center">Content</div>'
154
- const classMap = new Map([
155
- ['flex items-center', 'hw-xyz'],
156
- ])
157
-
158
- const result = transformContent(content, classMap)
159
-
160
- expect(result).toBe('<div className="hw-xyz">Content</div>')
161
- })
162
- })
163
-
164
- describe('CompileClassTransformer', () => {
165
- it('should process file and extract compile classes', () => {
166
- const transformer = new CompileClassTransformer()
167
- const content = '<div class=":hw: text-center sm:text-left">Content</div>'
168
-
169
- const result = transformer.processFile(content)
170
-
171
- expect(result.hasChanges).toBe(true)
172
- expect(result.content).not.toContain(':hw:')
173
- expect(result.content).toMatch(/class="hw-[a-z0-9]+"/)
174
- })
175
-
176
- it('should not modify content without compile markers', () => {
177
- const transformer = new CompileClassTransformer()
178
- const content = '<div class="regular-class">Content</div>'
179
-
180
- const result = transformer.processFile(content)
181
-
182
- expect(result.hasChanges).toBe(false)
183
- expect(result.content).toBe(content)
184
- })
185
-
186
- it('should accumulate compiled classes across multiple files', () => {
187
- const transformer = new CompileClassTransformer()
188
-
189
- transformer.processFile('<div class=":hw: p-4 m-2">File 1</div>')
190
- transformer.processFile('<div class=":hw: font-bold">File 2</div>')
191
-
192
- const compiledClasses = transformer.getCompiledClasses()
193
- expect(compiledClasses.size).toBe(2)
194
- })
195
-
196
- it('should deduplicate identical class groups across files', () => {
197
- const transformer = new CompileClassTransformer()
198
-
199
- transformer.processFile('<div class=":hw: p-4 m-2">File 1</div>')
200
- transformer.processFile('<div class=":hw: m-2 p-4">File 2</div>')
201
-
202
- const compiledClasses = transformer.getCompiledClasses()
203
- expect(compiledClasses.size).toBe(1)
204
- })
205
-
206
- it('should provide accurate statistics', () => {
207
- const transformer = new CompileClassTransformer()
208
-
209
- transformer.processFile('<div class=":hw: p-4 m-2">File 1</div>')
210
- transformer.processFile('<div class=":hw: font-bold text-sm">File 2</div>')
211
-
212
- const stats = transformer.getStats()
213
- expect(stats.totalGroups).toBe(2)
214
- expect(stats.totalUtilities).toBe(4) // p-4, m-2, font-bold, text-sm
215
- expect(stats.averageUtilitiesPerGroup).toBe(2)
216
- })
217
-
218
- it('should reset state correctly', () => {
219
- const transformer = new CompileClassTransformer()
220
-
221
- transformer.processFile('<div class=":hw: p-4 m-2">Content</div>')
222
- expect(transformer.getCompiledClasses().size).toBe(1)
223
-
224
- transformer.reset()
225
- expect(transformer.getCompiledClasses().size).toBe(0)
226
- })
227
-
228
- it('should use custom options', () => {
229
- const transformer = new CompileClassTransformer({
230
- trigger: ':hw:',
231
- classPrefix: 'crosswind-',
232
- })
233
-
234
- const content = '<div class=":hw: p-4 m-2">Content</div>'
235
- const result = transformer.processFile(content)
236
-
237
- expect(result.hasChanges).toBe(true)
238
- expect(result.content).toMatch(/class="crosswind-[a-z0-9]+"/)
239
- })
240
- })
241
-
242
- describe('Edge Cases', () => {
243
- it('should handle empty trigger marker', () => {
244
- const content = '<div class=":hw: ">Content</div>'
245
- const result = extractCompileClasses(content)
246
-
247
- expect(result.size).toBe(0)
248
- })
249
-
250
- it('should handle trigger with extra spaces', () => {
251
- const content = '<div class=":hw: p-4 m-2 ">Content</div>'
252
- const result = extractCompileClasses(content)
253
-
254
- expect(result.size).toBe(1)
255
- const classes = Array.from(result.values())[0]
256
- expect(classes.sort()).toEqual(['m-2', 'p-4'])
257
- })
258
-
259
- it('should handle single class', () => {
260
- const content = '<div class=":hw: single-class">Content</div>'
261
- const result = extractCompileClasses(content)
262
-
263
- expect(result.size).toBe(1)
264
- const classes = Array.from(result.values())[0]
265
- expect(classes.sort()).toEqual(['single-class'])
266
- })
267
-
268
- it('should handle classes with special characters', () => {
269
- const content = '<div class=":hw: hover:bg-blue-500 md:w-1/2">Content</div>'
270
- const result = extractCompileClasses(content)
271
-
272
- expect(result.size).toBe(1)
273
- const classes = Array.from(result.values())[0].sort()
274
- expect(classes).toContain('hover:bg-blue-500')
275
- expect(classes).toContain('md:w-1/2')
276
- })
277
-
278
- it('should handle single quotes', () => {
279
- const content = '<div class=\':hw: p-4 m-2\'>Content</div>'
280
- const result = extractCompileClasses(content)
281
-
282
- expect(result.size).toBe(1)
283
- const classes = Array.from(result.values())[0]
284
- expect(classes.sort()).toEqual(['m-2', 'p-4'])
285
- })
286
- })
287
- })