@pyreon/unistyle 0.24.5 → 0.24.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.
Files changed (50) hide show
  1. package/package.json +7 -9
  2. package/src/__tests__/alignContent.test.ts +0 -121
  3. package/src/__tests__/borderRadius.test.ts +0 -125
  4. package/src/__tests__/camelToKebab.test.ts +0 -44
  5. package/src/__tests__/context.test.ts +0 -147
  6. package/src/__tests__/createMediaQueries.test.ts +0 -98
  7. package/src/__tests__/edge.test.ts +0 -164
  8. package/src/__tests__/enrichTheme.test.ts +0 -56
  9. package/src/__tests__/extendCss.test.ts +0 -45
  10. package/src/__tests__/index.test.ts +0 -79
  11. package/src/__tests__/makeItResponsive.test.ts +0 -431
  12. package/src/__tests__/manifest-snapshot.test.ts +0 -34
  13. package/src/__tests__/native-marker.test.ts +0 -9
  14. package/src/__tests__/optimizeBreakpointDeltas.test.ts +0 -124
  15. package/src/__tests__/processDescriptor.test.ts +0 -322
  16. package/src/__tests__/responsive.test.ts +0 -221
  17. package/src/__tests__/special-keys.test.ts +0 -120
  18. package/src/__tests__/styles.test.ts +0 -273
  19. package/src/__tests__/unistyle.browser.test.tsx +0 -169
  20. package/src/__tests__/units.test.ts +0 -134
  21. package/src/context.tsx +0 -44
  22. package/src/enrichTheme.ts +0 -42
  23. package/src/env.d.ts +0 -6
  24. package/src/index.ts +0 -91
  25. package/src/manifest.ts +0 -197
  26. package/src/responsive/breakpoints.ts +0 -15
  27. package/src/responsive/createMediaQueries.ts +0 -43
  28. package/src/responsive/index.ts +0 -15
  29. package/src/responsive/makeItResponsive.ts +0 -223
  30. package/src/responsive/normalizeTheme.ts +0 -79
  31. package/src/responsive/optimizeBreakpointDeltas.ts +0 -190
  32. package/src/responsive/optimizeTheme.ts +0 -60
  33. package/src/responsive/sortBreakpoints.ts +0 -10
  34. package/src/responsive/transformTheme.ts +0 -54
  35. package/src/styles/alignContent.ts +0 -62
  36. package/src/styles/extendCss.ts +0 -26
  37. package/src/styles/index.ts +0 -16
  38. package/src/styles/shorthands/borderRadius.ts +0 -89
  39. package/src/styles/shorthands/edge.ts +0 -108
  40. package/src/styles/shorthands/index.ts +0 -4
  41. package/src/styles/styles/camelToKebab.ts +0 -3
  42. package/src/styles/styles/index.ts +0 -132
  43. package/src/styles/styles/processDescriptor.ts +0 -136
  44. package/src/styles/styles/propertyMap.ts +0 -438
  45. package/src/styles/styles/types.ts +0 -368
  46. package/src/types.ts +0 -175
  47. package/src/units/index.ts +0 -6
  48. package/src/units/stripUnit.ts +0 -25
  49. package/src/units/value.ts +0 -47
  50. package/src/units/values.ts +0 -40
@@ -1,431 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest'
2
-
3
- vi.mock('@pyreon/ui-core', () => ({
4
- isEmpty: (val: unknown) =>
5
- val == null || (typeof val === 'object' && Object.keys(val as object).length === 0),
6
- set: (obj: any, path: (string | number)[], value: unknown) => {
7
- let current = obj
8
- for (let i = 0; i < path.length - 1; i++) {
9
- const key = path[i] as string | number
10
- if (current[key] == null || typeof current[key] !== 'object') {
11
- current[key] = {}
12
- }
13
- current = current[key]
14
- }
15
- const lastKey = path[path.length - 1] as string | number
16
- current[lastKey] = value
17
- },
18
- }))
19
-
20
- import makeItResponsive from '../responsive/makeItResponsive'
21
-
22
- const mockCss = (strings: TemplateStringsArray, ...vals: any[]) => {
23
- let r = ''
24
- for (let i = 0; i < strings.length; i++) {
25
- r += strings[i]
26
- if (i < vals.length) r += String(vals[i])
27
- }
28
- return r
29
- }
30
-
31
- const mockStyles = ({ theme }: { theme: Record<string, unknown> }) => {
32
- return Object.entries(theme)
33
- .map(([k, v]) => `${k}: ${v};`)
34
- .join(' ')
35
- }
36
-
37
- describe('makeItResponsive', () => {
38
- it('returns empty string when customTheme is empty/undefined', () => {
39
- const responsive = makeItResponsive({
40
- key: 'styles',
41
- css: mockCss,
42
- styles: mockStyles,
43
- })
44
-
45
- const result = responsive({ theme: {} })
46
- expect(result).toBe('')
47
- })
48
-
49
- it('returns empty string when customTheme is empty object', () => {
50
- const responsive = makeItResponsive({
51
- theme: {},
52
- key: 'styles',
53
- css: mockCss,
54
- styles: mockStyles,
55
- })
56
-
57
- const result = responsive({ theme: {} })
58
- expect(result).toBe('')
59
- })
60
-
61
- it('without breakpoints: wraps styles output in css template', () => {
62
- const responsive = makeItResponsive({
63
- theme: { color: 'red' },
64
- css: mockCss,
65
- styles: mockStyles,
66
- })
67
-
68
- const result = responsive({ theme: {} })
69
- expect(result).toContain('color: red;')
70
- })
71
-
72
- it('uses props[key] when theme is not provided', () => {
73
- const responsive = makeItResponsive({
74
- key: 'myStyles',
75
- css: mockCss,
76
- styles: mockStyles,
77
- })
78
-
79
- const result = responsive({
80
- theme: {},
81
- myStyles: { fontSize: '16px' },
82
- })
83
-
84
- expect(result).toContain('fontSize: 16px;')
85
- })
86
-
87
- it('with breakpoints and __PYREON__: returns array of media-wrapped styles per breakpoint', () => {
88
- const sortedBreakpoints = ['xs', 'sm']
89
- const media: Record<string, (strings: TemplateStringsArray, ...vals: any[]) => string> = {
90
- xs: mockCss,
91
- sm: (strings: TemplateStringsArray, ...vals: any[]) => {
92
- let r = '@media (min-width: 36em) {'
93
- for (let i = 0; i < strings.length; i++) {
94
- r += strings[i]
95
- if (i < vals.length) r += String(vals[i])
96
- }
97
- r += '}'
98
- return r
99
- },
100
- }
101
-
102
- const responsive = makeItResponsive({
103
- theme: { color: { xs: 'red', sm: 'blue' } },
104
- css: mockCss,
105
- styles: mockStyles,
106
- normalize: true,
107
- })
108
-
109
- const result = responsive({
110
- theme: {
111
- breakpoints: { xs: 0, sm: 576 },
112
- __PYREON__: { sortedBreakpoints, media },
113
- },
114
- })
115
-
116
- expect(Array.isArray(result)).toBe(true)
117
- expect(result).toHaveLength(2)
118
- })
119
-
120
- it('caching: second call with same internalTheme object returns same result', () => {
121
- const sortedBreakpoints = ['xs', 'sm']
122
- const media: Record<string, (strings: TemplateStringsArray, ...vals: any[]) => string> = {
123
- xs: mockCss,
124
- sm: mockCss,
125
- }
126
-
127
- const themeObj = { color: { xs: 'red', sm: 'blue' } }
128
-
129
- const responsive = makeItResponsive({
130
- theme: themeObj,
131
- css: mockCss,
132
- styles: mockStyles,
133
- })
134
-
135
- const globalTheme = {
136
- breakpoints: { xs: 0, sm: 576 },
137
- __PYREON__: { sortedBreakpoints, media },
138
- }
139
-
140
- const result1 = responsive({ theme: globalTheme })
141
- const result2 = responsive({ theme: globalTheme })
142
-
143
- expect(result1).toEqual(result2)
144
- })
145
-
146
- it('normalize=false skips normalizeTheme step', () => {
147
- const sortedBreakpoints = ['xs', 'sm']
148
- const media: Record<string, (strings: TemplateStringsArray, ...vals: any[]) => string> = {
149
- xs: mockCss,
150
- sm: mockCss,
151
- }
152
-
153
- // When normalize=false, object values are not expanded across breakpoints.
154
- // Provide a pre-normalized theme (object keyed by breakpoint names).
155
- const responsive = makeItResponsive({
156
- theme: { color: { xs: 'red', sm: 'blue' } },
157
- css: mockCss,
158
- styles: mockStyles,
159
- normalize: false,
160
- })
161
-
162
- const result = responsive({
163
- theme: {
164
- breakpoints: { xs: 0, sm: 576 },
165
- __PYREON__: { sortedBreakpoints, media },
166
- },
167
- })
168
-
169
- expect(Array.isArray(result)).toBe(true)
170
- })
171
-
172
- describe('delta optimization (mirrors vitus-labs)', () => {
173
- it('strips re-emitted unchanged declarations across breakpoints', () => {
174
- // mockStyles emits `color: red; padding: 0;` at xs and the same color
175
- // with a different padding at sm. The delta optimizer should drop
176
- // `color: red` from sm because it's already cascaded from xs via
177
- // `@media (min-width: …)`.
178
- const sortedBreakpoints = ['xs', 'sm']
179
- const captured: Record<string, string> = {}
180
- const media: Record<string, (s: TemplateStringsArray, ...v: any[]) => string> = {
181
- xs: (s, ...vals) => {
182
- let out = ''
183
- for (let i = 0; i < s.length; i++) {
184
- out += s[i]
185
- if (i < vals.length) out += String(vals[i])
186
- }
187
- captured.xs = out
188
- return out
189
- },
190
- sm: (s, ...vals) => {
191
- let out = ''
192
- for (let i = 0; i < s.length; i++) {
193
- out += s[i]
194
- if (i < vals.length) out += String(vals[i])
195
- }
196
- captured.sm = out
197
- return out
198
- },
199
- }
200
-
201
- const responsive = makeItResponsive({
202
- theme: { color: { xs: 'red', sm: 'red' }, padding: { xs: '0', sm: '1rem' } },
203
- css: mockCss,
204
- styles: mockStyles,
205
- normalize: true,
206
- })
207
-
208
- responsive({
209
- theme: {
210
- breakpoints: { xs: 0, sm: 576 },
211
- __PYREON__: { sortedBreakpoints, media },
212
- },
213
- })
214
-
215
- // xs sees full output
216
- expect(captured.xs).toContain('color: red;')
217
- expect(captured.xs).toContain('padding: 0;')
218
- // sm sees only the delta — color is in cascade already
219
- expect(captured.sm).toContain('padding: 1rem;')
220
- expect(captured.sm).not.toContain('color:')
221
- })
222
-
223
- it('skips the media-template call entirely when a breakpoint has no deltas', () => {
224
- const sortedBreakpoints = ['xs', 'sm']
225
- let xsCalls = 0
226
- let smCalls = 0
227
- const media: Record<string, (s: TemplateStringsArray, ...v: any[]) => string> = {
228
- xs: (s, ...vals) => {
229
- xsCalls++
230
- let out = ''
231
- for (let i = 0; i < s.length; i++) {
232
- out += s[i]
233
- if (i < vals.length) out += String(vals[i])
234
- }
235
- return out
236
- },
237
- sm: (s, ...vals) => {
238
- smCalls++
239
- let out = ''
240
- for (let i = 0; i < s.length; i++) {
241
- out += s[i]
242
- if (i < vals.length) out += String(vals[i])
243
- }
244
- return out
245
- },
246
- }
247
-
248
- const responsive = makeItResponsive({
249
- // Identical values at both breakpoints — sm produces zero deltas.
250
- theme: { color: { xs: 'red', sm: 'red' } },
251
- css: mockCss,
252
- styles: mockStyles,
253
- })
254
-
255
- const result = responsive({
256
- theme: {
257
- breakpoints: { xs: 0, sm: 576 },
258
- __PYREON__: { sortedBreakpoints, media },
259
- },
260
- })
261
-
262
- // xs renders (has content); sm produces no @media wrapper at all
263
- expect(xsCalls).toBe(1)
264
- expect(smCalls).toBe(0)
265
- // sm slot is the empty-string sentinel
266
- expect((result as unknown[])[1]).toBe('')
267
- })
268
- })
269
-
270
- describe('stringify fallback (mirrors vitus-labs)', () => {
271
- // Where the two paths diverge observably: `styles` is invoked
272
- // ONCE per breakpoint in the optimized path (for stringification),
273
- // and TWICE per breakpoint in the fallback path (once for
274
- // stringify which returns null, then again to re-render against
275
- // the engine for the @media wrapper). With 2 breakpoints that's
276
- // 2 calls vs 4 calls — clean observable distinction.
277
-
278
- it('takes the unoptimized path when styles result has [object Foo] toString', () => {
279
- // Foreign-engine result whose default toString is `[object ForeignResult]`.
280
- // stringifyResult returns null → canOptimize=false → fallback path.
281
- class ForeignResult {
282
- constructor(public payload: string) {}
283
- // Default toString → "[object ForeignResult]"
284
- }
285
-
286
- const sortedBreakpoints = ['xs', 'sm']
287
- const media: Record<string, (s: TemplateStringsArray, ...v: any[]) => string> = {
288
- xs: mockCss,
289
- sm: mockCss,
290
- }
291
-
292
- let stylesCalls = 0
293
- const stylesReturningForeign = ({ theme }: { theme: Record<string, unknown> }) => {
294
- stylesCalls++
295
- return new ForeignResult(JSON.stringify(theme))
296
- }
297
-
298
- // Distinct values per breakpoint so optimizeTheme keeps both keys
299
- // (identical values get deduplicated upstream and never reach the
300
- // optimization-vs-fallback split).
301
- const responsive = makeItResponsive({
302
- theme: { color: { xs: 'red', sm: 'blue' } },
303
- css: mockCss,
304
- styles: stylesReturningForeign as any,
305
- })
306
-
307
- responsive({
308
- theme: {
309
- breakpoints: { xs: 0, sm: 576 },
310
- __PYREON__: { sortedBreakpoints, media },
311
- },
312
- })
313
-
314
- // Fallback signature: stringify pass + re-render pass = 2 calls per bp
315
- expect(stylesCalls).toBe(4)
316
- })
317
-
318
- it('takes the optimized path for plain-string styles results', () => {
319
- const sortedBreakpoints = ['xs', 'sm']
320
- const media: Record<string, (s: TemplateStringsArray, ...v: any[]) => string> = {
321
- xs: mockCss,
322
- sm: mockCss,
323
- }
324
-
325
- let stylesCalls = 0
326
- const stylesCountingMock = (args: { theme: Record<string, unknown> }) => {
327
- stylesCalls++
328
- return mockStyles(args)
329
- }
330
-
331
- const responsive = makeItResponsive({
332
- theme: { color: { xs: 'red', sm: 'blue' } },
333
- css: mockCss,
334
- styles: stylesCountingMock,
335
- })
336
-
337
- responsive({
338
- theme: {
339
- breakpoints: { xs: 0, sm: 576 },
340
- __PYREON__: { sortedBreakpoints, media },
341
- },
342
- })
343
-
344
- // Optimized signature: stringify pass only = 1 call per bp
345
- expect(stylesCalls).toBe(2)
346
- })
347
- })
348
-
349
- describe('render-output cache (mirrors vitus-labs)', () => {
350
- it('returns the same rendered output by reference when called twice with stable theme + internal-theme refs', () => {
351
- const sortedBreakpoints = ['xs', 'sm']
352
- let xsCalls = 0
353
- const media: Record<string, (s: TemplateStringsArray, ...v: any[]) => string> = {
354
- xs: (s, ...vals) => {
355
- xsCalls++
356
- let out = ''
357
- for (let i = 0; i < s.length; i++) {
358
- out += s[i]
359
- if (i < vals.length) out += String(vals[i])
360
- }
361
- return out
362
- },
363
- sm: mockCss,
364
- }
365
-
366
- const themeObj = { color: { xs: 'red', sm: 'blue' } }
367
- const globalTheme = {
368
- breakpoints: { xs: 0, sm: 576 },
369
- __PYREON__: { sortedBreakpoints, media },
370
- }
371
-
372
- const responsive = makeItResponsive({
373
- theme: themeObj,
374
- css: mockCss,
375
- styles: mockStyles,
376
- })
377
-
378
- const result1 = responsive({ theme: globalTheme })
379
- const xsCallsAfterFirst = xsCalls
380
- const result2 = responsive({ theme: globalTheme })
381
-
382
- // Same identity means the rendered cache hit (no re-rendering)
383
- expect(result2).toBe(result1)
384
- // Render cache hit means the media template was NOT called again
385
- expect(xsCalls).toBe(xsCallsAfterFirst)
386
- })
387
-
388
- it('re-renders when the outer theme reference changes (e.g. provider value swap)', () => {
389
- const sortedBreakpoints = ['xs', 'sm']
390
- let xsCalls = 0
391
- const media: Record<string, (s: TemplateStringsArray, ...v: any[]) => string> = {
392
- xs: (s, ...vals) => {
393
- xsCalls++
394
- let out = ''
395
- for (let i = 0; i < s.length; i++) {
396
- out += s[i]
397
- if (i < vals.length) out += String(vals[i])
398
- }
399
- return out
400
- },
401
- sm: mockCss,
402
- }
403
-
404
- const themeObj = { color: { xs: 'red', sm: 'blue' } }
405
-
406
- const responsive = makeItResponsive({
407
- theme: themeObj,
408
- css: mockCss,
409
- styles: mockStyles,
410
- })
411
-
412
- // Two distinct outer-theme objects with the same content
413
- const globalA = {
414
- breakpoints: { xs: 0, sm: 576 },
415
- __PYREON__: { sortedBreakpoints, media },
416
- }
417
- const globalB = {
418
- breakpoints: { xs: 0, sm: 576 },
419
- __PYREON__: { sortedBreakpoints, media },
420
- }
421
-
422
- responsive({ theme: globalA })
423
- const callsAfterA = xsCalls
424
- responsive({ theme: globalB })
425
-
426
- // New outer theme object → no render cache hit → media template re-runs
427
- expect(xsCalls).toBeGreaterThan(callsAfterA)
428
- })
429
-
430
- })
431
- })
@@ -1,34 +0,0 @@
1
- import {
2
- renderApiReferenceEntries,
3
- renderLlmsFullSection,
4
- renderLlmsTxtLine,
5
- } from '@pyreon/manifest'
6
- import manifest from '../manifest'
7
-
8
- describe('gen-docs — unistyle snapshot', () => {
9
- it('renders a llms.txt bullet starting with the package prefix', () => {
10
- const line = renderLlmsTxtLine(manifest)
11
- expect(line.startsWith('- @pyreon/unistyle —')).toBe(true)
12
- })
13
-
14
- it('renders a llms-full.txt section with the right header', () => {
15
- const section = renderLlmsFullSection(manifest)
16
- expect(section.startsWith('## @pyreon/unistyle —')).toBe(true)
17
- expect(section).toContain('```typescript')
18
- })
19
-
20
- it('renders MCP api-reference entries for every api[] item', () => {
21
- const record = renderApiReferenceEntries(manifest)
22
- expect(Object.keys(record).sort()).toEqual([
23
- 'unistyle/alignContent',
24
- 'unistyle/breakpoints',
25
- 'unistyle/createMediaQueries',
26
- 'unistyle/enrichTheme',
27
- 'unistyle/extendCss',
28
- 'unistyle/makeItResponsive',
29
- 'unistyle/stripUnit',
30
- 'unistyle/styles',
31
- 'unistyle/value',
32
- ])
33
- })
34
- })
@@ -1,9 +0,0 @@
1
- import { isNativeCompat } from '@pyreon/core'
2
- import { describe, expect, it } from 'vitest'
3
- import UnistyleProvider from '../context'
4
-
5
- describe('native-compat marker — @pyreon/unistyle', () => {
6
- it('Provider is marked native', () => {
7
- expect(isNativeCompat(UnistyleProvider)).toBe(true)
8
- })
9
- })
@@ -1,124 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { optimizeBreakpointDeltas } from '../responsive'
3
-
4
- describe('optimizeBreakpointDeltas', () => {
5
- describe('cascade pruning', () => {
6
- it('returns input unchanged when there is one or fewer breakpoints', () => {
7
- expect(optimizeBreakpointDeltas([])).toEqual([])
8
- expect(optimizeBreakpointDeltas(['color: red;'])).toEqual(['color: red;'])
9
- })
10
-
11
- it('strips re-emitted unchanged declarations from later breakpoints', () => {
12
- const out = optimizeBreakpointDeltas([
13
- 'color: red; padding: 0;',
14
- 'color: red; padding: 1rem;',
15
- ])
16
- expect(out[0]).toBe('color: red; padding: 0;')
17
- // `color: red` was already in the cascade — only padding survives
18
- expect(out[1]).toBe('padding: 1rem;')
19
- })
20
-
21
- it('keeps changed declarations across multiple breakpoints', () => {
22
- const out = optimizeBreakpointDeltas([
23
- 'color: red; font-size: 12px;',
24
- 'color: blue; font-size: 12px;',
25
- 'color: blue; font-size: 16px;',
26
- ])
27
- expect(out[0]).toBe('color: red; font-size: 12px;')
28
- expect(out[1]).toBe('color: blue;')
29
- expect(out[2]).toBe('font-size: 16px;')
30
- })
31
-
32
- it('emits empty string when a later breakpoint adds no deltas', () => {
33
- const out = optimizeBreakpointDeltas([
34
- 'color: red; padding: 0;',
35
- 'color: red; padding: 0;',
36
- ])
37
- expect(out[0]).toBe('color: red; padding: 0;')
38
- expect(out[1]).toBe('')
39
- })
40
-
41
- it('passes through empty / null breakpoints unchanged', () => {
42
- const out = optimizeBreakpointDeltas(['color: red;', '', 'color: blue;'])
43
- expect(out[0]).toBe('color: red;')
44
- expect(out[1]).toBe('')
45
- expect(out[2]).toBe('color: blue;')
46
- })
47
- })
48
-
49
- describe('parser edge cases', () => {
50
- it('skips colons inside parens (linear-gradient args)', () => {
51
- const out = optimizeBreakpointDeltas([
52
- 'background: linear-gradient(red 0%, blue 100%);',
53
- 'background: linear-gradient(red 0%, blue 100%);',
54
- ])
55
- expect(out[0]).toBe('background: linear-gradient(red 0%, blue 100%);')
56
- // Same value cascades — delta is empty
57
- expect(out[1]).toBe('')
58
- })
59
-
60
- it('skips semicolons inside quoted strings (content: ";")', () => {
61
- const out = optimizeBreakpointDeltas([
62
- `content: ";"; color: red;`,
63
- `content: ";"; color: blue;`,
64
- ])
65
- // Both declarations parsed correctly on bp1; bp2 only color delta
66
- expect(out[0]).toContain(`content: ";";`)
67
- expect(out[0]).toContain('color: red;')
68
- expect(out[1]).toBe('color: blue;')
69
- })
70
-
71
- it('treats nested selector blocks as opaque, deduped by exact text', () => {
72
- const out = optimizeBreakpointDeltas([
73
- '&:hover { color: red; } padding: 0;',
74
- '&:hover { color: red; } padding: 1rem;',
75
- ])
76
- // The hover block dedupes; padding delta survives
77
- expect(out[1]).not.toContain('&:hover')
78
- expect(out[1]).toContain('padding: 1rem;')
79
- })
80
-
81
- it('keeps differently-shaped nested blocks across breakpoints', () => {
82
- const out = optimizeBreakpointDeltas([
83
- '&:hover { color: red; }',
84
- '&:hover { color: blue; }',
85
- ])
86
- expect(out[0]).toContain('&:hover { color: red; }')
87
- // Different inner text → not deduped
88
- expect(out[1]).toContain('&:hover { color: blue; }')
89
- })
90
-
91
- it('handles trailing declarations with no terminating semicolon', () => {
92
- const out = optimizeBreakpointDeltas(['color: red', 'color: blue'])
93
- expect(out[0]).toBe('color: red;')
94
- expect(out[1]).toBe('color: blue;')
95
- })
96
-
97
- it('preserves @supports / @media-style nested blocks as opaque blocks', () => {
98
- const out = optimizeBreakpointDeltas([
99
- '@supports (display: grid) { display: grid; }',
100
- '@supports (display: grid) { display: grid; } color: red;',
101
- ])
102
- expect(out[0]).toBe('@supports (display: grid) { display: grid; }')
103
- // @supports block dedupes; color is new
104
- expect(out[1]).toBe('color: red;')
105
- })
106
-
107
- it('keeps shorthand and longhand decls separately (no shorthand modeling)', () => {
108
- const out = optimizeBreakpointDeltas([
109
- 'padding: 1rem;',
110
- 'padding-top: 0;',
111
- ])
112
- // Different `prop` keys → both retained
113
- expect(out[0]).toBe('padding: 1rem;')
114
- expect(out[1]).toBe('padding-top: 0;')
115
- })
116
-
117
- it('keeps malformed declaration-shaped fragments without losing them', () => {
118
- const out = optimizeBreakpointDeltas([':abc;', ':abc;'])
119
- // No prop name (starts with `:`) → kept as opaque block; deduped on bp2
120
- expect(out[0]).toBe(':abc;')
121
- expect(out[1]).toBe('')
122
- })
123
- })
124
- })