Skip to content

cn@0.2.3

Choose a tag to compare

@github-actions github-actions released this 02 Sep 01:15
· 8 commits to main since this release
b336e09

Patch Changes

  • #10 bf918c2 Thanks @aidenybai! - perf: non-string arguments and JSC hit path.

    • Object and array arguments resolve in place and ride the argument-identity cache instead of taking a full clsx join on every call; the prediction probe runs again over the resolved strings, so cn(base, { active: isActive }) verifies by identity like an all-string call. 0.83× to 0.88× per call on object and nested-array shapes on node, similar on bun.
    • A single array argument takes the argument path (cn([a, b]) is cn(a, b) under clsx flattening), so stable element identities hit: 0.10× on node, 0.15× on bun.
    • An argument-cache bucket holds 256 tuples instead of 8. The cache keys on the first argument, a component's base string at every usage site, so one key routinely carries dozens of tuples; the old cap evicted them faster than the sequence chain could learn them. 64 sites sharing a base: 301 ns to 6.4 ns per call on node, 153 ns to 5.4 ns on bun.
    • A freshly joined string (the arg-cache miss path) goes through the doorkeeper before the whole-string cache. A first sighting merges straight through with no dictionary lookup on a never-seen key (V8 hashes and internalizes those: ~200 ns at 17 chars, ~1.1 µs at 360) and no cache entry anywhere, so a site with a dynamic arbitrary value (translate-x-[${x}px]) stops churning both caches: 0.16× to 0.43× per call on node, 0.14× to 0.58× on bun. The doorkeeper hash now also folds the first and last eight characters, where arbitrary values keep their digits, which cuts cold arbitrary-value renders to 0.83× on node and 0.78× on bun.
    • On JavaScriptCore the whole-string cache is a Map: JSC looks up a string key in a dictionary-mode object in ~21 ns and a fresh key in ~220 ns, where a Map takes 6 and 75 (V8 is the reverse and keeps the dictionary). 8k-string working sets on bun: 25 ns to 6.6 ns per hit, SSR streams 0.92×; 16-string sets pay ~0.5 ns.
    • On JavaScriptCore the whole-string cache hit is a tiny inlinable front with the miss body outlined: recurring single-string calls 0.62× to 0.71× on bun. V8 keeps the single-closure form, where the outlined body measured 1.17× on long strings.

    All-string call shapes are unchanged on both engines. Size gate: 10,602 gz.