@pyreon/styler 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 (47) hide show
  1. package/package.json +5 -7
  2. package/src/ThemeProvider.ts +0 -65
  3. package/src/__tests__/ThemeProvider.test.ts +0 -67
  4. package/src/__tests__/benchmark.bench.ts +0 -200
  5. package/src/__tests__/composition-chain.test.ts +0 -537
  6. package/src/__tests__/css.test.ts +0 -70
  7. package/src/__tests__/dev-gate-treeshake.test.ts +0 -85
  8. package/src/__tests__/forward.test.ts +0 -282
  9. package/src/__tests__/globalStyle.test.ts +0 -72
  10. package/src/__tests__/hash.test.ts +0 -70
  11. package/src/__tests__/hybrid-injection.test.ts +0 -225
  12. package/src/__tests__/index.ts +0 -14
  13. package/src/__tests__/inject-rules.browser.test.ts +0 -40
  14. package/src/__tests__/insertion-effect.test.ts +0 -119
  15. package/src/__tests__/integration-dom.test.ts +0 -58
  16. package/src/__tests__/integration.test.ts +0 -179
  17. package/src/__tests__/keyframes.test.ts +0 -68
  18. package/src/__tests__/memory-growth.test.ts +0 -220
  19. package/src/__tests__/native-marker.test.ts +0 -9
  20. package/src/__tests__/p3-features.test.ts +0 -316
  21. package/src/__tests__/resolve-cache.test.ts +0 -94
  22. package/src/__tests__/resolve.test.ts +0 -308
  23. package/src/__tests__/shared.test.ts +0 -133
  24. package/src/__tests__/sheet-advanced.test.ts +0 -659
  25. package/src/__tests__/sheet-split-atrules.test.ts +0 -410
  26. package/src/__tests__/sheet.test.ts +0 -250
  27. package/src/__tests__/static-styler-resolve-cost.test.ts +0 -160
  28. package/src/__tests__/styled-reactive.test.ts +0 -74
  29. package/src/__tests__/styled-ssr.test.ts +0 -75
  30. package/src/__tests__/styled.test.ts +0 -511
  31. package/src/__tests__/styler.browser.test.tsx +0 -194
  32. package/src/__tests__/theme.test.ts +0 -33
  33. package/src/__tests__/useCSS.test.ts +0 -172
  34. package/src/css.ts +0 -13
  35. package/src/env.d.ts +0 -6
  36. package/src/forward.ts +0 -308
  37. package/src/globalStyle.ts +0 -53
  38. package/src/hash.ts +0 -28
  39. package/src/index.ts +0 -15
  40. package/src/keyframes.ts +0 -36
  41. package/src/manifest.ts +0 -332
  42. package/src/resolve.ts +0 -225
  43. package/src/shared.ts +0 -22
  44. package/src/sheet.ts +0 -635
  45. package/src/styled.tsx +0 -503
  46. package/src/tests/manifest-snapshot.test.ts +0 -51
  47. package/src/useCSS.ts +0 -20
package/src/styled.tsx DELETED
@@ -1,503 +0,0 @@
1
- /**
2
- * styled() component factory. Creates Pyreon components that inject CSS
3
- * class names from tagged template literals.
4
- *
5
- * Supports:
6
- * - styled('div')`...` and styled(Component)`...`
7
- * - styled.div`...` (via Proxy)
8
- * - `as` prop for polymorphic rendering
9
- * - $-prefixed transient props (not forwarded to DOM)
10
- * - Custom shouldForwardProp for per-component prop filtering
11
- * - Static path optimization (templates with no dynamic interpolations)
12
- * - Boost specificity via doubled selector
13
- *
14
- * CSS nesting (`&` selectors) works natively — the resolver passes CSS
15
- * through without transformation, so `&:hover`, `&::before`, etc. work
16
- * as-is in browsers supporting CSS Nesting (all modern browsers).
17
- */
18
- import type { ComponentFn, VNode } from '@pyreon/core'
19
- import { h } from '@pyreon/core'
20
- import { computed, renderEffect, runUntracked } from '@pyreon/reactivity'
21
- import { buildProps } from './forward'
22
- import { type Interpolation, normalizeCSS, resolve } from './resolve'
23
- import { isDynamic } from './shared'
24
- import { onSheetClear, sheet } from './sheet'
25
- import { useThemeAccessor } from './ThemeProvider'
26
-
27
- // Dev-time counter sink — see packages/internals/perf-harness/COUNTERS.md.
28
- const _countSink = globalThis as { __pyreon_count__?: (name: string, n?: number) => void }
29
-
30
- type Tag = string | ComponentFn<any>
31
-
32
- export interface StyledOptions {
33
- /** Custom prop filter. Return true to forward the prop to the DOM element. */
34
- shouldForwardProp?: (prop: string) => boolean
35
- /**
36
- * CSS @layer name. Rules are wrapped in `@layer <name> { ... }`.
37
- * Framework CSS uses two layers with explicit ordering:
38
- * `@layer elements, rocketstyle;`
39
- * Elements (base layout) use `'elements'`, rocketstyle themes use
40
- * `'rocketstyle'`. The ordering ensures themes always override base
41
- * styles regardless of source order.
42
- */
43
- layer?: string
44
- }
45
-
46
- const getDisplayName = (tag: Tag): string =>
47
- typeof tag === 'string'
48
- ? tag
49
- : (tag as ComponentFn<any> & { displayName?: string }).displayName || tag.name || 'Component'
50
-
51
- // Component cache: same template literal + tag + no options → same component.
52
- // WeakMap on `strings` (TemplateStringsArray is object-identity per source location).
53
- // `let` so `sheet.clearAll()` (HMR / dev reload) can drop stale entries by
54
- // swapping the WeakMap reference — WeakMap has no `.clear()` method, and stale
55
- // `StaticStyled` ComponentFns left behind would keep returning class names the
56
- // sheet just deleted from the DOM.
57
- let staticComponentCache = new WeakMap<TemplateStringsArray, Map<Tag, ComponentFn>>()
58
-
59
- // Single-entry hot cache — 3 reference comparisons, no Map/WeakMap overhead.
60
- // All 3 fields move atomically (consolidated into one object so `clearAll`
61
- // resets them together — pre-fix, partial state was possible if a reset
62
- // path forgot one field).
63
- const _hotCache: {
64
- strings: TemplateStringsArray | null
65
- tag: Tag | null
66
- component: ComponentFn | null
67
- } = { strings: null, tag: null, component: null }
68
-
69
- // Subscribe to `sheet.clearAll()` (HMR / dev-time reset). Drops both the
70
- // WeakMap and the hot-cache slots so subsequent `styled()` calls produce
71
- // fresh components with up-to-date class names. Static class names emitted
72
- // before `clearAll` are stale by the time the user observes them — the rule
73
- // they pointed at has been deleted from the DOM.
74
- onSheetClear(() => {
75
- staticComponentCache = new WeakMap()
76
- _hotCache.strings = null
77
- _hotCache.tag = null
78
- _hotCache.component = null
79
- })
80
-
81
- const createStyledComponent = (
82
- tag: Tag,
83
- strings: TemplateStringsArray,
84
- values: Interpolation[],
85
- options?: StyledOptions,
86
- ): ComponentFn => {
87
- // Ultra-fast hot cache: 3 reference comparisons → return immediately
88
- if (values.length === 0 && !options) {
89
- if (strings === _hotCache.strings && tag === _hotCache.tag)
90
- return _hotCache.component as ComponentFn
91
-
92
- // WeakMap fallback for alternating patterns
93
- const tagMap = staticComponentCache.get(strings)
94
- if (tagMap) {
95
- const cached = tagMap.get(tag)
96
- if (cached) {
97
- _hotCache.strings = strings
98
- _hotCache.tag = tag
99
- _hotCache.component = cached
100
- return cached
101
- }
102
- }
103
- }
104
-
105
- // Fast check: no values means no dynamic interpolations — avoids .some() scan
106
- const hasDynamicValues = values.length > 0 && values.some(isDynamic)
107
- const customFilter = options ? options.shouldForwardProp : undefined
108
- const insertLayer = options?.layer
109
-
110
- // STATIC FAST PATH: no function interpolations → compute class once at creation time
111
- if (!hasDynamicValues) {
112
- // Inline resolve for the common no-values case
113
- const raw = values.length === 0 ? (strings[0] as string) : resolve(strings, values, {})
114
- const cssText = normalizeCSS(raw)
115
- const hasCss = cssText.length > 0
116
-
117
- const staticClassName = hasCss ? sheet.insert(cssText, false, insertLayer) : ''
118
-
119
- // Hoisted out of the render fn: `tag` is known at component-creation time,
120
- // and `tag` matches `rawProps.as ?? tag` whenever rawProps is empty (the
121
- // common case for `<MyStyled />` without any props). The DOM-ness check
122
- // doesn't change between renders for the same `tag`.
123
- const tagIsDOM = typeof tag === 'string'
124
-
125
- // Pre-built VNode for the no-extra-props hot path (`<MyStyled />`). Same
126
- // shape `h(tag, { class })` would produce per render, but allocated once
127
- // at component-creation time. Mount.ts spreads `vnode.props` into a new
128
- // object before invoking the component (mount.ts:404-418 doesn't mutate
129
- // the source vnode), so sharing the same VNode across mount sites is
130
- // safe. `vnode.children` is empty here because the empty-rawProps branch
131
- // also implies no children were passed — `rawProps.children` would be
132
- // `undefined` and the `Array.isArray ? : ?? : []` chain produces `[]`.
133
- //
134
- // **Cache lifetime**: this VNode references `staticClassName`, which is
135
- // the className the sheet just inserted. If `sheet.clearAll()` runs
136
- // (HMR / dev reload), the className becomes stale BUT the outer
137
- // `staticComponentCache` (and `_hot*` caches) ALSO survive that path —
138
- // so consumers continue to receive the stale className regardless. The
139
- // companion fix to wire `onSheetClear` and reset both caches is tracked
140
- // separately (see PR #561). This optimization is correct under the
141
- // existing cache lifetime contract; the HMR-staleness issue is broader
142
- // than the VNode cache.
143
- const cachedEmptyVNode = h(
144
- tag as string,
145
- staticClassName ? { class: staticClassName } : {},
146
- )
147
-
148
- const StaticStyled: ComponentFn = (rawProps: Record<string, any>): VNode | null => {
149
- // Hot path: no extra props beyond what's empty AND no `ref` / `as`.
150
- // `for ... in` over an empty object is O(0); the `break` exits on the
151
- // first key. Skipping the cache when `ref` is present is necessary
152
- // because the user expects their callback to fire on the mounted DOM
153
- // node — the pre-built VNode has no `ref` in its props.
154
- let hasExtraProps = false
155
- for (const _k in rawProps) {
156
- hasExtraProps = true
157
- break
158
- }
159
- if (!hasExtraProps && rawProps.ref == null) {
160
- if (process.env.NODE_ENV !== 'production')
161
- _countSink.__pyreon_count__?.('styler.staticVNode.hit')
162
- return cachedEmptyVNode
163
- }
164
-
165
- const finalTag = rawProps.as || tag
166
- // Fast `isDOM` when the user didn't pass `as` — reuses the closure-time
167
- // check. Only `typeof` is needed when `as` overrides the tag.
168
- const isDOM = finalTag === tag ? tagIsDOM : typeof finalTag === 'string'
169
- const finalProps = buildProps(rawProps, staticClassName, isDOM, customFilter)
170
-
171
- return h(
172
- finalTag as string,
173
- finalProps,
174
- ...(Array.isArray(rawProps.children)
175
- ? rawProps.children
176
- : rawProps.children != null
177
- ? [rawProps.children]
178
- : []),
179
- )
180
- }
181
-
182
- ;(StaticStyled as ComponentFn & { displayName?: string }).displayName =
183
- `styled(${getDisplayName(tag)})`
184
-
185
- // Store in component cache + hot cache for future reuse
186
- if (!options && values.length === 0) {
187
- let tagMap = staticComponentCache.get(strings)
188
- if (!tagMap) {
189
- tagMap = new Map()
190
- staticComponentCache.set(strings, tagMap)
191
- }
192
- tagMap.set(tag, StaticStyled)
193
- _hotCache.strings = strings
194
- _hotCache.tag = tag
195
- _hotCache.component = StaticStyled
196
- }
197
-
198
- return StaticStyled
199
- }
200
-
201
- // ─── Tier 2: Per-definition class cache ───────────────────────────────────
202
- // Two-level WeakMap: $rocketstyle → $rocketstate → className.
203
- // 50 identical Items with the same resolved theme → 1 resolve + 49 hits.
204
- const classCache = new WeakMap<object, WeakMap<object, string>>()
205
- // Single-key cache for non-rocketstyle styled components (e.g. Element's
206
- // Wrapper, which depends on `$element` + `$childFix`). The key is the
207
- // `$element` object identity; `$childFix` is folded into a `Map<bool,
208
- // string>` per `$element` to avoid wrong-cache hits when childFix differs.
209
- // Element-layer interning (see `@pyreon/elements` Element/component.tsx)
210
- // gives `$element` stable identity across mounts, which is what makes this
211
- // cache fire — analogous to PR #344's rocketstyle dimension memo.
212
- const elClassCache = new WeakMap<object, Map<unknown, string>>()
213
-
214
- // DYNAMIC PATH: uses computed() for reactive class derivation.
215
- //
216
- // Architecture:
217
- // - $rocketstyle/$rocketstate may be function ACCESSORS (from rocketstyle)
218
- // or plain objects (from direct styled() usage).
219
- // - When they're accessors, a computed() tracks them so mode/dimension
220
- // signal changes produce a new CSS class reactively.
221
- // - The resolve() itself runs UNTRACKED inside the computed to prevent
222
- // exponential cascade from theme deep-reads in interpolation functions.
223
- // - The computed memoizes by string equality — same CSS class = no DOM update.
224
- // - Pyreon's built-in renderEffect handles the DOM class attribute update
225
- // when the computed value changes.
226
- //
227
- // This gives reactive mode/dimension switching WITHOUT per-component effect().
228
- const DynamicStyled: ComponentFn = (rawProps: Record<string, any>): VNode | null => {
229
- const themeAccessor = useThemeAccessor()
230
- const theme = themeAccessor() // snapshot for initial + static path
231
- const $rs = rawProps.$rocketstyle
232
- const $rsState = rawProps.$rocketstate
233
- const isReactiveRS = typeof $rs === 'function'
234
- const isReactiveState = typeof $rsState === 'function'
235
-
236
- // Helper: resolve CSS + cache result
237
- const doResolve = (rs: any, rsState: any, t: any): string => {
238
- // Tier 2 cache: skip resolve if same object identity seen before
239
- if (rs && typeof rs === 'object' && rsState && typeof rsState === 'object') {
240
- const inner = classCache.get(rs)
241
- if (inner) {
242
- const cached = inner.get(rsState)
243
- if (cached !== undefined) return cached
244
- }
245
- }
246
-
247
- // Element-layer cache (no rocketstyle props, but $element is present
248
- // and an object). Fires only when the rocketstyle path didn't apply
249
- // — they're mutually exclusive in practice.
250
- const $el = rawProps.$element
251
- const $childFix = rawProps.$childFix
252
- const useElCache =
253
- (!rs || typeof rs !== 'object' || !rsState || typeof rsState !== 'object') &&
254
- $el &&
255
- typeof $el === 'object'
256
- if (useElCache) {
257
- const inner = elClassCache.get($el as object)
258
- if (inner) {
259
- const cached = inner.get($childFix)
260
- if (cached !== undefined) {
261
- if (process.env.NODE_ENV !== 'production')
262
- _countSink.__pyreon_count__?.('styler.elClassCache.hit')
263
- return cached
264
- }
265
- }
266
- }
267
-
268
- const resolveProps = {
269
- ...rawProps,
270
- ...(isReactiveRS ? { $rocketstyle: rs } : {}),
271
- ...(isReactiveState ? { $rocketstate: rsState } : {}),
272
- theme: t,
273
- }
274
- const cssText = normalizeCSS(resolve(strings, values, resolveProps))
275
- const className = cssText.length > 0 ? sheet.insert(cssText, false, insertLayer) : ''
276
-
277
- if (rs && typeof rs === 'object' && rsState && typeof rsState === 'object') {
278
- let inner = classCache.get(rs)
279
- if (!inner) {
280
- inner = new WeakMap()
281
- classCache.set(rs, inner)
282
- }
283
- inner.set(rsState, className)
284
- } else if (useElCache) {
285
- let inner = elClassCache.get($el as object)
286
- if (!inner) {
287
- inner = new Map()
288
- elClassCache.set($el as object, inner)
289
- }
290
- inner.set($childFix, className)
291
- }
292
- return className
293
- }
294
-
295
- // If any axis is reactive, wrap in computed that tracks all three:
296
- // 1. $rocketstyle accessor (mode + dimension signals)
297
- // 2. $rocketstate accessor (state descriptor)
298
- // 3. themeAccessor (user-preference theme swap)
299
- // The resolve itself runs UNTRACKED to prevent exponential cascade.
300
- const hasReactive = isReactiveRS || isReactiveState
301
- const cssClass = hasReactive
302
- ? computed(() => {
303
- // TRACKED reads:
304
- const rs = isReactiveRS ? $rs() : $rs
305
- const rsState = isReactiveState ? $rsState() : $rsState
306
- const t = themeAccessor() // TRACKED — theme swap
307
-
308
- // UNTRACKED: resolve + sheet insert
309
- return runUntracked(() => doResolve(rs, rsState, t))
310
- }, { equals: (a, b) => a === b })
311
- : null
312
-
313
- const finalTag = rawProps.as || tag
314
- const isDOM = typeof finalTag === 'string'
315
-
316
- // Initial class: computed (reactive) or direct resolve (static)
317
- const className = cssClass
318
- ? cssClass()
319
- : doResolve($rs, $rsState, theme)
320
- const finalProps = buildProps(rawProps, className, isDOM, customFilter)
321
-
322
- // Reactive path: lightweight renderEffect that reads the pre-computed
323
- // class string and toggles classList. This is NOT the old PR #258
324
- // approach — the expensive resolve() already happened inside the
325
- // computed. This renderEffect only does: read string → compare → toggle.
326
- if (cssClass) {
327
- let el: Element | null = null
328
- let currentClassName = className
329
-
330
- const originalRef = finalProps.ref
331
- finalProps.ref = (node: Element | null) => {
332
- el = node
333
- if (originalRef) {
334
- if (typeof originalRef === 'function') originalRef(node)
335
- else if (originalRef && typeof originalRef === 'object') (originalRef as any).current = node
336
- }
337
- }
338
-
339
- renderEffect(() => {
340
- const newClass = cssClass() // reads computed — O(1), just string
341
- if (el && newClass !== currentClassName) {
342
- if (currentClassName) el.classList.remove(currentClassName)
343
- if (newClass) el.classList.add(newClass)
344
- currentClassName = newClass
345
- }
346
- })
347
- }
348
-
349
- return h(
350
- finalTag as string,
351
- finalProps,
352
- ...(Array.isArray(rawProps.children)
353
- ? rawProps.children
354
- : rawProps.children != null
355
- ? [rawProps.children]
356
- : []),
357
- )
358
- }
359
-
360
- ;(DynamicStyled as ComponentFn & { displayName?: string }).displayName =
361
- `styled(${getDisplayName(tag)})`
362
- return DynamicStyled
363
- }
364
-
365
- /** Factory function: styled(tag) returns a tagged template function. */
366
- const styledFactory = (tag: Tag, options?: StyledOptions) => {
367
- const templateFn = (strings: TemplateStringsArray, ...values: Interpolation[]) =>
368
- createStyledComponent(tag, strings, values, options)
369
-
370
- return templateFn
371
- }
372
-
373
- /**
374
- * Main styled export. Supports both calling conventions:
375
- * - `styled('div')` or `styled(Component)` → returns tagged template function
376
- * - `styled('div', { shouldForwardProp })` → with custom prop filtering
377
- * - `styled.div` → shorthand via Proxy (no options)
378
- */
379
- // Cache template functions per tag to avoid closure allocation on every Proxy get
380
- const proxyCache = new Map<string, (...args: any[]) => any>()
381
-
382
- /**
383
- * Generic tagged template function returned by `styled(tag)` and `styled.tag`.
384
- *
385
- * Accepts an optional type parameter `<P>` for consumer-defined props
386
- * (typically transient $-prefixed props that aren't forwarded to the DOM).
387
- *
388
- * @example
389
- * const Box = styled('div')<{ $color: string }>`
390
- * background: ${(props) => props.$color};
391
- * `
392
- * <Box $color="red" /> // $color is required and typed
393
- */
394
- type TagTemplateFn = <P extends object = Record<string, unknown>>(
395
- strings: TemplateStringsArray,
396
- ...values: Interpolation<P>[]
397
- ) => ComponentFn<P & Record<string, unknown>>
398
-
399
- type HtmlTags =
400
- | 'a'
401
- | 'abbr'
402
- | 'address'
403
- | 'article'
404
- | 'aside'
405
- | 'audio'
406
- | 'b'
407
- | 'blockquote'
408
- | 'body'
409
- | 'br'
410
- | 'button'
411
- | 'canvas'
412
- | 'caption'
413
- | 'code'
414
- | 'col'
415
- | 'colgroup'
416
- | 'dd'
417
- | 'details'
418
- | 'div'
419
- | 'dl'
420
- | 'dt'
421
- | 'em'
422
- | 'fieldset'
423
- | 'figcaption'
424
- | 'figure'
425
- | 'footer'
426
- | 'form'
427
- | 'h1'
428
- | 'h2'
429
- | 'h3'
430
- | 'h4'
431
- | 'h5'
432
- | 'h6'
433
- | 'head'
434
- | 'header'
435
- | 'hr'
436
- | 'html'
437
- | 'i'
438
- | 'iframe'
439
- | 'img'
440
- | 'input'
441
- | 'label'
442
- | 'legend'
443
- | 'li'
444
- | 'link'
445
- | 'main'
446
- | 'mark'
447
- | 'menu'
448
- | 'meta'
449
- | 'nav'
450
- | 'ol'
451
- | 'optgroup'
452
- | 'option'
453
- | 'output'
454
- | 'p'
455
- | 'picture'
456
- | 'pre'
457
- | 'progress'
458
- | 'q'
459
- | 'section'
460
- | 'select'
461
- | 'small'
462
- | 'source'
463
- | 'span'
464
- | 'strong'
465
- | 'style'
466
- | 'sub'
467
- | 'summary'
468
- | 'sup'
469
- | 'svg'
470
- | 'table'
471
- | 'tbody'
472
- | 'td'
473
- | 'template'
474
- | 'textarea'
475
- | 'tfoot'
476
- | 'th'
477
- | 'thead'
478
- | 'time'
479
- | 'tr'
480
- | 'u'
481
- | 'ul'
482
- | 'video'
483
-
484
- export type StyledFunction = ((tag: Tag, options?: StyledOptions) => TagTemplateFn) & {
485
- [K in HtmlTags]: TagTemplateFn
486
- }
487
-
488
- // Proxy is needed to support styled.div`...` syntax; the cast bridges
489
- // styledFactory's call signature to StyledFunction which adds HTML tag properties.
490
- // Proxy target uses `as any` because TS can't resolve Proxy<StyledFunction> with mapped types
491
- export const styled: StyledFunction = new Proxy(styledFactory as any, {
492
- get(_target: unknown, prop: string) {
493
- if (prop === 'prototype' || prop === '$$typeof') return undefined
494
- // styled.div`...`, styled.span`...`, etc.
495
- let fn = proxyCache.get(prop)
496
- if (!fn) {
497
- fn = (strings: TemplateStringsArray, ...values: Interpolation[]) =>
498
- createStyledComponent(prop, strings, values)
499
- proxyCache.set(prop, fn)
500
- }
501
- return fn
502
- },
503
- })
@@ -1,51 +0,0 @@
1
- import {
2
- renderApiReferenceEntries,
3
- renderLlmsFullSection,
4
- renderLlmsTxtLine,
5
- } from '@pyreon/manifest'
6
- import manifest from '../manifest'
7
-
8
- describe('gen-docs — styler snapshot', () => {
9
- it('renders a llms.txt bullet starting with the package prefix', () => {
10
- const line = renderLlmsTxtLine(manifest)
11
- expect(line.startsWith('- @pyreon/styler —')).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/styler —')).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
- 'styler/StyleSheet',
24
- 'styler/ThemeContext',
25
- 'styler/ThemeProvider',
26
- 'styler/buildProps',
27
- 'styler/clearNormCache',
28
- 'styler/createGlobalStyle',
29
- 'styler/createSheet',
30
- 'styler/css',
31
- 'styler/filterProps',
32
- 'styler/isDynamic',
33
- 'styler/keyframes',
34
- 'styler/normalizeCSS',
35
- 'styler/resolve',
36
- 'styler/resolveValue',
37
- 'styler/sheet',
38
- 'styler/styled',
39
- 'styler/useCSS',
40
- 'styler/useTheme',
41
- 'styler/useThemeAccessor',
42
- ])
43
- })
44
-
45
- it('carries the CSS-in-JS foot-gun catalog into MCP mistakes for flagship APIs', () => {
46
- const r = renderApiReferenceEntries(manifest)
47
- expect(r['styler/styled']?.mistakes).toContain('transient')
48
- expect(r['styler/css']?.mistakes).toContain('lazy')
49
- expect(r['styler/useTheme']?.mistakes).toContain('snapshot')
50
- })
51
- })
package/src/useCSS.ts DELETED
@@ -1,20 +0,0 @@
1
- /**
2
- * Hook that resolves a CSSResult template with props, injects CSS
3
- * into the shared stylesheet, and returns the className.
4
- *
5
- * Use this when you need computed CSS class names on plain elements
6
- * without the overhead of a styled component layer.
7
- */
8
- import { type CSSResult, normalizeCSS, resolve } from './resolve'
9
- import { sheet } from './sheet'
10
- import { useTheme } from './ThemeProvider'
11
-
12
- export function useCSS(template: CSSResult, props?: Record<string, any>, boost?: boolean): string {
13
- const theme = useTheme()
14
- const allProps = theme ? { ...props, theme } : (props ?? {})
15
- const cssText = normalizeCSS(resolve(template.strings, template.values, allProps))
16
-
17
- if (!cssText.trim()) return ''
18
-
19
- return sheet.insert(cssText, boost)
20
- }