@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,24 +1,30 @@
1
- import { afterEach, describe, expect, it } from "vitest"
2
- import { css } from "../css"
3
- import { sheet } from "../sheet"
4
- import { useCSS } from "../useCSS"
1
+ import { afterEach, describe, expect, it } from 'vitest'
2
+ import { css } from '../css'
3
+ import { sheet } from '../sheet'
4
+ import { useCSS } from '../useCSS'
5
5
 
6
- describe("useCSS", () => {
6
+ describe('useCSS', () => {
7
7
  afterEach(() => {
8
8
  sheet.clearAll()
9
9
  })
10
10
 
11
- describe("basic usage", () => {
12
- it("returns a className string for static CSS", () => {
13
- const template = css`display: flex;`
11
+ describe('basic usage', () => {
12
+ it('returns a className string for static CSS', () => {
13
+ const template = css`
14
+ display: flex;
15
+ `
14
16
  const result = useCSS(template)
15
- expect(typeof result).toBe("string")
17
+ expect(typeof result).toBe('string')
16
18
  expect(result).toMatch(/^pyr-[0-9a-z]+$/)
17
19
  })
18
20
 
19
- it("returns different classNames for different CSS", () => {
20
- const template1 = css`display: flex;`
21
- const template2 = css`display: block;`
21
+ it('returns different classNames for different CSS', () => {
22
+ const template1 = css`
23
+ display: flex;
24
+ `
25
+ const template2 = css`
26
+ display: block;
27
+ `
22
28
 
23
29
  const r1 = useCSS(template1)
24
30
  const r2 = useCSS(template2)
@@ -28,115 +34,139 @@ describe("useCSS", () => {
28
34
  expect(r1).not.toBe(r2)
29
35
  })
30
36
 
31
- it("returns empty string for empty CSS", () => {
37
+ it('returns empty string for empty CSS', () => {
32
38
  const template = css``
33
39
  const result = useCSS(template)
34
- expect(result).toBe("")
40
+ expect(result).toBe('')
35
41
  })
36
42
 
37
- it("returns empty string for whitespace-only CSS", () => {
38
- const template = css` `
43
+ it('returns empty string for whitespace-only CSS', () => {
44
+ const template = css``
39
45
  const result = useCSS(template)
40
- expect(result).toBe("")
46
+ expect(result).toBe('')
41
47
  })
42
48
  })
43
49
 
44
- describe("dynamic values", () => {
45
- it("works with static interpolation values", () => {
46
- const color = "red"
47
- const template = css`color: ${color};`
50
+ describe('dynamic values', () => {
51
+ it('works with static interpolation values', () => {
52
+ const color = 'red'
53
+ const template = css`
54
+ color: ${color};
55
+ `
48
56
  const result = useCSS(template)
49
57
  expect(result).toMatch(/^pyr-[0-9a-z]+$/)
50
58
  })
51
59
 
52
- it("works with function interpolations resolved via props", () => {
53
- const template = css`color: ${(p: any) => p.color};`
54
- const result = useCSS(template, { color: "blue" })
60
+ it('works with function interpolations resolved via props', () => {
61
+ const template = css`
62
+ color: ${(p: any) => p.color};
63
+ `
64
+ const result = useCSS(template, { color: 'blue' })
55
65
  expect(result).toMatch(/^pyr-[0-9a-z]+$/)
56
66
  })
57
67
 
58
- it("different prop values produce different classNames", () => {
59
- const template = css`color: ${(p: any) => p.color};`
68
+ it('different prop values produce different classNames', () => {
69
+ const template = css`
70
+ color: ${(p: any) => p.color};
71
+ `
60
72
 
61
- const r1 = useCSS(template, { color: "red" })
62
- const r2 = useCSS(template, { color: "green" })
73
+ const r1 = useCSS(template, { color: 'red' })
74
+ const r2 = useCSS(template, { color: 'green' })
63
75
 
64
76
  expect(r1).not.toBe(r2)
65
77
  })
66
78
  })
67
79
 
68
- describe("caching", () => {
69
- it("same CSS returns same className on repeated calls", () => {
70
- const template = css`display: flex;`
80
+ describe('caching', () => {
81
+ it('same CSS returns same className on repeated calls', () => {
82
+ const template = css`
83
+ display: flex;
84
+ `
71
85
  const cls1 = useCSS(template)
72
86
  const cls2 = useCSS(template)
73
87
  expect(cls1).toBe(cls2)
74
88
  })
75
89
 
76
- it("same dynamic CSS with same props returns same className", () => {
77
- const template = css`color: ${(p: any) => p.color};`
78
- const cls1 = useCSS(template, { color: "red" })
79
- const cls2 = useCSS(template, { color: "red" })
90
+ it('same dynamic CSS with same props returns same className', () => {
91
+ const template = css`
92
+ color: ${(p: any) => p.color};
93
+ `
94
+ const cls1 = useCSS(template, { color: 'red' })
95
+ const cls2 = useCSS(template, { color: 'red' })
80
96
  expect(cls1).toBe(cls2)
81
97
  })
82
98
  })
83
99
 
84
- describe("cache hit path", () => {
85
- it("reuses cached className with identical resolved CSS", () => {
86
- const template = css`color: ${(p: any) => p.color};`
87
- const cls1 = useCSS(template, { color: "red" })
88
- const cls2 = useCSS(template, { color: "red" })
100
+ describe('cache hit path', () => {
101
+ it('reuses cached className with identical resolved CSS', () => {
102
+ const template = css`
103
+ color: ${(p: any) => p.color};
104
+ `
105
+ const cls1 = useCSS(template, { color: 'red' })
106
+ const cls2 = useCSS(template, { color: 'red' })
89
107
  expect(cls1).toBe(cls2)
90
108
  expect(cls1).toMatch(/^pyr-/)
91
109
  })
92
110
 
93
- it("updates className when resolved CSS changes", () => {
94
- const template = css`color: ${(p: any) => p.color};`
95
- const cls1 = useCSS(template, { color: "red" })
96
- const cls2 = useCSS(template, { color: "blue" })
111
+ it('updates className when resolved CSS changes', () => {
112
+ const template = css`
113
+ color: ${(p: any) => p.color};
114
+ `
115
+ const cls1 = useCSS(template, { color: 'red' })
116
+ const cls2 = useCSS(template, { color: 'blue' })
97
117
  expect(cls1).not.toBe(cls2)
98
118
  })
99
119
  })
100
120
 
101
- describe("boost parameter", () => {
102
- it("does not throw when boost is true", () => {
103
- const template = css`display: flex;`
121
+ describe('boost parameter', () => {
122
+ it('does not throw when boost is true', () => {
123
+ const template = css`
124
+ display: flex;
125
+ `
104
126
  const result = useCSS(template, undefined, true)
105
127
  expect(result).toMatch(/^pyr-[0-9a-z]+$/)
106
128
  })
107
129
 
108
- it("does not throw when boost is false", () => {
109
- const template = css`display: flex;`
130
+ it('does not throw when boost is false', () => {
131
+ const template = css`
132
+ display: flex;
133
+ `
110
134
  const result = useCSS(template, undefined, false)
111
135
  expect(result).toMatch(/^pyr-[0-9a-z]+$/)
112
136
  })
113
137
  })
114
138
 
115
- describe("without theme and without props", () => {
116
- it("uses empty object when no props and no theme", () => {
117
- const template = css`display: flex;`
139
+ describe('without theme and without props', () => {
140
+ it('uses empty object when no props and no theme', () => {
141
+ const template = css`
142
+ display: flex;
143
+ `
118
144
  const result = useCSS(template)
119
145
  expect(result).toMatch(/^pyr-[0-9a-z]+$/)
120
146
  })
121
147
 
122
- it("handles dynamic template without theme or props", () => {
123
- const template = css`color: ${(p: any) => p.color ?? "red"};`
148
+ it('handles dynamic template without theme or props', () => {
149
+ const template = css`
150
+ color: ${(p: any) => p.color ?? 'red'};
151
+ `
124
152
  const result = useCSS(template, undefined)
125
153
  expect(result).toMatch(/^pyr-/)
126
154
  })
127
155
  })
128
156
 
129
- describe("empty CSS from dynamic resolution", () => {
130
- it("returns empty className when dynamic CSS resolves to empty", () => {
131
- const template = css`${(p: any) => (p.color ? `color: ${p.color};` : "")}`
157
+ describe('empty CSS from dynamic resolution', () => {
158
+ it('returns empty className when dynamic CSS resolves to empty', () => {
159
+ const template = css`
160
+ ${(p: any) => (p.color ? `color: ${p.color};` : '')}
161
+ `
132
162
 
133
163
  // First call: non-empty CSS
134
- const cls1 = useCSS(template, { color: "red" })
164
+ const cls1 = useCSS(template, { color: 'red' })
135
165
  expect(cls1).toMatch(/^pyr-/)
136
166
 
137
167
  // Second call: empty CSS
138
168
  const cls2 = useCSS(template, { color: undefined })
139
- expect(cls2).toBe("")
169
+ expect(cls2).toBe('')
140
170
  })
141
171
  })
142
172
  })
package/src/css.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { CSSResult, type Interpolation } from "./resolve"
1
+ import { CSSResult, type Interpolation } from './resolve'
2
2
 
3
3
  /**
4
4
  * Tagged template function for CSS. Captures the template strings and
package/src/forward.ts CHANGED
@@ -7,189 +7,189 @@
7
7
  // Common HTML attributes, event handlers, and ARIA/data attributes
8
8
  const HTML_PROPS = new Set([
9
9
  // Core props
10
- "className",
11
- "class",
12
- "dangerouslySetInnerHTML",
13
- "htmlFor",
14
- "id",
15
- "key",
16
- "ref",
17
- "style",
18
- "tabIndex",
19
- "role",
10
+ 'className',
11
+ 'class',
12
+ 'dangerouslySetInnerHTML',
13
+ 'htmlFor',
14
+ 'id',
15
+ 'key',
16
+ 'ref',
17
+ 'style',
18
+ 'tabIndex',
19
+ 'role',
20
20
  // Event handlers
21
- "onAbort",
22
- "onAnimationEnd",
23
- "onAnimationIteration",
24
- "onAnimationStart",
25
- "onBlur",
26
- "onChange",
27
- "onClick",
28
- "onCompositionEnd",
29
- "onCompositionStart",
30
- "onCompositionUpdate",
31
- "onContextMenu",
32
- "onCopy",
33
- "onCut",
34
- "onDoubleClick",
35
- "onDrag",
36
- "onDragEnd",
37
- "onDragEnter",
38
- "onDragLeave",
39
- "onDragOver",
40
- "onDragStart",
41
- "onDrop",
42
- "onError",
43
- "onFocus",
44
- "onInput",
45
- "onKeyDown",
46
- "onKeyPress",
47
- "onKeyUp",
48
- "onLoad",
49
- "onMouseDown",
50
- "onMouseEnter",
51
- "onMouseLeave",
52
- "onMouseMove",
53
- "onMouseOut",
54
- "onMouseOver",
55
- "onMouseUp",
56
- "onPaste",
57
- "onPointerCancel",
58
- "onPointerDown",
59
- "onPointerEnter",
60
- "onPointerLeave",
61
- "onPointerMove",
62
- "onPointerOut",
63
- "onPointerOver",
64
- "onPointerUp",
65
- "onScroll",
66
- "onSelect",
67
- "onSubmit",
68
- "onTouchCancel",
69
- "onTouchEnd",
70
- "onTouchMove",
71
- "onTouchStart",
72
- "onTransitionEnd",
73
- "onWheel",
21
+ 'onAbort',
22
+ 'onAnimationEnd',
23
+ 'onAnimationIteration',
24
+ 'onAnimationStart',
25
+ 'onBlur',
26
+ 'onChange',
27
+ 'onClick',
28
+ 'onCompositionEnd',
29
+ 'onCompositionStart',
30
+ 'onCompositionUpdate',
31
+ 'onContextMenu',
32
+ 'onCopy',
33
+ 'onCut',
34
+ 'onDoubleClick',
35
+ 'onDrag',
36
+ 'onDragEnd',
37
+ 'onDragEnter',
38
+ 'onDragLeave',
39
+ 'onDragOver',
40
+ 'onDragStart',
41
+ 'onDrop',
42
+ 'onError',
43
+ 'onFocus',
44
+ 'onInput',
45
+ 'onKeyDown',
46
+ 'onKeyPress',
47
+ 'onKeyUp',
48
+ 'onLoad',
49
+ 'onMouseDown',
50
+ 'onMouseEnter',
51
+ 'onMouseLeave',
52
+ 'onMouseMove',
53
+ 'onMouseOut',
54
+ 'onMouseOver',
55
+ 'onMouseUp',
56
+ 'onPaste',
57
+ 'onPointerCancel',
58
+ 'onPointerDown',
59
+ 'onPointerEnter',
60
+ 'onPointerLeave',
61
+ 'onPointerMove',
62
+ 'onPointerOut',
63
+ 'onPointerOver',
64
+ 'onPointerUp',
65
+ 'onScroll',
66
+ 'onSelect',
67
+ 'onSubmit',
68
+ 'onTouchCancel',
69
+ 'onTouchEnd',
70
+ 'onTouchMove',
71
+ 'onTouchStart',
72
+ 'onTransitionEnd',
73
+ 'onWheel',
74
74
  // HTML attributes
75
- "accept",
76
- "acceptCharset",
77
- "accessKey",
78
- "action",
79
- "allow",
80
- "allowFullScreen",
81
- "alt",
82
- "as",
83
- "async",
84
- "autoCapitalize",
85
- "autoComplete",
86
- "autoCorrect",
87
- "autoFocus",
88
- "autoPlay",
89
- "capture",
90
- "cellPadding",
91
- "cellSpacing",
92
- "charSet",
93
- "checked",
94
- "cite",
95
- "cols",
96
- "colSpan",
97
- "content",
98
- "contentEditable",
99
- "controls",
100
- "controlsList",
101
- "coords",
102
- "crossOrigin",
103
- "dateTime",
104
- "decoding",
105
- "default",
106
- "defaultChecked",
107
- "defaultValue",
108
- "defer",
109
- "dir",
110
- "disabled",
111
- "disablePictureInPicture",
112
- "disableRemotePlayback",
113
- "download",
114
- "draggable",
115
- "encType",
116
- "enterKeyHint",
117
- "fetchPriority",
118
- "form",
119
- "formAction",
120
- "formEncType",
121
- "formMethod",
122
- "formNoValidate",
123
- "formTarget",
124
- "frameBorder",
125
- "headers",
126
- "height",
127
- "hidden",
128
- "high",
129
- "href",
130
- "hrefLang",
131
- "httpEquiv",
132
- "inputMode",
133
- "integrity",
134
- "is",
135
- "label",
136
- "lang",
137
- "list",
138
- "loading",
139
- "loop",
140
- "low",
141
- "max",
142
- "maxLength",
143
- "media",
144
- "method",
145
- "min",
146
- "minLength",
147
- "multiple",
148
- "muted",
149
- "name",
150
- "noModule",
151
- "noValidate",
152
- "nonce",
153
- "open",
154
- "optimum",
155
- "pattern",
156
- "placeholder",
157
- "playsInline",
158
- "poster",
159
- "preload",
160
- "readOnly",
161
- "referrerPolicy",
162
- "rel",
163
- "required",
164
- "reversed",
165
- "rows",
166
- "rowSpan",
167
- "sandbox",
168
- "scope",
169
- "scoped",
170
- "scrolling",
171
- "selected",
172
- "shape",
173
- "size",
174
- "sizes",
175
- "slot",
176
- "span",
177
- "spellCheck",
178
- "src",
179
- "srcDoc",
180
- "srcLang",
181
- "srcSet",
182
- "start",
183
- "step",
184
- "summary",
185
- "target",
186
- "title",
187
- "translate",
188
- "type",
189
- "useMap",
190
- "value",
191
- "width",
192
- "wrap",
75
+ 'accept',
76
+ 'acceptCharset',
77
+ 'accessKey',
78
+ 'action',
79
+ 'allow',
80
+ 'allowFullScreen',
81
+ 'alt',
82
+ 'as',
83
+ 'async',
84
+ 'autoCapitalize',
85
+ 'autoComplete',
86
+ 'autoCorrect',
87
+ 'autoFocus',
88
+ 'autoPlay',
89
+ 'capture',
90
+ 'cellPadding',
91
+ 'cellSpacing',
92
+ 'charSet',
93
+ 'checked',
94
+ 'cite',
95
+ 'cols',
96
+ 'colSpan',
97
+ 'content',
98
+ 'contentEditable',
99
+ 'controls',
100
+ 'controlsList',
101
+ 'coords',
102
+ 'crossOrigin',
103
+ 'dateTime',
104
+ 'decoding',
105
+ 'default',
106
+ 'defaultChecked',
107
+ 'defaultValue',
108
+ 'defer',
109
+ 'dir',
110
+ 'disabled',
111
+ 'disablePictureInPicture',
112
+ 'disableRemotePlayback',
113
+ 'download',
114
+ 'draggable',
115
+ 'encType',
116
+ 'enterKeyHint',
117
+ 'fetchPriority',
118
+ 'form',
119
+ 'formAction',
120
+ 'formEncType',
121
+ 'formMethod',
122
+ 'formNoValidate',
123
+ 'formTarget',
124
+ 'frameBorder',
125
+ 'headers',
126
+ 'height',
127
+ 'hidden',
128
+ 'high',
129
+ 'href',
130
+ 'hrefLang',
131
+ 'httpEquiv',
132
+ 'inputMode',
133
+ 'integrity',
134
+ 'is',
135
+ 'label',
136
+ 'lang',
137
+ 'list',
138
+ 'loading',
139
+ 'loop',
140
+ 'low',
141
+ 'max',
142
+ 'maxLength',
143
+ 'media',
144
+ 'method',
145
+ 'min',
146
+ 'minLength',
147
+ 'multiple',
148
+ 'muted',
149
+ 'name',
150
+ 'noModule',
151
+ 'noValidate',
152
+ 'nonce',
153
+ 'open',
154
+ 'optimum',
155
+ 'pattern',
156
+ 'placeholder',
157
+ 'playsInline',
158
+ 'poster',
159
+ 'preload',
160
+ 'readOnly',
161
+ 'referrerPolicy',
162
+ 'rel',
163
+ 'required',
164
+ 'reversed',
165
+ 'rows',
166
+ 'rowSpan',
167
+ 'sandbox',
168
+ 'scope',
169
+ 'scoped',
170
+ 'scrolling',
171
+ 'selected',
172
+ 'shape',
173
+ 'size',
174
+ 'sizes',
175
+ 'slot',
176
+ 'span',
177
+ 'spellCheck',
178
+ 'src',
179
+ 'srcDoc',
180
+ 'srcLang',
181
+ 'srcSet',
182
+ 'start',
183
+ 'step',
184
+ 'summary',
185
+ 'target',
186
+ 'title',
187
+ 'translate',
188
+ 'type',
189
+ 'useMap',
190
+ 'value',
191
+ 'width',
192
+ 'wrap',
193
193
  ])
194
194
 
195
195
  /**
@@ -204,10 +204,10 @@ export const filterProps = (props: Record<string, unknown>): Record<string, unkn
204
204
  if (key.charCodeAt(0) === 36) continue // '$'
205
205
 
206
206
  // Skip `as` prop — handled separately by styled
207
- if (key === "as") continue
207
+ if (key === 'as') continue
208
208
 
209
209
  // Keep data-* and aria-* attributes
210
- if (key.startsWith("data-") || key.startsWith("aria-")) {
210
+ if (key.startsWith('data-') || key.startsWith('aria-')) {
211
211
  filtered[key] = props[key]
212
212
  continue
213
213
  }
@@ -246,7 +246,7 @@ export const buildProps = (
246
246
  // Component target — forward all props except as/className/class and $-prefixed
247
247
  if (!isDOM) {
248
248
  for (const key in rawProps) {
249
- if (key === "as" || key === "className" || key === "class") continue
249
+ if (key === 'as' || key === 'className' || key === 'class') continue
250
250
  if (key.charCodeAt(0) === 36) continue // $-prefixed transient
251
251
  result[key] = rawProps[key]
252
252
  }
@@ -256,7 +256,7 @@ export const buildProps = (
256
256
  // DOM element with custom shouldForwardProp
257
257
  if (customFilter) {
258
258
  for (const key in rawProps) {
259
- if (key === "as" || key === "className" || key === "class") continue
259
+ if (key === 'as' || key === 'className' || key === 'class') continue
260
260
  if (customFilter(key)) result[key] = rawProps[key]
261
261
  }
262
262
  return result
@@ -264,9 +264,9 @@ export const buildProps = (
264
264
 
265
265
  // DOM element with default filtering
266
266
  for (const key in rawProps) {
267
- if (key === "as" || key === "className" || key === "class") continue
267
+ if (key === 'as' || key === 'className' || key === 'class') continue
268
268
  if (key.charCodeAt(0) === 36) continue // $-prefixed transient
269
- if (key.startsWith("data-") || key.startsWith("aria-")) {
269
+ if (key.startsWith('data-') || key.startsWith('aria-')) {
270
270
  result[key] = rawProps[key]
271
271
  continue
272
272
  }