@bamboocss/shared@1.13.2
Patch Changes
-
79c9872: Assemble class names without the throwaway arrays.
Every style leaf of every
css()cache miss built an array for the prefix, filtered it and joined it — and most
configs set no prefix, so that array only ever held the class it started with. Conditions were spread into a second
array and joined even when there were none.- A flat
css()cache miss end to end: 1710 → 1443 ns (-15.6%) - One with conditions and a responsive value: 2675 → 2589 ns (-3.2%)
- Measured on the assembly alone, with the memo forced to miss: +25% flat, +10% with a condition, +14%
with a condition and a prefix - Class names are unchanged across a 27,000-object corpus, and across 43,008 combinations of prefix, class, condition
and hashing
The prefix is now read once when the
cssfunction is built rather than per leaf. It is set in theUtility
constructor and theutility:createdhook can only replacetoHash, so there is nothing to re-read. - A flat
-
61fe88c: Answer "is this style object empty" without building the compacted object.
mergeCssdiscards style objects that hold nothing once undefined values are dropped, and it decided that by
compacting the object, taking a key array for the result, and throwing both away. It only ever needed to know whether
one defined value existed.- The predicate itself: 19x on a three-key style object, 43x on a twenty-key one
- A flat
css()cache miss end to end: 2030 → 1857 ns (-8.5%); the nested case moves within noise - Class names are unchanged across a 27,000-object corpus
The predicate is the same one:
Object.keysenumerates exactly whatcompact'sObject.entriesdid, so own,
enumerable and string-keyed still decide it, andnullstill counts as present whereundefineddoes not. -
be3764d: Skip the per-leaf string rewrites that have nothing to rewrite.
sanitize,isImportant,withoutImportantandwithoutSpacerun on every style leaf of everycss()cache miss,
and each one starts with a regex rewrite. For the values that dominate real style objects —red,4px,lg— all
four are no-ops. Each now begins with the cheapest search that can prove there is nothing to do.- A flat
css()cache miss: 2474 → 2027 ns (-18%) - One with conditions and a responsive value: 3040 → 2808 ns (-7.6%)
- Class names are unchanged across a 27,000-object corpus covering conditions, responsive arrays,
!important, and
values carrying whitespace
The guards are exact rather than approximate, which is the only thing making them safe:
/\s/is precisely the class
the collapse matched,trim()strips precisely that set again, and/\s*!(important)?/cannot match a string with no
!.withoutImportantandwithoutSpacenow declarestring | Tinstead of inferring it. They return a rewritten
string, so inferringTwould have promised callers back the literal they passed in. - A flat
-
7a63215: Stop rebuilding style objects that are already normal.
Normalizing renames a shorthand to its longhand, expands a responsive array into a breakpoint object, and drops
nullish leaves. A flat object of plain values written in longhand needs none of the three — which is most of what
css()is handed — but it was still walked and rebuilt, with a path array allocated per key.- Normalizing a flat object, measured through
mergeCss: -22% to -26%, and -28% for one carrying twenty
properties - A flat
css()cache miss end to end: 1825 → 1685 ns (-7.7%) - Class names are unchanged across a 27,000-object corpus
An object that does need normalizing pays for the check that found out, which measures between +2% and +7% depending
on how late the first dirty key appears. The nested case is around -4% overall, since the same objects tend to have
flat blocks inside them.The result may now be the argument itself rather than a fresh object, so callers have to treat it as read-only. Every
one already does: merging accumulates into its own object, andcss.raw()andcva.raw()clone at the boundary. - Normalizing a flat object, measured through
-
2130606: Call
splitPropspredicates with the key alone.The predicate was handed straight to
Array.prototype.filter, which calls it with(key, index, allKeys). A
one-parameter predicate cannot see the extra arguments, but a memoized one reads its whole argument list — and the
predicate the JSX factory passes isisCssProperty, which is memoized. So the memo hashed the entire key array once
per prop, and keyed its cache on it: two elements with different prop sets shared no entry even for the same prop
name.Every styled element pays this, once per prop, on every render.
splitPropswith a memoized predicate: 6.0x faster- A React SSR render of styled elements: 4.18 → 1.15 µs per element (3.6x)
- The same for elements with a
cvaconfig: 11.2 → 2.17 µs per element (5.2x) - Markup and
splitPropsoutput are unchanged
Predicates have always been typed
(key: string) => boolean, so no typed caller could have read the extra arguments.