@pyreon/styler 0.11.5 → 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,282 +1,282 @@
1
- import { describe, expect, it } from "vitest"
2
- import { buildProps, filterProps } from "../forward"
1
+ import { describe, expect, it } from 'vitest'
2
+ import { buildProps, filterProps } from '../forward'
3
3
 
4
- describe("filterProps", () => {
5
- describe("keeps standard HTML props", () => {
6
- it("keeps id", () => {
7
- const result = filterProps({ id: "test" })
8
- expect(result.id).toBe("test")
4
+ describe('filterProps', () => {
5
+ describe('keeps standard HTML props', () => {
6
+ it('keeps id', () => {
7
+ const result = filterProps({ id: 'test' })
8
+ expect(result.id).toBe('test')
9
9
  })
10
10
 
11
- it("keeps className", () => {
12
- const result = filterProps({ className: "foo" })
13
- expect(result.className).toBe("foo")
11
+ it('keeps className', () => {
12
+ const result = filterProps({ className: 'foo' })
13
+ expect(result.className).toBe('foo')
14
14
  })
15
15
 
16
- it("keeps style", () => {
17
- const style = { color: "red" }
16
+ it('keeps style', () => {
17
+ const style = { color: 'red' }
18
18
  const result = filterProps({ style })
19
19
  expect(result.style).toBe(style)
20
20
  })
21
21
 
22
- it("keeps href", () => {
23
- const result = filterProps({ href: "/path" })
24
- expect(result.href).toBe("/path")
22
+ it('keeps href', () => {
23
+ const result = filterProps({ href: '/path' })
24
+ expect(result.href).toBe('/path')
25
25
  })
26
26
 
27
- it("keeps disabled", () => {
27
+ it('keeps disabled', () => {
28
28
  const result = filterProps({ disabled: true })
29
29
  expect(result.disabled).toBe(true)
30
30
  })
31
31
 
32
- it("keeps multiple standard props at once", () => {
32
+ it('keeps multiple standard props at once', () => {
33
33
  const result = filterProps({
34
- id: "main",
34
+ id: 'main',
35
35
  tabIndex: 0,
36
- role: "button",
37
- title: "Click me",
36
+ role: 'button',
37
+ title: 'Click me',
38
38
  })
39
39
  expect(result).toEqual({
40
- id: "main",
40
+ id: 'main',
41
41
  tabIndex: 0,
42
- role: "button",
43
- title: "Click me",
42
+ role: 'button',
43
+ title: 'Click me',
44
44
  })
45
45
  })
46
46
  })
47
47
 
48
- describe("keeps data-* attributes", () => {
49
- it("keeps data-testid", () => {
50
- const result = filterProps({ "data-testid": "hello" })
51
- expect(result["data-testid"]).toBe("hello")
48
+ describe('keeps data-* attributes', () => {
49
+ it('keeps data-testid', () => {
50
+ const result = filterProps({ 'data-testid': 'hello' })
51
+ expect(result['data-testid']).toBe('hello')
52
52
  })
53
53
 
54
- it("keeps data-custom", () => {
55
- const result = filterProps({ "data-custom": "value" })
56
- expect(result["data-custom"]).toBe("value")
54
+ it('keeps data-custom', () => {
55
+ const result = filterProps({ 'data-custom': 'value' })
56
+ expect(result['data-custom']).toBe('value')
57
57
  })
58
58
  })
59
59
 
60
- describe("keeps aria-* attributes", () => {
61
- it("keeps aria-label", () => {
62
- const result = filterProps({ "aria-label": "Close" })
63
- expect(result["aria-label"]).toBe("Close")
60
+ describe('keeps aria-* attributes', () => {
61
+ it('keeps aria-label', () => {
62
+ const result = filterProps({ 'aria-label': 'Close' })
63
+ expect(result['aria-label']).toBe('Close')
64
64
  })
65
65
 
66
- it("keeps aria-hidden", () => {
67
- const result = filterProps({ "aria-hidden": true })
68
- expect(result["aria-hidden"]).toBe(true)
66
+ it('keeps aria-hidden', () => {
67
+ const result = filterProps({ 'aria-hidden': true })
68
+ expect(result['aria-hidden']).toBe(true)
69
69
  })
70
70
 
71
- it("keeps aria-describedby", () => {
72
- const result = filterProps({ "aria-describedby": "desc" })
73
- expect(result["aria-describedby"]).toBe("desc")
71
+ it('keeps aria-describedby', () => {
72
+ const result = filterProps({ 'aria-describedby': 'desc' })
73
+ expect(result['aria-describedby']).toBe('desc')
74
74
  })
75
75
  })
76
76
 
77
- describe("keeps event handlers", () => {
78
- it("keeps onClick", () => {
77
+ describe('keeps event handlers', () => {
78
+ it('keeps onClick', () => {
79
79
  const fn = () => undefined
80
80
  const result = filterProps({ onClick: fn })
81
81
  expect(result.onClick).toBe(fn)
82
82
  })
83
83
 
84
- it("keeps onMouseEnter", () => {
84
+ it('keeps onMouseEnter', () => {
85
85
  const fn = () => undefined
86
86
  const result = filterProps({ onMouseEnter: fn })
87
87
  expect(result.onMouseEnter).toBe(fn)
88
88
  })
89
89
 
90
- it("keeps onKeyDown", () => {
90
+ it('keeps onKeyDown', () => {
91
91
  const fn = () => undefined
92
92
  const result = filterProps({ onKeyDown: fn })
93
93
  expect(result.onKeyDown).toBe(fn)
94
94
  })
95
95
  })
96
96
 
97
- describe("strips $-prefixed transient props", () => {
98
- it("strips $rocketstyle", () => {
99
- const result = filterProps({ $rocketstyle: { color: "red" } })
97
+ describe('strips $-prefixed transient props', () => {
98
+ it('strips $rocketstyle', () => {
99
+ const result = filterProps({ $rocketstyle: { color: 'red' } })
100
100
  expect(result).toEqual({})
101
101
  })
102
102
 
103
- it("strips $element", () => {
104
- const result = filterProps({ $element: "button" })
103
+ it('strips $element', () => {
104
+ const result = filterProps({ $element: 'button' })
105
105
  expect(result).toEqual({})
106
106
  })
107
107
 
108
- it("strips $active", () => {
108
+ it('strips $active', () => {
109
109
  const result = filterProps({ $active: true })
110
110
  expect(result).toEqual({})
111
111
  })
112
112
 
113
- it("strips multiple $-prefixed props while keeping valid ones", () => {
113
+ it('strips multiple $-prefixed props while keeping valid ones', () => {
114
114
  const result = filterProps({
115
115
  $rocketstyle: {},
116
- $element: "div",
117
- id: "test",
118
- "data-x": "y",
116
+ $element: 'div',
117
+ id: 'test',
118
+ 'data-x': 'y',
119
119
  })
120
- expect(result).toEqual({ id: "test", "data-x": "y" })
120
+ expect(result).toEqual({ id: 'test', 'data-x': 'y' })
121
121
  })
122
122
  })
123
123
 
124
- describe("strips as prop", () => {
125
- it("strips the as prop", () => {
126
- const result = filterProps({ as: "button" })
124
+ describe('strips as prop', () => {
125
+ it('strips the as prop', () => {
126
+ const result = filterProps({ as: 'button' })
127
127
  expect(result).toEqual({})
128
128
  })
129
129
 
130
- it("strips as while keeping other props", () => {
131
- const result = filterProps({ as: "section", id: "main" })
132
- expect(result).toEqual({ id: "main" })
130
+ it('strips as while keeping other props', () => {
131
+ const result = filterProps({ as: 'section', id: 'main' })
132
+ expect(result).toEqual({ id: 'main' })
133
133
  })
134
134
  })
135
135
 
136
- describe("strips unknown props", () => {
137
- it("strips customProp", () => {
138
- const result = filterProps({ customProp: "value" })
136
+ describe('strips unknown props', () => {
137
+ it('strips customProp', () => {
138
+ const result = filterProps({ customProp: 'value' })
139
139
  expect(result).toEqual({})
140
140
  })
141
141
 
142
- it("strips myThing", () => {
142
+ it('strips myThing', () => {
143
143
  const result = filterProps({ myThing: 42 })
144
144
  expect(result).toEqual({})
145
145
  })
146
146
 
147
- it("strips camelCase unknown props", () => {
148
- const result = filterProps({ isActive: true, backgroundColor: "red" })
147
+ it('strips camelCase unknown props', () => {
148
+ const result = filterProps({ isActive: true, backgroundColor: 'red' })
149
149
  expect(result).toEqual({})
150
150
  })
151
151
 
152
- it("returns empty object for all-unknown props", () => {
152
+ it('returns empty object for all-unknown props', () => {
153
153
  const result = filterProps({
154
154
  foo: 1,
155
155
  bar: 2,
156
156
  baz: 3,
157
- customThing: "x",
157
+ customThing: 'x',
158
158
  })
159
159
  expect(result).toEqual({})
160
160
  })
161
161
  })
162
162
  })
163
163
 
164
- describe("buildProps", () => {
165
- describe("className merging", () => {
166
- it("merges generatedCls with user className", () => {
167
- const result = buildProps({ className: "custom" }, "pyr-abc", true)
168
- expect(result.class).toBe("pyr-abc custom")
164
+ describe('buildProps', () => {
165
+ describe('className merging', () => {
166
+ it('merges generatedCls with user className', () => {
167
+ const result = buildProps({ className: 'custom' }, 'pyr-abc', true)
168
+ expect(result.class).toBe('pyr-abc custom')
169
169
  })
170
170
 
171
- it("merges generatedCls with user class", () => {
172
- const result = buildProps({ class: "custom" }, "pyr-abc", true)
173
- expect(result.class).toBe("pyr-abc custom")
171
+ it('merges generatedCls with user class', () => {
172
+ const result = buildProps({ class: 'custom' }, 'pyr-abc', true)
173
+ expect(result.class).toBe('pyr-abc custom')
174
174
  })
175
175
 
176
- it("uses only generatedCls when no user className", () => {
177
- const result = buildProps({}, "pyr-abc", true)
178
- expect(result.class).toBe("pyr-abc")
176
+ it('uses only generatedCls when no user className', () => {
177
+ const result = buildProps({}, 'pyr-abc', true)
178
+ expect(result.class).toBe('pyr-abc')
179
179
  })
180
180
 
181
- it("uses only user className when generatedCls is empty", () => {
182
- const result = buildProps({ className: "custom" }, "", true)
183
- expect(result.class).toBe("custom")
181
+ it('uses only user className when generatedCls is empty', () => {
182
+ const result = buildProps({ className: 'custom' }, '', true)
183
+ expect(result.class).toBe('custom')
184
184
  })
185
185
 
186
- it("sets no class when both are empty/missing", () => {
187
- const result = buildProps({}, "", true)
186
+ it('sets no class when both are empty/missing', () => {
187
+ const result = buildProps({}, '', true)
188
188
  expect(result.class).toBeUndefined()
189
189
  })
190
190
  })
191
191
 
192
- describe("isDOM=false (component target)", () => {
193
- it("forwards all props except as, className, class, and $-prefixed", () => {
192
+ describe('isDOM=false (component target)', () => {
193
+ it('forwards all props except as, className, class, and $-prefixed', () => {
194
194
  const result = buildProps(
195
195
  {
196
- as: "button",
197
- className: "user",
198
- customProp: "hello",
196
+ as: 'button',
197
+ className: 'user',
198
+ customProp: 'hello',
199
199
  $rocketstyle: {},
200
200
  $rocketstate: { hover: true },
201
- "data-x": "y",
201
+ 'data-x': 'y',
202
202
  onClick: () => undefined,
203
203
  },
204
- "pyr-abc",
204
+ 'pyr-abc',
205
205
  false,
206
206
  )
207
207
 
208
- expect(result.customProp).toBe("hello")
208
+ expect(result.customProp).toBe('hello')
209
209
  expect(result.$rocketstyle).toBeUndefined()
210
210
  expect(result.$rocketstate).toBeUndefined()
211
- expect(result["data-x"]).toBe("y")
211
+ expect(result['data-x']).toBe('y')
212
212
  expect(result.onClick).toBeDefined()
213
213
  // as and className are not forwarded from rawProps (class is merged separately)
214
214
  expect(result.as).toBeUndefined()
215
- expect(result.class).toBe("pyr-abc user")
215
+ expect(result.class).toBe('pyr-abc user')
216
216
  })
217
217
  })
218
218
 
219
- describe("isDOM=true (default filtering)", () => {
220
- it("filters unknown DOM props", () => {
221
- const result = buildProps({ customProp: "hello", unknownThing: 42 }, "pyr-abc", true)
219
+ describe('isDOM=true (default filtering)', () => {
220
+ it('filters unknown DOM props', () => {
221
+ const result = buildProps({ customProp: 'hello', unknownThing: 42 }, 'pyr-abc', true)
222
222
  expect(result.customProp).toBeUndefined()
223
223
  expect(result.unknownThing).toBeUndefined()
224
- expect(result.class).toBe("pyr-abc")
224
+ expect(result.class).toBe('pyr-abc')
225
225
  })
226
226
 
227
- it("strips $-prefixed props", () => {
228
- const result = buildProps({ $rocketstyle: {}, $active: true, id: "test" }, "pyr-abc", true)
227
+ it('strips $-prefixed props', () => {
228
+ const result = buildProps({ $rocketstyle: {}, $active: true, id: 'test' }, 'pyr-abc', true)
229
229
  expect(result.$rocketstyle).toBeUndefined()
230
230
  expect(result.$active).toBeUndefined()
231
- expect(result.id).toBe("test")
231
+ expect(result.id).toBe('test')
232
232
  })
233
233
 
234
- it("keeps data-* and aria-* attributes", () => {
235
- const result = buildProps({ "data-testid": "btn", "aria-label": "Close" }, "pyr-abc", true)
236
- expect(result["data-testid"]).toBe("btn")
237
- expect(result["aria-label"]).toBe("Close")
234
+ it('keeps data-* and aria-* attributes', () => {
235
+ const result = buildProps({ 'data-testid': 'btn', 'aria-label': 'Close' }, 'pyr-abc', true)
236
+ expect(result['data-testid']).toBe('btn')
237
+ expect(result['aria-label']).toBe('Close')
238
238
  })
239
239
 
240
- it("keeps standard HTML attributes", () => {
241
- const result = buildProps({ id: "main", disabled: true, tabIndex: 0 }, "pyr-abc", true)
242
- expect(result.id).toBe("main")
240
+ it('keeps standard HTML attributes', () => {
241
+ const result = buildProps({ id: 'main', disabled: true, tabIndex: 0 }, 'pyr-abc', true)
242
+ expect(result.id).toBe('main')
243
243
  expect(result.disabled).toBe(true)
244
244
  expect(result.tabIndex).toBe(0)
245
245
  })
246
246
 
247
- it("strips as prop", () => {
248
- const result = buildProps({ as: "section" }, "pyr-abc", true)
247
+ it('strips as prop', () => {
248
+ const result = buildProps({ as: 'section' }, 'pyr-abc', true)
249
249
  expect(result.as).toBeUndefined()
250
250
  })
251
251
  })
252
252
 
253
- describe("isDOM=true with customFilter", () => {
254
- it("uses customFilter to decide which props to forward", () => {
255
- const customFilter = (prop: string) => prop.startsWith("my")
253
+ describe('isDOM=true with customFilter', () => {
254
+ it('uses customFilter to decide which props to forward', () => {
255
+ const customFilter = (prop: string) => prop.startsWith('my')
256
256
  const result = buildProps(
257
- { myProp: "yes", otherProp: "no", id: "skip" },
258
- "pyr-abc",
257
+ { myProp: 'yes', otherProp: 'no', id: 'skip' },
258
+ 'pyr-abc',
259
259
  true,
260
260
  customFilter,
261
261
  )
262
- expect(result.myProp).toBe("yes")
262
+ expect(result.myProp).toBe('yes')
263
263
  expect(result.otherProp).toBeUndefined()
264
264
  expect(result.id).toBeUndefined()
265
265
  })
266
266
 
267
- it("customFilter still skips as and className from rawProps", () => {
267
+ it('customFilter still skips as and className from rawProps', () => {
268
268
  const customFilter = () => true
269
269
  const result = buildProps(
270
- { as: "button", className: "user", id: "test" },
271
- "pyr-abc",
270
+ { as: 'button', className: 'user', id: 'test' },
271
+ 'pyr-abc',
272
272
  true,
273
273
  customFilter,
274
274
  )
275
275
  // as is always skipped
276
276
  expect(result.as).toBeUndefined()
277
277
  // class is merged, not forwarded from rawProps
278
- expect(result.class).toBe("pyr-abc user")
279
- expect(result.id).toBe("test")
278
+ expect(result.class).toBe('pyr-abc user')
279
+ expect(result.id).toBe('test')
280
280
  })
281
281
  })
282
282
  })
@@ -1,50 +1,50 @@
1
- import { afterEach, describe, expect, it } from "vitest"
2
- import { createGlobalStyle } from "../globalStyle"
3
- import { sheet } from "../sheet"
1
+ import { afterEach, describe, expect, it } from 'vitest'
2
+ import { createGlobalStyle } from '../globalStyle'
3
+ import { sheet } from '../sheet'
4
4
 
5
- describe("createGlobalStyle -- empty CSS paths", () => {
5
+ describe('createGlobalStyle -- empty CSS paths', () => {
6
6
  afterEach(() => {
7
7
  sheet.clearAll()
8
8
  })
9
9
 
10
- it("static: returns null for empty template", () => {
10
+ it('static: returns null for empty template', () => {
11
11
  const GlobalStyle = createGlobalStyle``
12
12
  const result = GlobalStyle({})
13
13
  expect(result).toBeNull()
14
14
  })
15
15
 
16
- it("static: returns null for whitespace-only template", () => {
16
+ it('static: returns null for whitespace-only template', () => {
17
17
  const GlobalStyle = createGlobalStyle` `
18
18
  const result = GlobalStyle({})
19
19
  expect(result).toBeNull()
20
20
  })
21
21
 
22
- it("dynamic: returns null when interpolation resolves to empty CSS", () => {
23
- const GlobalStyle = createGlobalStyle`${({ theme }: any) => (theme.empty ? "" : "")}`
22
+ it('dynamic: returns null when interpolation resolves to empty CSS', () => {
23
+ const GlobalStyle = createGlobalStyle`${({ theme }: any) => (theme.empty ? '' : '')}`
24
24
  const result = GlobalStyle({})
25
25
  expect(result).toBeNull()
26
26
  })
27
27
 
28
- it("dynamic: returns null when interpolation resolves to whitespace", () => {
29
- const GlobalStyle = createGlobalStyle`${() => " "}`
28
+ it('dynamic: returns null when interpolation resolves to whitespace', () => {
29
+ const GlobalStyle = createGlobalStyle`${() => ' '}`
30
30
  const result = GlobalStyle({})
31
31
  expect(result).toBeNull()
32
32
  })
33
33
  })
34
34
 
35
- describe("createGlobalStyle", () => {
35
+ describe('createGlobalStyle', () => {
36
36
  afterEach(() => {
37
37
  sheet.clearAll()
38
38
  })
39
39
 
40
- it("returns a component function", () => {
40
+ it('returns a component function', () => {
41
41
  const GlobalStyle = createGlobalStyle`
42
42
  body { margin: 0; }
43
43
  `
44
- expect(typeof GlobalStyle).toBe("function")
44
+ expect(typeof GlobalStyle).toBe('function')
45
45
  })
46
46
 
47
- it("renders nothing (returns null)", () => {
47
+ it('renders nothing (returns null)', () => {
48
48
  const GlobalStyle = createGlobalStyle`
49
49
  body { margin: 0; padding: 0; }
50
50
  `
@@ -52,17 +52,17 @@ describe("createGlobalStyle", () => {
52
52
  expect(result).toBeNull()
53
53
  })
54
54
 
55
- it("handles dynamic interpolations with theme", () => {
55
+ it('handles dynamic interpolations with theme', () => {
56
56
  // Dynamic path: function interpolation causes per-render resolution
57
57
  const GlobalStyle = createGlobalStyle`
58
- body { font-family: ${({ theme }: any) => theme?.font ?? "sans-serif"}; }
58
+ body { font-family: ${({ theme }: any) => theme?.font ?? 'sans-serif'}; }
59
59
  `
60
60
  const result = GlobalStyle({})
61
61
  expect(result).toBeNull()
62
62
  })
63
63
 
64
- it("handles static interpolations", () => {
65
- const color = "red"
64
+ it('handles static interpolations', () => {
65
+ const color = 'red'
66
66
  const GlobalStyle = createGlobalStyle`
67
67
  body { color: ${color}; }
68
68
  `
@@ -1,59 +1,59 @@
1
- import { describe, expect, it } from "vitest"
2
- import { hash } from "../hash"
1
+ import { describe, expect, it } from 'vitest'
2
+ import { hash } from '../hash'
3
3
 
4
- describe("hash", () => {
5
- it("returns a string", () => {
6
- expect(typeof hash("test")).toBe("string")
4
+ describe('hash', () => {
5
+ it('returns a string', () => {
6
+ expect(typeof hash('test')).toBe('string')
7
7
  })
8
8
 
9
- it("is deterministic — same input always produces same output", () => {
10
- const input = "display: flex; color: red;"
9
+ it('is deterministic — same input always produces same output', () => {
10
+ const input = 'display: flex; color: red;'
11
11
  expect(hash(input)).toBe(hash(input))
12
12
  })
13
13
 
14
- it("produces different hashes for different inputs", () => {
15
- expect(hash("color: red")).not.toBe(hash("color: blue"))
14
+ it('produces different hashes for different inputs', () => {
15
+ expect(hash('color: red')).not.toBe(hash('color: blue'))
16
16
  })
17
17
 
18
- it("returns base-36 string (compact)", () => {
19
- const result = hash("some css")
18
+ it('returns base-36 string (compact)', () => {
19
+ const result = hash('some css')
20
20
  expect(result).toMatch(/^[0-9a-z]+$/)
21
21
  })
22
22
 
23
- it("handles empty string", () => {
24
- const result = hash("")
25
- expect(typeof result).toBe("string")
23
+ it('handles empty string', () => {
24
+ const result = hash('')
25
+ expect(typeof result).toBe('string')
26
26
  expect(result.length).toBeGreaterThan(0)
27
27
  })
28
28
 
29
- it("handles long CSS strings", () => {
30
- const longCSS = "display: flex; ".repeat(100)
29
+ it('handles long CSS strings', () => {
30
+ const longCSS = 'display: flex; '.repeat(100)
31
31
  const result = hash(longCSS)
32
- expect(typeof result).toBe("string")
32
+ expect(typeof result).toBe('string')
33
33
  // base-36 uint32 is at most 7 chars
34
34
  expect(result.length).toBeLessThan(10)
35
35
  })
36
36
 
37
- it("handles special characters in CSS", () => {
37
+ it('handles special characters in CSS', () => {
38
38
  const css = `@media (min-width: 48em) { .foo { content: "hello"; } }`
39
39
  const result = hash(css)
40
- expect(typeof result).toBe("string")
40
+ expect(typeof result).toBe('string')
41
41
  })
42
42
 
43
- it("produces consistent hash for FNV-1a offset basis on empty string", () => {
43
+ it('produces consistent hash for FNV-1a offset basis on empty string', () => {
44
44
  // Empty string: h stays at FNV_OFFSET = 2166136261, base36 = "zzzzzz" range
45
- const result = hash("")
45
+ const result = hash('')
46
46
  // Just verify it is stable
47
- expect(result).toBe(hash(""))
47
+ expect(result).toBe(hash(''))
48
48
  })
49
49
 
50
- it("handles unicode characters", () => {
50
+ it('handles unicode characters', () => {
51
51
  const result = hash('content: "🎉";')
52
- expect(typeof result).toBe("string")
52
+ expect(typeof result).toBe('string')
53
53
  expect(result.length).toBeGreaterThan(0)
54
54
  })
55
55
 
56
- it("single character inputs produce distinct hashes", () => {
56
+ it('single character inputs produce distinct hashes', () => {
57
57
  const hashes = new Set<string>()
58
58
  for (let i = 0; i < 26; i++) {
59
59
  hashes.add(hash(String.fromCharCode(97 + i)))
@@ -62,9 +62,9 @@ describe("hash", () => {
62
62
  expect(hashes.size).toBe(26)
63
63
  })
64
64
 
65
- it("hash is unsigned 32-bit (no negative values)", () => {
65
+ it('hash is unsigned 32-bit (no negative values)', () => {
66
66
  // base-36 of a uint32 is always positive
67
- const result = hash("test negative")
67
+ const result = hash('test negative')
68
68
  expect(Number.parseInt(result, 36)).toBeGreaterThanOrEqual(0)
69
69
  })
70
70
  })