@pyreon/styler 0.11.4 → 0.11.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 (36) hide show
  1. package/README.md +27 -23
  2. package/lib/index.d.ts +9 -2
  3. package/lib/index.js +47 -4
  4. package/package.json +22 -22
  5. package/src/ThemeProvider.ts +10 -3
  6. package/src/__tests__/ThemeProvider.test.ts +21 -21
  7. package/src/__tests__/benchmark.bench.ts +56 -45
  8. package/src/__tests__/composition-chain.test.ts +200 -151
  9. package/src/__tests__/forward.test.ts +122 -122
  10. package/src/__tests__/globalStyle.test.ts +18 -18
  11. package/src/__tests__/hash.test.ts +27 -27
  12. package/src/__tests__/hybrid-injection.test.ts +83 -59
  13. package/src/__tests__/index.ts +10 -10
  14. package/src/__tests__/insertion-effect.test.ts +45 -32
  15. package/src/__tests__/integration.test.ts +81 -51
  16. package/src/__tests__/keyframes.test.ts +13 -13
  17. package/src/__tests__/memory-growth.test.ts +21 -21
  18. package/src/__tests__/p3-features.test.ts +162 -104
  19. package/src/__tests__/shared.test.ts +51 -33
  20. package/src/__tests__/sheet-advanced.test.ts +227 -227
  21. package/src/__tests__/sheet-split-atrules.test.ts +85 -85
  22. package/src/__tests__/sheet.test.ts +69 -69
  23. package/src/__tests__/styled-ssr.test.ts +36 -28
  24. package/src/__tests__/styled.test.ts +214 -145
  25. package/src/__tests__/theme.test.ts +11 -11
  26. package/src/__tests__/useCSS.test.ts +89 -59
  27. package/src/css.ts +1 -1
  28. package/src/forward.ts +187 -187
  29. package/src/globalStyle.ts +5 -5
  30. package/src/index.ts +15 -15
  31. package/src/keyframes.ts +3 -3
  32. package/src/resolve.ts +14 -14
  33. package/src/shared.ts +2 -2
  34. package/src/sheet.ts +26 -26
  35. package/src/styled.tsx +145 -100
  36. package/src/useCSS.ts +4 -4
@@ -1,13 +1,13 @@
1
- import { afterEach, describe, expect, it } from "vitest"
2
- import { keyframes } from "../keyframes"
3
- import { sheet } from "../sheet"
1
+ import { afterEach, describe, expect, it } from 'vitest'
2
+ import { keyframes } from '../keyframes'
3
+ import { sheet } from '../sheet'
4
4
 
5
- describe("keyframes", () => {
5
+ describe('keyframes', () => {
6
6
  afterEach(() => {
7
7
  sheet.reset()
8
8
  })
9
9
 
10
- it("returns a KeyframesResult with a name property", () => {
10
+ it('returns a KeyframesResult with a name property', () => {
11
11
  const fadeIn = keyframes`
12
12
  from { opacity: 0; }
13
13
  to { opacity: 1; }
@@ -15,7 +15,7 @@ describe("keyframes", () => {
15
15
  expect(fadeIn.name).toMatch(/^pyr-kf-/)
16
16
  })
17
17
 
18
- it("returns pyr-kf- prefix", () => {
18
+ it('returns pyr-kf- prefix', () => {
19
19
  const fadeIn = keyframes`
20
20
  from { opacity: 0; }
21
21
  to { opacity: 1; }
@@ -23,19 +23,19 @@ describe("keyframes", () => {
23
23
  expect(fadeIn.name).toMatch(/^pyr-kf-[0-9a-z]+$/)
24
24
  })
25
25
 
26
- it("is deterministic — same input produces same name", () => {
26
+ it('is deterministic — same input produces same name', () => {
27
27
  const a = keyframes`from { opacity: 0; } to { opacity: 1; }`
28
28
  const b = keyframes`from { opacity: 0; } to { opacity: 1; }`
29
29
  expect(a.name).toBe(b.name)
30
30
  })
31
31
 
32
- it("different input produces different names", () => {
32
+ it('different input produces different names', () => {
33
33
  const fadeIn = keyframes`from { opacity: 0; } to { opacity: 1; }`
34
34
  const slideIn = keyframes`from { transform: translateX(-100%); } to { transform: translateX(0); }`
35
35
  expect(fadeIn.name).not.toBe(slideIn.name)
36
36
  })
37
37
 
38
- it("supports interpolation values", () => {
38
+ it('supports interpolation values', () => {
39
39
  const from = 0
40
40
  const to = 1
41
41
  const anim = keyframes`
@@ -45,19 +45,19 @@ describe("keyframes", () => {
45
45
  expect(anim.name).toMatch(/^pyr-kf-/)
46
46
  })
47
47
 
48
- it("toString returns the animation name", () => {
48
+ it('toString returns the animation name', () => {
49
49
  const fadeIn = keyframes`from { opacity: 0; } to { opacity: 1; }`
50
50
  expect(fadeIn.toString()).toBe(fadeIn.name)
51
51
  })
52
52
 
53
- it("can be used in template literals for animation property", () => {
53
+ it('can be used in template literals for animation property', () => {
54
54
  const fadeIn = keyframes`from { opacity: 0; } to { opacity: 1; }`
55
55
  const animationValue = `${fadeIn} 0.3s ease-in`
56
56
  expect(animationValue).toContain(fadeIn.name)
57
- expect(animationValue).toContain("0.3s ease-in")
57
+ expect(animationValue).toContain('0.3s ease-in')
58
58
  })
59
59
 
60
- it("handles complex keyframe definitions", () => {
60
+ it('handles complex keyframe definitions', () => {
61
61
  const pulse = keyframes`
62
62
  0% { transform: scale(1); }
63
63
  50% { transform: scale(1.1); }
@@ -1,9 +1,9 @@
1
- import { afterEach, beforeEach, describe, expect, it } from "vitest"
2
- import { createSheet } from "../sheet"
1
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest'
2
+ import { createSheet } from '../sheet'
3
3
 
4
- describe("memory growth", () => {
5
- describe("bounded cache prevents unbounded growth (DOM mode)", () => {
6
- it("cache stays bounded with maxCacheSize", () => {
4
+ describe('memory growth', () => {
5
+ describe('bounded cache prevents unbounded growth (DOM mode)', () => {
6
+ it('cache stays bounded with maxCacheSize', () => {
7
7
  const maxSize = 50
8
8
  const s = createSheet({ maxCacheSize: maxSize })
9
9
 
@@ -14,7 +14,7 @@ describe("memory growth", () => {
14
14
  expect(s.cacheSize).toBeLessThanOrEqual(maxSize * 1.5)
15
15
  })
16
16
 
17
- it("cache eviction preserves recent entries", () => {
17
+ it('cache eviction preserves recent entries', () => {
18
18
  const maxSize = 20
19
19
  const s = createSheet({ maxCacheSize: maxSize })
20
20
 
@@ -33,7 +33,7 @@ describe("memory growth", () => {
33
33
  }
34
34
  })
35
35
 
36
- it("handles rapid insertions without memory issues", () => {
36
+ it('handles rapid insertions without memory issues', () => {
37
37
  const s = createSheet({ maxCacheSize: 100 })
38
38
  const iterations = 1000
39
39
 
@@ -46,14 +46,14 @@ describe("memory growth", () => {
46
46
  })
47
47
  })
48
48
 
49
- describe("default cache (large limit, DOM mode)", () => {
49
+ describe('default cache (large limit, DOM mode)', () => {
50
50
  beforeEach(() => {
51
- document.querySelectorAll("style[data-pyreon-styler]").forEach((el) => {
51
+ document.querySelectorAll('style[data-pyreon-styler]').forEach((el) => {
52
52
  el.remove()
53
53
  })
54
54
  })
55
55
 
56
- it("default cache handles many unique rules", () => {
56
+ it('default cache handles many unique rules', () => {
57
57
  const s = createSheet()
58
58
 
59
59
  for (let i = 0; i < 500; i++) {
@@ -63,7 +63,7 @@ describe("memory growth", () => {
63
63
  expect(s.cacheSize).toBe(500)
64
64
  })
65
65
 
66
- it("deduplication prevents growth from repeated rules", () => {
66
+ it('deduplication prevents growth from repeated rules', () => {
67
67
  const s = createSheet()
68
68
 
69
69
  for (let cycle = 0; cycle < 100; cycle++) {
@@ -76,7 +76,7 @@ describe("memory growth", () => {
76
76
  })
77
77
  })
78
78
 
79
- describe("SSR mode memory", () => {
79
+ describe('SSR mode memory', () => {
80
80
  let originalDocument: typeof document
81
81
 
82
82
  beforeEach(() => {
@@ -89,7 +89,7 @@ describe("memory growth", () => {
89
89
  globalThis.document = originalDocument
90
90
  })
91
91
 
92
- it("reset prevents SSR buffer accumulation across requests", () => {
92
+ it('reset prevents SSR buffer accumulation across requests', () => {
93
93
  const s = createSheet()
94
94
 
95
95
  for (let i = 0; i < 100; i++) {
@@ -98,13 +98,13 @@ describe("memory growth", () => {
98
98
  expect(s.getStyles().length).toBeGreaterThan(0)
99
99
 
100
100
  s.reset()
101
- expect(s.getStyles()).toBe("")
101
+ expect(s.getStyles()).toBe('')
102
102
 
103
- s.insert("req2-single: value;")
104
- expect(s.getStyles()).not.toContain("req1-prop")
103
+ s.insert('req2-single: value;')
104
+ expect(s.getStyles()).not.toContain('req1-prop')
105
105
  })
106
106
 
107
- it("keyframes cache does not grow unboundedly", () => {
107
+ it('keyframes cache does not grow unboundedly', () => {
108
108
  const s = createSheet({ maxCacheSize: 20 })
109
109
 
110
110
  for (let i = 0; i < 50; i++) {
@@ -114,7 +114,7 @@ describe("memory growth", () => {
114
114
  expect(s.cacheSize).toBeLessThanOrEqual(50)
115
115
  })
116
116
 
117
- it("global rules cache does not grow unboundedly", () => {
117
+ it('global rules cache does not grow unboundedly', () => {
118
118
  const s = createSheet({ maxCacheSize: 20 })
119
119
 
120
120
  for (let i = 0; i < 50; i++) {
@@ -124,7 +124,7 @@ describe("memory growth", () => {
124
124
  expect(s.cacheSize).toBeLessThanOrEqual(50)
125
125
  })
126
126
 
127
- it("SSR buffer grows with unique rules (expected behavior)", () => {
127
+ it('SSR buffer grows with unique rules (expected behavior)', () => {
128
128
  const s = createSheet()
129
129
  const ruleCount = 100
130
130
 
@@ -138,11 +138,11 @@ describe("memory growth", () => {
138
138
  }
139
139
  })
140
140
 
141
- it("SSR buffer does not duplicate identical rules", () => {
141
+ it('SSR buffer does not duplicate identical rules', () => {
142
142
  const s = createSheet()
143
143
 
144
144
  for (let cycle = 0; cycle < 10; cycle++) {
145
- s.insert("color: red;")
145
+ s.insert('color: red;')
146
146
  }
147
147
 
148
148
  const matches = s.getStyles().match(/color: red;/g)
@@ -1,136 +1,158 @@
1
- import type { VNode } from "@pyreon/core"
2
- import { h } from "@pyreon/core"
3
- import { afterEach, beforeEach, describe, expect, it } from "vitest"
4
- import { css } from "../css"
5
- import { createSheet, StyleSheet } from "../sheet"
6
- import { styled } from "../styled"
7
-
8
- describe("P3 features", () => {
9
- describe("shouldForwardProp", () => {
10
- it("allows custom prop filtering", () => {
11
- const Comp = styled("div", {
12
- shouldForwardProp: (prop) => prop !== "color",
13
- })`display: flex;`
14
-
15
- const vnode = Comp({ color: "red", title: "hello" }) as VNode
1
+ import type { VNode } from '@pyreon/core'
2
+ import { h } from '@pyreon/core'
3
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest'
4
+ import { css } from '../css'
5
+ import { createSheet, StyleSheet } from '../sheet'
6
+ import { styled } from '../styled'
7
+
8
+ describe('P3 features', () => {
9
+ describe('shouldForwardProp', () => {
10
+ it('allows custom prop filtering', () => {
11
+ const Comp = styled('div', {
12
+ shouldForwardProp: (prop) => prop !== 'color',
13
+ })`
14
+ display: flex;
15
+ `
16
+
17
+ const vnode = Comp({ color: 'red', title: 'hello' }) as VNode
16
18
  expect(vnode.props.color).toBeUndefined()
17
- expect(vnode.props.title).toBe("hello")
19
+ expect(vnode.props.title).toBe('hello')
18
20
  })
19
21
 
20
- it("custom filter receives all non-system props", () => {
22
+ it('custom filter receives all non-system props', () => {
21
23
  const forwarded: string[] = []
22
- const Comp = styled("div", {
24
+ const Comp = styled('div', {
23
25
  shouldForwardProp: (prop) => {
24
26
  forwarded.push(prop)
25
27
  return true
26
28
  },
27
- })`display: flex;`
29
+ })`
30
+ display: flex;
31
+ `
28
32
 
29
- Comp({ "data-x": "1", title: "hi" })
30
- expect(forwarded).toContain("data-x")
31
- expect(forwarded).toContain("title")
33
+ Comp({ 'data-x': '1', title: 'hi' })
34
+ expect(forwarded).toContain('data-x')
35
+ expect(forwarded).toContain('title')
32
36
  })
33
37
 
34
- it("works with dynamic interpolations", () => {
35
- const Comp = styled("div", {
36
- shouldForwardProp: (prop) => prop === "title",
37
- })`color: ${(p: any) => p.$color};`
38
+ it('works with dynamic interpolations', () => {
39
+ const Comp = styled('div', {
40
+ shouldForwardProp: (prop) => prop === 'title',
41
+ })`
42
+ color: ${(p: any) => p.$color};
43
+ `
38
44
 
39
- const vnode = Comp({ $color: "red", title: "yes", custom: "no" }) as VNode
40
- expect(vnode.props.title).toBe("yes")
45
+ const vnode = Comp({ $color: 'red', title: 'yes', custom: 'no' }) as VNode
46
+ expect(vnode.props.title).toBe('yes')
41
47
  expect(vnode.props.custom).toBeUndefined()
42
48
  })
43
49
 
44
- it("does not affect component wrapping (components receive all props)", () => {
50
+ it('does not affect component wrapping (components receive all props)', () => {
45
51
  const Inner = (props: { class?: string; myProp?: string }) =>
46
- h("div", { class: props.class, "data-my": props.myProp })
52
+ h('div', { class: props.class, 'data-my': props.myProp })
47
53
 
48
54
  // shouldForwardProp is only for HTML elements
49
55
  const Comp = styled(Inner, {
50
56
  shouldForwardProp: () => false,
51
- })`color: red;`
57
+ })`
58
+ color: red;
59
+ `
52
60
 
53
- const vnode = Comp({ myProp: "hello" }) as VNode
61
+ const vnode = Comp({ myProp: 'hello' }) as VNode
54
62
  // Components always receive all props (no filtering)
55
63
  // The VNode wraps Inner and should pass myProp through
56
- expect(vnode.props.myProp).toBe("hello")
64
+ expect(vnode.props.myProp).toBe('hello')
57
65
  })
58
66
  })
59
67
 
60
- describe("styled(StyledComponent) — extending", () => {
61
- it("extends a styled component", () => {
62
- const Base = styled("div")`color: red;`
63
- const Extended = styled(Base)`font-size: 20px;`
68
+ describe('styled(StyledComponent) — extending', () => {
69
+ it('extends a styled component', () => {
70
+ const Base = styled('div')`
71
+ color: red;
72
+ `
73
+ const Extended = styled(Base)`
74
+ font-size: 20px;
75
+ `
64
76
 
65
77
  const vnode = Extended({}) as VNode
66
78
  // Extended wraps Base, so Base applies its own className
67
79
  // and Extended passes its className to Base as a prop
68
- expect(vnode.props.class).toContain("pyr-")
80
+ expect(vnode.props.class).toContain('pyr-')
69
81
  })
70
82
 
71
- it("extended component receives className from outer", () => {
72
- const Base = styled("div")`color: red;`
73
- const Extended = styled(Base)`font-size: 20px;`
83
+ it('extended component receives className from outer', () => {
84
+ const Base = styled('div')`
85
+ color: red;
86
+ `
87
+ const Extended = styled(Base)`
88
+ font-size: 20px;
89
+ `
74
90
 
75
- const vnode = Extended({ className: "user-cls" }) as VNode
76
- expect(vnode.props.class).toContain("user-cls")
91
+ const vnode = Extended({ className: 'user-cls' }) as VNode
92
+ expect(vnode.props.class).toContain('user-cls')
77
93
  })
78
94
 
79
- it("multi-level extension works", () => {
80
- const L1 = styled("div")`display: flex;`
81
- const L2 = styled(L1)`color: red;`
82
- const L3 = styled(L2)`font-size: 14px;`
95
+ it('multi-level extension works', () => {
96
+ const L1 = styled('div')`
97
+ display: flex;
98
+ `
99
+ const L2 = styled(L1)`
100
+ color: red;
101
+ `
102
+ const L3 = styled(L2)`
103
+ font-size: 14px;
104
+ `
83
105
 
84
106
  const vnode = L3({}) as VNode
85
- expect(vnode.props.class).toContain("pyr-")
107
+ expect(vnode.props.class).toContain('pyr-')
86
108
  // L3 wraps L2 which wraps L1 which wraps 'div'.
87
109
  // The outermost VNode's type is the next component in the chain.
88
- expect(typeof vnode.type).toBe("function")
110
+ expect(typeof vnode.type).toBe('function')
89
111
  })
90
112
  })
91
113
 
92
- describe("HMR cleanup API", () => {
114
+ describe('HMR cleanup API', () => {
93
115
  beforeEach(() => {
94
- document.querySelectorAll("style[data-pyreon-styler]").forEach((el) => {
116
+ document.querySelectorAll('style[data-pyreon-styler]').forEach((el) => {
95
117
  el.remove()
96
118
  })
97
119
  })
98
120
 
99
- it("clearCache removes all cached entries", () => {
121
+ it('clearCache removes all cached entries', () => {
100
122
  const s = createSheet()
101
- s.insert("color: red;")
102
- s.insert("color: blue;")
123
+ s.insert('color: red;')
124
+ s.insert('color: blue;')
103
125
  expect(s.cacheSize).toBe(2)
104
126
 
105
127
  s.clearCache()
106
128
  expect(s.cacheSize).toBe(0)
107
129
  })
108
130
 
109
- it("clearAll removes cache, SSR buffer, and DOM rules", () => {
131
+ it('clearAll removes cache, SSR buffer, and DOM rules', () => {
110
132
  const s = new StyleSheet()
111
- s.insert("color: red;")
112
- s.insert("color: blue;")
133
+ s.insert('color: red;')
134
+ s.insert('color: blue;')
113
135
  expect(s.cacheSize).toBe(2)
114
136
 
115
137
  s.clearAll()
116
138
  expect(s.cacheSize).toBe(0)
117
139
  })
118
140
 
119
- it("after clearCache, same CSS gets re-inserted", () => {
141
+ it('after clearCache, same CSS gets re-inserted', () => {
120
142
  const s = createSheet()
121
- s.insert("color: red;")
143
+ s.insert('color: red;')
122
144
  expect(s.cacheSize).toBe(1)
123
145
 
124
146
  s.clearCache()
125
147
  expect(s.cacheSize).toBe(0)
126
148
 
127
149
  // Re-insert — should work since cache was cleared
128
- s.insert("color: red;")
150
+ s.insert('color: red;')
129
151
  expect(s.cacheSize).toBe(1)
130
152
  })
131
153
  })
132
154
 
133
- describe("HMR cleanup API (SSR mode)", () => {
155
+ describe('HMR cleanup API (SSR mode)', () => {
134
156
  let originalDocument: typeof document
135
157
 
136
158
  beforeEach(() => {
@@ -143,98 +165,132 @@ describe("P3 features", () => {
143
165
  globalThis.document = originalDocument
144
166
  })
145
167
 
146
- it("clearAll in SSR mode clears buffer and cache", () => {
168
+ it('clearAll in SSR mode clears buffer and cache', () => {
147
169
  const s = createSheet()
148
- s.insert("color: red;")
149
- expect(s.getStyles()).toContain("color: red;")
170
+ s.insert('color: red;')
171
+ expect(s.getStyles()).toContain('color: red;')
150
172
  expect(s.cacheSize).toBe(1)
151
173
 
152
174
  s.clearAll()
153
- expect(s.getStyles()).toBe("")
175
+ expect(s.getStyles()).toBe('')
154
176
  expect(s.cacheSize).toBe(0)
155
177
  })
156
178
  })
157
179
 
158
- describe("CSS nesting (& selectors)", () => {
159
- it("& selectors pass through to the CSS rule", () => {
180
+ describe('CSS nesting (& selectors)', () => {
181
+ it('& selectors pass through to the CSS rule', () => {
160
182
  // Native CSS nesting is supported by modern browsers
161
183
  // The resolver passes CSS through without transformation
162
- const Comp = styled("div")`
184
+ const Comp = styled('div')`
163
185
  color: red;
164
- &:hover { color: blue; }
186
+ &:hover {
187
+ color: blue;
188
+ }
165
189
  `
166
190
  const vnode = Comp({}) as VNode
167
191
  expect(vnode.props.class).toMatch(/^pyr-/)
168
192
  })
169
193
 
170
- it("nested & with pseudo-elements", () => {
171
- const Comp = styled("div")`
194
+ it('nested & with pseudo-elements', () => {
195
+ const Comp = styled('div')`
172
196
  position: relative;
173
- &::before { content: ""; display: block; }
174
- &::after { content: ""; display: block; }
197
+ &::before {
198
+ content: '';
199
+ display: block;
200
+ }
201
+ &::after {
202
+ content: '';
203
+ display: block;
204
+ }
175
205
  `
176
206
  const vnode = Comp({}) as VNode
177
207
  expect(vnode.props.class).toMatch(/^pyr-/)
178
208
  })
179
209
  })
180
210
 
181
- describe("edge cases", () => {
182
- it("empty template with dynamic interpolation returning nothing", () => {
183
- const Comp = styled("div")`${(p: any) => p.$show && css`color: red;`}`
211
+ describe('edge cases', () => {
212
+ it('empty template with dynamic interpolation returning nothing', () => {
213
+ const Comp = styled('div')`
214
+ ${(p: any) =>
215
+ p.$show &&
216
+ css`
217
+ color: red;
218
+ `}
219
+ `
184
220
  const vnode = Comp({ $show: false }) as VNode
185
221
  // When resolved CSS is empty/whitespace, no className
186
222
  expect(vnode.props.class).toBeFalsy()
187
223
  })
188
224
 
189
- it("empty template with dynamic interpolation returning value", () => {
190
- const Comp = styled("div")`${(p: any) => p.$show && css`color: red;`}`
225
+ it('empty template with dynamic interpolation returning value', () => {
226
+ const Comp = styled('div')`
227
+ ${(p: any) =>
228
+ p.$show &&
229
+ css`
230
+ color: red;
231
+ `}
232
+ `
191
233
  const vnode = Comp({ $show: true }) as VNode
192
234
  expect(vnode.props.class).toMatch(/^pyr-/)
193
235
  })
194
236
 
195
- it("deeply nested CSSResult chains resolve correctly", () => {
196
- const l1 = css`color: red;`
197
- const l2 = css`${l1} font-size: 14px;`
198
- const l3 = css`${l2} display: flex;`
199
- const l4 = css`${l3} padding: 8px;`
200
- const l5 = css`${l4} margin: 4px;`
237
+ it('deeply nested CSSResult chains resolve correctly', () => {
238
+ const l1 = css`
239
+ color: red;
240
+ `
241
+ const l2 = css`
242
+ ${l1} font-size: 14px;
243
+ `
244
+ const l3 = css`
245
+ ${l2} display: flex;
246
+ `
247
+ const l4 = css`
248
+ ${l3} padding: 8px;
249
+ `
250
+ const l5 = css`
251
+ ${l4} margin: 4px;
252
+ `
201
253
 
202
254
  const resolved = l5.toString()
203
- expect(resolved).toContain("color: red;")
204
- expect(resolved).toContain("font-size: 14px;")
205
- expect(resolved).toContain("display: flex;")
206
- expect(resolved).toContain("padding: 8px;")
207
- expect(resolved).toContain("margin: 4px;")
255
+ expect(resolved).toContain('color: red;')
256
+ expect(resolved).toContain('font-size: 14px;')
257
+ expect(resolved).toContain('display: flex;')
258
+ expect(resolved).toContain('padding: 8px;')
259
+ expect(resolved).toContain('margin: 4px;')
208
260
  })
209
261
 
210
- it("anonymous component gets fallback displayName", () => {
262
+ it('anonymous component gets fallback displayName', () => {
211
263
  const Anon = (() => {
212
264
  const fn = () => null
213
- Object.defineProperty(fn, "name", { value: "" })
265
+ Object.defineProperty(fn, 'name', { value: '' })
214
266
  return fn
215
267
  })()
216
268
 
217
- const Comp = styled(Anon)`color: red;`
218
- expect((Comp as any).displayName).toBe("styled(Component)")
269
+ const Comp = styled(Anon)`
270
+ color: red;
271
+ `
272
+ expect((Comp as any).displayName).toBe('styled(Component)')
219
273
  })
220
274
 
221
- it("handles very large CSS strings", () => {
222
- const bigCSS = Array.from({ length: 100 }, (_, i) => `prop${i}: val${i};`).join(" ")
223
- const Comp = styled("div")`${bigCSS}`
275
+ it('handles very large CSS strings', () => {
276
+ const bigCSS = Array.from({ length: 100 }, (_, i) => `prop${i}: val${i};`).join(' ')
277
+ const Comp = styled('div')`
278
+ ${bigCSS}
279
+ `
224
280
  const vnode = Comp({}) as VNode
225
281
  expect(vnode.props.class).toMatch(/^pyr-/)
226
282
  })
227
283
 
228
- it("different dynamic values cause different classNames", () => {
229
- const Comp = styled("div")`
284
+ it('different dynamic values cause different classNames', () => {
285
+ const Comp = styled('div')`
230
286
  color: ${(p: any) => p.$color};
231
287
  font-size: ${(p: any) => p.$size};
232
288
  `
233
289
 
234
- const vnode1 = Comp({ $color: "red", $size: "14px" }) as VNode
290
+ const vnode1 = Comp({ $color: 'red', $size: '14px' }) as VNode
235
291
  const cls1 = vnode1.props.class as string
236
292
 
237
- const vnode2 = Comp({ $color: "blue", $size: "16px" }) as VNode
293
+ const vnode2 = Comp({ $color: 'blue', $size: '16px' }) as VNode
238
294
  const cls2 = vnode2.props.class as string
239
295
 
240
296
  expect(cls1).not.toBe(cls2)
@@ -242,14 +298,16 @@ describe("P3 features", () => {
242
298
  expect(cls2).toMatch(/^pyr-/)
243
299
  })
244
300
 
245
- it("same dynamic values produce same className (dedup cache)", () => {
246
- const Comp = styled("div")`color: ${(p: any) => p.$color};`
301
+ it('same dynamic values produce same className (dedup cache)', () => {
302
+ const Comp = styled('div')`
303
+ color: ${(p: any) => p.$color};
304
+ `
247
305
 
248
- const vnode1 = Comp({ $color: "red" }) as VNode
306
+ const vnode1 = Comp({ $color: 'red' }) as VNode
249
307
  const cls1 = vnode1.props.class as string
250
308
 
251
309
  // Re-render with same value
252
- const vnode2 = Comp({ $color: "red" }) as VNode
310
+ const vnode2 = Comp({ $color: 'red' }) as VNode
253
311
  const cls2 = vnode2.props.class as string
254
312
 
255
313
  expect(cls1).toBe(cls2)