@bamboocss/generator 1.13.2 → 1.15.0
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.
- package/dist/index.cjs +255 -22
- package/dist/index.mjs +255 -22
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -143,7 +143,7 @@ function generateCssFn(ctx) {
|
|
|
143
143
|
const { separator, getPropShorthands } = utility;
|
|
144
144
|
return {
|
|
145
145
|
dts: outdent.outdent`
|
|
146
|
-
${ctx.file.importType("SystemStyleObject", "../types/index")}
|
|
146
|
+
${ctx.file.importType("SystemStyleObject, ViewTransitionFn", "../types/index")}
|
|
147
147
|
|
|
148
148
|
type Styles = SystemStyleObject | undefined | null | false
|
|
149
149
|
|
|
@@ -165,6 +165,39 @@ function generateCssFn(ctx) {
|
|
|
165
165
|
|
|
166
166
|
export declare const css: CssFunction;
|
|
167
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Build a fallback value: a list of candidates, most-preferred first, emitted as repeated
|
|
170
|
+
* declarations so the browser keeps the last one it understands.
|
|
171
|
+
*
|
|
172
|
+
* Sugar for the string form — \`fallback('100dvh', '100vh')\` is \`'fallback(100dvh, 100vh)'\`.
|
|
173
|
+
* The candidates are not individually type-checked, the same trade the \`[...]\` escape
|
|
174
|
+
* hatch makes.
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* css({ height: fallback('calc(100dvh - 100px)', 'calc(100vh - 100px)') })
|
|
178
|
+
*
|
|
179
|
+
* @see https://bamboocss.com/docs/concepts/writing-styles#fallback-values
|
|
180
|
+
*/
|
|
181
|
+
export declare function fallback(preferred: string | number, ...rest: Array<string | number>): \`fallback(\${string})\`;
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Style the View Transitions API and get back one stable class for the bag.
|
|
185
|
+
*
|
|
186
|
+
* The class is applied through \`view-transition-class\`, so the same transition can be
|
|
187
|
+
* shared by any number of elements. You still set \`view-transition-name\` yourself —
|
|
188
|
+
* it has to be unique per element, so bamboo cannot share it for you.
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* const slide = viewTransition({
|
|
192
|
+
* group: { animationDuration: '0.4s' },
|
|
193
|
+
* old: { animationName: 'slide-out' },
|
|
194
|
+
* new: { animationName: 'slide-in' },
|
|
195
|
+
* })
|
|
196
|
+
*
|
|
197
|
+
* @see https://bamboocss.com/docs/concepts/view-transitions
|
|
198
|
+
*/
|
|
199
|
+
export declare const viewTransition: ViewTransitionFn;
|
|
200
|
+
|
|
168
201
|
/**
|
|
169
202
|
* Internal. Emitted for the source transform, which rewrites a single dynamic style
|
|
170
203
|
* leaf into a call to this. Not part of the authoring API.
|
|
@@ -172,7 +205,7 @@ function generateCssFn(ctx) {
|
|
|
172
205
|
export declare const cssLeaf: (prefix: string, prop: string, value: unknown) => string;
|
|
173
206
|
`,
|
|
174
207
|
js: outdent.outdent`
|
|
175
|
-
${ctx.file.import("cloneStyles, createCss, createMergeCss, hypenateProperty, leafClass, memo, withoutSpace", "../helpers")}
|
|
208
|
+
${ctx.file.import("cloneStyles, createCss, createMergeCss, hypenateProperty, leafClass, memo, viewTransitionClassName, withoutSpace", "../helpers")}
|
|
176
209
|
${ctx.file.import("sortConditions, finalizeConditions", "./conditions")}
|
|
177
210
|
|
|
178
211
|
const utilities = "${utility.entries().map(([prop, className]) => {
|
|
@@ -237,6 +270,16 @@ function generateCssFn(ctx) {
|
|
|
237
270
|
return className === undefined ? css({ [prop]: value }) : className
|
|
238
271
|
}
|
|
239
272
|
|
|
273
|
+
// Sugar for the string form, so the feature has an import to discover, a signature to
|
|
274
|
+
// hover and a name the editor can complete. The extractor evaluates the call, so the
|
|
275
|
+
// value reaching \`css()\` is the same literal either way.
|
|
276
|
+
export const fallback = (...values) => \`fallback($\{values.join(', ')})\`
|
|
277
|
+
|
|
278
|
+
// The class is the whole return value — the CSS behind it was emitted at build time
|
|
279
|
+
// from the same options, hashed by this same function. A call the extractor never saw
|
|
280
|
+
// still returns a class, exactly as \`css()\` does for a value it never saw.
|
|
281
|
+
export const viewTransition = (options) => viewTransitionClassName(options, ${JSON.stringify(prefix.className ?? "")})
|
|
282
|
+
|
|
240
283
|
export const { mergeCss, assignCss } = createMergeCss(context)
|
|
241
284
|
`
|
|
242
285
|
};
|
|
@@ -420,31 +463,205 @@ function generateCvaFn(ctx) {
|
|
|
420
463
|
}
|
|
421
464
|
//#endregion
|
|
422
465
|
//#region src/artifacts/js/cx.ts
|
|
423
|
-
|
|
466
|
+
const dts = outdent.default`
|
|
467
|
+
type Argument = string | boolean | null | undefined | Argument[]
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Join classNames into a single string, with the last conflicting utility winning.
|
|
471
|
+
*
|
|
472
|
+
* \`cx('px_4', 'px_2')\` is \`'px_2'\`: two classes that set the same property under the
|
|
473
|
+
* same conditions cannot both apply, and which one the browser picks would otherwise
|
|
474
|
+
* depend on their order in the stylesheet rather than on the order you passed them.
|
|
475
|
+
* Classes bamboo did not generate are left alone, duplicates included.
|
|
476
|
+
*/
|
|
477
|
+
export declare function cx(...args: Argument[]): string
|
|
478
|
+
`;
|
|
479
|
+
/**
|
|
480
|
+
* The plain concatenating \`cx\`, for when the class names carry nothing to merge on.
|
|
481
|
+
*
|
|
482
|
+
* With \`hash.className\` every class is an opaque hash, so there is no property to compare
|
|
483
|
+
* and no merge to do — emitting the matcher would only cost bytes on a path that runs in
|
|
484
|
+
* the browser on every render.
|
|
485
|
+
*/
|
|
486
|
+
function concatOnly() {
|
|
487
|
+
return outdent.default`
|
|
488
|
+
function cx(...args) {
|
|
489
|
+
let str = ''
|
|
490
|
+
|
|
491
|
+
for (let i = 0; i < args.length; i++) {
|
|
492
|
+
const arg = args[i]
|
|
493
|
+
if (!arg) continue
|
|
494
|
+
// Arrays are part of the declared type, so this branch has to handle them even
|
|
495
|
+
// though it does no merging — returning '' for \`cx(['a', 'b'])\` would be a lie.
|
|
496
|
+
const part = Array.isArray(arg) ? cx(...arg) : typeof arg === 'string' ? arg : ''
|
|
497
|
+
if (!part) continue
|
|
498
|
+
str && (str += ' ')
|
|
499
|
+
str += part
|
|
500
|
+
}
|
|
501
|
+
return str
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
export { cx }
|
|
505
|
+
`;
|
|
506
|
+
}
|
|
507
|
+
function generateCx(ctx) {
|
|
508
|
+
const { utility, hash, prefix } = ctx;
|
|
509
|
+
const separatorChar = utility.separator;
|
|
510
|
+
const utilityClassNames = [...new Set(utility.keys().map((key) => {
|
|
511
|
+
const withEmptyValue = utility.getClassName(utility.resolveShorthand(key), "");
|
|
512
|
+
return withEmptyValue.endsWith(separatorChar) ? withEmptyValue.slice(0, -separatorChar.length) : withEmptyValue;
|
|
513
|
+
}))].sort();
|
|
514
|
+
const recipeClassNames = [...new Set(ctx.recipes.details.map((node) => node.className))].filter(Boolean).sort();
|
|
515
|
+
if (hash.className || utilityClassNames.length === 0) return {
|
|
516
|
+
js: concatOnly(),
|
|
517
|
+
dts
|
|
518
|
+
};
|
|
424
519
|
return {
|
|
425
520
|
js: outdent.default`
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
521
|
+
const cxSeparator = ${JSON.stringify(utility.separator)}
|
|
522
|
+
const cxPrefix = ${prefix.className ? JSON.stringify(prefix.className + "-") : "''"}
|
|
523
|
+
const cxUtilities = new Set(${JSON.stringify(utilityClassNames.join(","))}.split(','))
|
|
524
|
+
const cxRecipes = ${recipeClassNames.length ? `new Set(${JSON.stringify(recipeClassNames.join(","))}.split(','))` : "null"}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* The declaration a bamboo class sets: its condition path plus the property, without the
|
|
528
|
+
* value. Two classes sharing one are alternatives, and only the last can apply.
|
|
529
|
+
*
|
|
530
|
+
* \`null\` for anything that is not a bamboo class, which is then never merged.
|
|
531
|
+
*/
|
|
532
|
+
function mergeKey(className) {
|
|
533
|
+
let end = className.length
|
|
534
|
+
|
|
535
|
+
// \`c_red\` and \`c_red!\` are the same declaration. Argument order decides between them,
|
|
536
|
+
// which is the point of this function — the cascade would always pick the important
|
|
537
|
+
// one no matter which the caller asked for.
|
|
538
|
+
if (end > 0 && className.charCodeAt(end - 1) === 33) end -= 1
|
|
539
|
+
if (end === 0) return null
|
|
540
|
+
|
|
541
|
+
// The last colon ends the condition path — but only one outside brackets, since an
|
|
542
|
+
// arbitrary selector carries its own: \`[&[data-x="a:b"]]:px_4\`.
|
|
543
|
+
let depth = 0
|
|
544
|
+
let lastColon = -1
|
|
545
|
+
for (let i = 0; i < end; i++) {
|
|
546
|
+
const code = className.charCodeAt(i)
|
|
547
|
+
if (code === 91) depth++
|
|
548
|
+
else if (code === 93) depth--
|
|
549
|
+
else if (code === 58 && depth === 0) lastColon = i
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Conditions come before the prefix — \`hover:bam-px_4\` — so the prefix is skipped
|
|
553
|
+
// after the condition path, not before it. When a prefix is configured every class
|
|
554
|
+
// bamboo emits carries it, so one that does not is by definition someone else's.
|
|
555
|
+
let propStart = lastColon + 1
|
|
556
|
+
if (cxPrefix) {
|
|
557
|
+
if (!className.startsWith(cxPrefix, propStart)) return null
|
|
558
|
+
propStart += cxPrefix.length
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// A recipe owns its whole class, bare or with a \`--variant\` suffix, whatever it
|
|
562
|
+
// looks like to the utility matcher below.
|
|
563
|
+
if (cxRecipes !== null) {
|
|
564
|
+
const variantIdx = className.indexOf('--', propStart)
|
|
565
|
+
const base = variantIdx === -1 ? className.slice(propStart, end) : className.slice(propStart, variantIdx)
|
|
566
|
+
if (cxRecipes.has(base)) return null
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// The longest registered utility name wins, not the first separator. Utility names
|
|
570
|
+
// contain the separator themselves under \`separator: '-'\` — \`bd-w\`, \`ov-x\`,
|
|
571
|
+
// \`translate-x\` — and their leading segment is often a utility too, so stopping at the
|
|
572
|
+
// first \`-\` would key \`bd-w-4px\` and \`bd-c-red\` both on \`bd\` and drop one of them.
|
|
573
|
+
let property = null
|
|
574
|
+
let sepIdx = className.indexOf(cxSeparator, propStart)
|
|
575
|
+
while (sepIdx > propStart && sepIdx < end) {
|
|
576
|
+
const candidate = className.slice(propStart, sepIdx)
|
|
577
|
+
if (cxUtilities.has(candidate)) property = candidate
|
|
578
|
+
sepIdx = className.indexOf(cxSeparator, sepIdx + 1)
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Only a class bamboo generated for a utility. A recipe class or a hand-written one
|
|
582
|
+
// may well contain the separator, and merging on the text before it would drop a
|
|
583
|
+
// class the caller meant to keep.
|
|
584
|
+
if (property === null) return null
|
|
585
|
+
|
|
586
|
+
return lastColon === -1 ? property : className.slice(0, lastColon) + ':' + property
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function isClassWhitespace(code) {
|
|
590
|
+
return code === 32 || code === 9 || code === 10 || code === 12 || code === 13
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function flattenParts(parts, out) {
|
|
594
|
+
for (let i = 0; i < parts.length; i++) {
|
|
595
|
+
const part = parts[i]
|
|
596
|
+
if (!part) continue
|
|
597
|
+
if (Array.isArray(part)) flattenParts(part, out)
|
|
598
|
+
else if (typeof part === 'string') out.push(part)
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function mergeClassStrings(classes) {
|
|
603
|
+
const seen = new Map()
|
|
604
|
+
const order = []
|
|
605
|
+
let id = 0
|
|
606
|
+
|
|
607
|
+
for (let c = 0; c < classes.length; c++) {
|
|
608
|
+
const cls = classes[c]
|
|
609
|
+
let tokenStart = 0
|
|
610
|
+
|
|
611
|
+
for (let i = 0; i <= cls.length; i++) {
|
|
612
|
+
// The class attribute splits on all ASCII whitespace, not just the space, and a
|
|
613
|
+
// multi-line template literal is an ordinary way to write one.
|
|
614
|
+
if (i !== cls.length && !isClassWhitespace(cls.charCodeAt(i))) continue
|
|
615
|
+
if (i === tokenStart) {
|
|
616
|
+
tokenStart = i + 1
|
|
617
|
+
continue
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
const token = cls.slice(tokenStart, i)
|
|
621
|
+
tokenStart = i + 1
|
|
622
|
+
|
|
623
|
+
const key = mergeKey(token)
|
|
624
|
+
if (key !== null) {
|
|
625
|
+
// Keeps the first position and the last value, so a later override lands where
|
|
626
|
+
// the class it replaces already sat.
|
|
627
|
+
if (!seen.has(key)) order.push(key)
|
|
628
|
+
seen.set(key, token)
|
|
629
|
+
} else {
|
|
630
|
+
// Not ours to reason about — kept as written, duplicates and all.
|
|
631
|
+
const uniqueKey = '\\0' + id++
|
|
632
|
+
order.push(uniqueKey)
|
|
633
|
+
seen.set(uniqueKey, token)
|
|
634
|
+
}
|
|
435
635
|
}
|
|
436
636
|
}
|
|
637
|
+
|
|
638
|
+
if (order.length === 0) return ''
|
|
639
|
+
let str = seen.get(order[0])
|
|
640
|
+
for (let i = 1; i < order.length; i++) str += ' ' + seen.get(order[i])
|
|
437
641
|
return str
|
|
438
642
|
}
|
|
439
643
|
|
|
644
|
+
function cx() {
|
|
645
|
+
// Everything that produces a bamboo class string — \`css()\`, a recipe, a nested \`cx\` —
|
|
646
|
+
// emits one that is already conflict-free, so a lone string has nothing to merge and
|
|
647
|
+
// tokenizing it is pure cost. This is the hot path: \`cx(staticClasses, props.className)\`
|
|
648
|
+
// with no \`className\` passed.
|
|
649
|
+
if (arguments.length === 1) {
|
|
650
|
+
const only = arguments[0]
|
|
651
|
+
if (typeof only === 'string') return only
|
|
652
|
+
if (!only) return ''
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const flat = []
|
|
656
|
+
flattenParts(arguments, flat)
|
|
657
|
+
if (flat.length === 0) return ''
|
|
658
|
+
if (flat.length === 1) return flat[0]
|
|
659
|
+
return mergeClassStrings(flat)
|
|
660
|
+
}
|
|
661
|
+
|
|
440
662
|
export { cx }
|
|
441
663
|
`,
|
|
442
|
-
dts
|
|
443
|
-
type Argument = string | boolean | null | undefined
|
|
444
|
-
|
|
445
|
-
/** Conditionally join classNames into a single string */
|
|
446
|
-
export declare function cx(...args: Argument[]): string
|
|
447
|
-
`
|
|
664
|
+
dts
|
|
448
665
|
};
|
|
449
666
|
}
|
|
450
667
|
//#endregion
|
|
@@ -452,7 +669,7 @@ function generateCx() {
|
|
|
452
669
|
var content$11 = "//#region src/astish.ts\nconst newRule = /(?:([\\u0080-\\uFFFF\\w-%@]+) *:? *([^{;]+?);|([^;}{]*?) *{)|(}\\s*)/g;\nconst ruleClean = /\\/\\*[^]*?\\*\\/| +/g;\nconst ruleNewline = /\\n+/g;\nconst empty = \" \";\nconst astish = (val, tree = [{}]) => {\n if (!val) return tree[0];\n let block, left;\n while (block = newRule.exec(val.replace(ruleClean, \"\"))) if (block[4]) tree.shift();\n else if (block[3]) {\n left = block[3].replace(ruleNewline, empty).trim();\n if (!left.includes(\"&\") && !left.startsWith(\"@\")) left = \"& \" + left;\n tree.unshift(tree[0][left] = tree[0][left] || {});\n } else tree[0][block[1]] = block[2].replace(ruleNewline, empty).trim();\n return tree[0];\n};\n//#endregion\nexport { astish };\n";
|
|
453
670
|
//#endregion
|
|
454
671
|
//#region src/artifacts/generated/helpers.mjs.json
|
|
455
|
-
var content$10 = "//#region src/assert.ts\nfunction isObject(value) {\n return typeof value === \"object\" && value != null && !Array.isArray(value);\n}\nconst isObjectOrArray = (obj) => typeof obj === \"object\" && obj !== null;\n//#endregion\n//#region src/condition.ts\nconst isBaseCondition = (v) => v === \"base\";\nfunction filterBaseConditions(c) {\n return c.slice().filter((v) => !isBaseCondition(v));\n}\n//#endregion\n//#region src/hash.ts\nfunction toChar(code) {\n return String.fromCharCode(code + (code > 25 ? 39 : 97));\n}\nfunction toName(code) {\n let name = \"\";\n let x;\n for (x = Math.abs(code); x > 52; x = x / 52 | 0) name = toChar(x % 52) + name;\n return toChar(x % 52) + name;\n}\nfunction toPhash(h, x) {\n let i = x.length;\n while (i) h = h * 33 ^ x.charCodeAt(--i);\n return h;\n}\nfunction toHash(value) {\n return toName(toPhash(5381, value) >>> 0);\n}\n//#endregion\n//#region src/important.ts\nconst importantRegex = /\\s*!(important)?/i;\nconst whitespaceRegex = /\\s/;\n/**\n* Collapse every run of whitespace to a single space, which is what the class name is\n* built from. Exported because `leafClass` has to reproduce this exact pipeline, and a\n* second copy of it would be free to drift from the one `createCss` runs.\n*/\nfunction sanitize(value) {\n if (typeof value !== \"string\") return value;\n return whitespaceRegex.test(value) ? value.replaceAll(/[\\n\\s]+/g, \" \") : value;\n}\nfunction isImportant(value) {\n if (typeof value !== \"string\") return false;\n return value.includes(\"!\") && importantRegex.test(value);\n}\nfunction withoutImportant(value) {\n if (typeof value !== \"string\") return value;\n if (!value.includes(\"!\")) return value.trim();\n return value.replace(importantRegex, \"\").trim();\n}\nfunction withoutSpace(str) {\n if (typeof str !== \"string\") return str;\n return str.includes(\" \") ? str.replaceAll(\" \", \"_\") : str;\n}\n//#endregion\n//#region src/memo.ts\n/**\n* Bounded argument memo used by the generated runtime (`css`, patterns, `cva`, recipes).\n*\n* Two regimes, picked per call:\n*\n* - Arguments that are flat (objects of primitives) take a cheap structural hash\n* and are confirmed with an exact comparison, so a hash collision can never\n* serve the wrong result. This is the shape `css({ ... })` has.\n* - Anything nested falls back to `JSON.stringify`, which V8 does faster than a\n* JS walk.\n*\n* The second point is the counter-intuitive one, and it has been measured rather\n* than assumed. Extending the structural hash to recurse — so nested styles could\n* take the fast path too — is *slower*, because it trades one native serialization\n* for two JS walks (hash, then the deep equality that confirms it). Over 10k\n* iterations per shape:\n*\n* shape stringify recursive hash + deep equal\n* flat 1.06ms 2.09ms\n* _hover 1.00ms 2.16ms\n* responsive 1.23ms 2.15ms\n* realistic 2.32ms 5.84ms\n* nested 3 deep 1.22ms 2.35ms\n*\n* So a nested `css()` call costing several times a flat one is not a defect here.\n* It is the floor for a value-keyed memo in JS, and the way to avoid it is to not\n* make the call — see the build-time fold in `@bamboocss/vite`.\n*\n* Both regimes key on *values*, never on object identity: mutating a style object\n* between calls changes its hash, so the next call misses and recomputes rather\n* than serving a stale class. Keying nested arguments on the identity of the inner\n* objects would skip serialization entirely, but it cannot detect a mutation, and\n* \"same object, different contents\" is exactly what a style object built per render\n* looks like.\n*\n* Both caches are bounded. An unbounded memo is a leak in any long-lived process\n* (SSR), where the set of distinct style objects grows without limit.\n*/\n/**\n* Distinct hashes held per memoized function before the cache rotates.\n*\n* This bounds *buckets*, not entries: a bucket keeps up to `MAX_BUCKET` colliding\n* argument lists, so the ceiling is `MAX_ENTRIES * MAX_BUCKET` live entries, and\n* twice that across both generations, since the previous one is retained until the\n* next rotation. Collisions are rare in practice, so the realistic figure is close\n* to `MAX_ENTRIES` — but the worst case is what matters when sizing a long-lived\n* process, so state it plainly.\n*\n* Rotation beats evicting the oldest key: single-key eviction is worst-case for a\n* working set that cycles, because it drops exactly the entry about to be needed.\n* Measured on a cycling set of 20k styles, one-at-a-time eviction cost ~719ns/op\n* against ~189ns unbounded, while rotation holds ~274ns. On realistic skewed\n* access rotation is at or below the unbounded cost.\n*/\nconst MAX_ENTRIES = 1e3;\n/** Entries kept per hash bucket, to bound the cost of a collision scan. */\nconst MAX_BUCKET = 8;\n/**\n* DJB2 over the arguments' own keys and primitive values.\n* Returns `null` for anything nested, which routes the call to the string key.\n*/\nconst flatHashOrNull = (args) => {\n let h = 5381;\n for (let a = 0; a < args.length; a++) {\n const obj = args[a];\n if (obj === null || typeof obj !== \"object\") {\n const t = typeof obj;\n if (t === \"string\") for (let i = 0; i < obj.length; i++) h = h * 33 ^ obj.charCodeAt(i);\n else if (t === \"number\") h = h * 33 ^ (obj | 0);\n else if (t === \"boolean\") h = h * 33 ^ (obj ? 991 : 997);\n else h = h * 33 ^ 3;\n continue;\n }\n if (Array.isArray(obj)) h = h * 33 ^ 7;\n else {\n const proto = Object.getPrototypeOf(obj);\n if (proto !== Object.prototype && proto !== null) return null;\n }\n for (const k in obj) {\n const v = obj[k];\n const tv = typeof v;\n if (v !== null && tv === \"object\") return null;\n for (let i = 0; i < k.length; i++) h = h * 33 ^ k.charCodeAt(i);\n if (tv === \"string\") for (let i = 0; i < v.length; i++) h = h * 33 ^ v.charCodeAt(i);\n else if (tv === \"number\") h = h * 33 ^ (v | 0);\n else if (tv === \"boolean\") h = h * 33 ^ (v ? 991 : 997);\n else h = h * 33 ^ 2;\n }\n }\n return h >>> 0;\n};\n/**\n* Value snapshot of the arguments, taken once at insert.\n*\n* The cache must not hold the caller's objects: a style object can capture a much\n* larger graph, and keeping it alive until the cache rotates changes GC behaviour\n* for code that never asked to be cached. Only the flat path reaches here, so a\n* shallow copy contains primitives only and retains nothing.\n*\n* Comparing against a copy also removes the last way a mutation could be missed.\n* Were the caller's own object stored, `oa === ob` would short-circuit the value\n* comparison, and a mutation that happened to preserve the hash would return the\n* stale entry. Against a copy that check can only ever be true for equal\n* primitives.\n*/\nconst snapshotArgs = (args) => {\n const values = [];\n const counts = [];\n for (let i = 0; i < args.length; i++) {\n const o = args[i];\n if (o !== null && typeof o === \"object\") {\n const copy = Array.isArray(o) ? [] : {};\n let n = 0;\n for (const k in o) {\n copy[k] = o[k];\n n++;\n }\n values.push(copy);\n counts.push(n);\n } else {\n values.push(o);\n counts.push(0);\n }\n }\n return {\n values,\n counts\n };\n};\n/**\n* Exact match, so a `flatHashOrNull` collision is resolved rather than trusted.\n* `bCounts` is the cached side's key count; comparing against it avoids the\n* `Object.keys()` allocation this would otherwise make on every cache hit.\n*/\nconst flatArgsEqual = (a, b, bCounts) => {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n const oa = a[i];\n const ob = b[i];\n if (oa === ob) continue;\n if (oa === null || ob === null || typeof oa !== \"object\" || typeof ob !== \"object\") return false;\n if (Array.isArray(oa) !== Array.isArray(ob)) return false;\n let n = 0;\n for (const k in oa) {\n if (oa[k] !== ob[k]) return false;\n n++;\n }\n if (n !== bCounts[i]) return false;\n }\n return true;\n};\nconst memo = (fn) => {\n let buckets = /* @__PURE__ */ new Map();\n let priorBuckets = /* @__PURE__ */ new Map();\n let strings = /* @__PURE__ */ new Map();\n let priorStrings = /* @__PURE__ */ new Map();\n /**\n * One scalar argument, keyed directly.\n *\n * This is the shape of the hottest callers — `isCssProperty(prop)` runs per prop\n * per render — and a plain map lookup beats hashing, bucket scanning and\n * snapshotting for it. Distinct types stay distinct keys, so `1` and `'1'` do not\n * share an entry.\n */\n let scalars = /* @__PURE__ */ new Map();\n let priorScalars = /* @__PURE__ */ new Map();\n const scan = (bucket, args) => {\n if (bucket) for (let i = 0; i < bucket.length; i++) {\n const entry = bucket[i];\n if (flatArgsEqual(args, entry.values, entry.counts)) return entry;\n }\n };\n const get = (...args) => {\n if (args.length === 1) {\n const only = args[0];\n if (only === null || typeof only !== \"object\") {\n if (scalars.has(only)) return scalars.get(only);\n if (priorScalars.has(only)) {\n const promoted = priorScalars.get(only);\n scalars.set(only, promoted);\n return promoted;\n }\n const out = fn(only);\n scalars.set(only, out);\n if (scalars.size > MAX_ENTRIES) {\n priorScalars = scalars;\n scalars = /* @__PURE__ */ new Map();\n }\n return out;\n }\n }\n const hash = flatHashOrNull(args);\n if (hash !== null) {\n let bucket = buckets.get(hash);\n const hit = scan(bucket, args);\n if (hit) return hit.out;\n const priorHit = scan(priorBuckets.get(hash), args);\n if (priorHit) {\n if (!bucket) {\n bucket = [];\n buckets.set(hash, bucket);\n }\n bucket.push(priorHit);\n if (bucket.length > MAX_BUCKET) bucket.shift();\n return priorHit.out;\n }\n const snap = snapshotArgs(args);\n const out = fn(...args);\n if (!bucket) {\n bucket = [];\n buckets.set(hash, bucket);\n }\n bucket.push({\n values: snap.values,\n counts: snap.counts,\n out\n });\n if (bucket.length > MAX_BUCKET) bucket.shift();\n if (buckets.size > MAX_ENTRIES) {\n priorBuckets = buckets;\n buckets = /* @__PURE__ */ new Map();\n }\n return out;\n }\n const key = JSON.stringify(args);\n if (strings.has(key)) return strings.get(key);\n if (priorStrings.has(key)) {\n const promoted = priorStrings.get(key);\n strings.set(key, promoted);\n return promoted;\n }\n const out = fn(...args);\n strings.set(key, out);\n if (strings.size > MAX_ENTRIES) {\n priorStrings = strings;\n strings = /* @__PURE__ */ new Map();\n }\n return out;\n };\n return get;\n};\n//#endregion\n//#region src/merge-props.ts\nconst MERGE_OMIT = new Set([\n \"__proto__\",\n \"constructor\",\n \"prototype\"\n]);\nfunction mergeProps(...sources) {\n return sources.reduce((prev, obj) => {\n if (!obj) return prev;\n Object.keys(obj).forEach((key) => {\n if (MERGE_OMIT.has(key)) return;\n const prevValue = prev[key];\n const value = obj[key];\n if (isObject(prevValue) && isObject(value)) prev[key] = mergeProps(prevValue, value);\n else prev[key] = value;\n });\n return prev;\n }, {});\n}\n//#endregion\n//#region src/walk-object.ts\nconst isNotNullish = (element) => element != null;\nfunction walkObject(target, predicate, options = {}) {\n const { stop, getKey } = options;\n function inner(value, path = []) {\n if (isObjectOrArray(value)) {\n const result = {};\n for (const [prop, child] of Object.entries(value)) {\n const key = getKey?.(prop, child) ?? prop;\n const childPath = [...path, key];\n if (stop?.(value, childPath)) return predicate(value, path);\n const next = inner(child, childPath);\n if (isNotNullish(next)) result[key] = next;\n }\n return result;\n }\n return predicate(value, path);\n }\n return inner(target);\n}\nfunction mapObject(obj, fn) {\n if (Array.isArray(obj)) return obj.map((value) => fn(value));\n if (!isObject(obj)) return fn(obj);\n return walkObject(obj, (value) => fn(value));\n}\n//#endregion\n//#region src/normalize-style-object.ts\nfunction toResponsiveObject(values, breakpoints) {\n return values.reduce((acc, current, index) => {\n const key = breakpoints[index];\n if (current != null) acc[key] = current;\n return acc;\n }, {});\n}\n/**\n* Whether walking the object would only rebuild it.\n*\n* Normalizing does three things: it renames a shorthand to its longhand, expands a responsive\n* array into a breakpoint object, and drops nullish leaves. A flat object of plain values\n* written in longhand needs none of them, and that is most of what `css()` is handed — but it\n* still paid for a full rebuild plus a path array per key.\n*\n* Every clause has to be exact, since a false positive returns an object the walk would have\n* changed. Nullish is one of them: a leaf the walk removes must not survive, or a later merge\n* would see it override the value beneath it. The array check is another, and it is on the\n* container as well as the values — `stop` is handed the container, so an array arriving at\n* the top level becomes a breakpoint object rather than being walked into.\n*\n* `for...in` reads inherited keys the walk ignores, which is safe in the only direction it can\n* be wrong — an extra key can send this to the slow path, never past it.\n*\n* It does read every value, as `compactStyles` and the argument memo already do, so an\n* accessor prop is read once more than before. Style props are values by the time they get\n* here and reading one has no effect, but it is the reason this cannot be reordered to read\n* lazily.\n*/\nfunction needsNoNormalizing(styles, resolveShorthand) {\n if (Array.isArray(styles)) return false;\n for (const key in styles) {\n const value = styles[key];\n if (value == null || typeof value === \"object\") return false;\n if (resolveShorthand !== void 0 && resolveShorthand(key) !== key) return false;\n }\n return true;\n}\n/**\n* The result may be the argument itself rather than a fresh object, so callers have to treat\n* it as read-only. Every one of them does today: merging accumulates into its own object and\n* the two `raw()` helpers clone at the boundary.\n*/\nfunction normalizeStyleObject(styles, context, shorthand = true) {\n const { utility, conditions } = context;\n const { hasShorthand, resolveShorthand } = utility;\n if (needsNoNormalizing(styles, shorthand && hasShorthand ? resolveShorthand : void 0)) return styles;\n return walkObject(styles, (value) => {\n return Array.isArray(value) ? toResponsiveObject(value, conditions.breakpoints.keys) : value;\n }, {\n stop: (value) => Array.isArray(value),\n getKey: shorthand ? (prop) => hasShorthand ? resolveShorthand(prop) : prop : void 0\n });\n}\n//#endregion\n//#region src/classname.ts\nconst fallbackCondition = {\n shift: (v) => v,\n finalize: (v) => v,\n breakpoints: { keys: [] }\n};\nconst ENTRY_SEP = \"]___[\";\nconst COND_SEP = \"<___>\";\nfunction createCss(context) {\n const { utility, hash, grouped, conditions: conds = fallbackCondition } = context;\n const { prefix } = utility;\n const formatClassName = prefix ? (str) => str ? `${prefix}-${str}` : prefix : (str) => str || \"\";\n const hashFn = (conditions, className) => {\n if (hash) {\n const baseArray = [...conds.finalize(conditions), className];\n return formatClassName(utility.toHash(baseArray, toHash));\n }\n const finalized = conds.finalize(conditions);\n if (finalized.length === 0) return formatClassName(className);\n return [...finalized, formatClassName(className)].join(\":\");\n };\n if (grouped) return memo(({ base, ...styles } = {}) => {\n const normalizedObject = normalizeStyleObject(Object.assign(styles, base), context);\n const hashes = [];\n walkObject(normalizedObject, (value, paths) => {\n if (value == null) return;\n const [prop, ...allConditions] = conds.shift(paths);\n const conditions = filterBaseConditions(allConditions);\n const parts = [`${prop}${ENTRY_SEP}value:${value}`];\n if (conditions.length) parts.push(`cond:${conditions.join(COND_SEP)}`);\n hashes.push(parts.join(ENTRY_SEP));\n });\n if (hashes.length === 0) return \"\";\n hashes.sort();\n const groupId = hashes.join(\"|\");\n return formatClassName(utility.toHash([\"grouped\", groupId], toHash));\n });\n return memo(({ base, ...styles } = {}) => {\n const normalizedObject = normalizeStyleObject(Object.assign(styles, base), context);\n const classNames = /* @__PURE__ */ new Set();\n walkObject(normalizedObject, (value, paths) => {\n if (value == null) return;\n const important = isImportant(value);\n const [prop, ...allConditions] = conds.shift(paths);\n let className = hashFn(filterBaseConditions(allConditions), utility.transform(prop, withoutImportant(sanitize(value))).className);\n if (important) className = `${className}!`;\n classNames.add(className);\n });\n return Array.from(classNames).join(\" \");\n });\n}\n/**\n* Whether a style object carries anything `compact` would have kept.\n*\n* The question `compactStyles` asks is only ever \"is this empty once undefined values are\n* dropped\", but it used to answer it by building the compacted object and then a key array\n* for it, then throwing both away. `Object.keys` enumerates exactly what `compact`'s\n* `Object.entries` did — own, enumerable, string-keyed — so this is the same predicate\n* without the two allocations, and it stops at the first value that settles it.\n*/\nfunction hasDefinedValue(style) {\n const keys = Object.keys(style);\n for (let i = 0; i < keys.length; i++) if (style[keys[i]] !== void 0) return true;\n return false;\n}\nfunction compactStyles(...styles) {\n return styles.flat().filter((style) => isObject(style) && hasDefinedValue(style));\n}\nfunction createMergeCss(context) {\n function resolve(styles) {\n const allStyles = compactStyles(...styles);\n if (allStyles.length === 1) return allStyles;\n return allStyles.map((style) => normalizeStyleObject(style, context));\n }\n function mergeCss(...styles) {\n return mergeProps(...resolve(styles));\n }\n function assignCss(...styles) {\n return Object.assign({}, ...resolve(styles));\n }\n return {\n mergeCss: memo(mergeCss),\n assignCss\n };\n}\n//#endregion\n//#region src/clone-styles.ts\nconst OMIT = new Set([\n \"__proto__\",\n \"constructor\",\n \"prototype\"\n]);\n/**\n* Independent copy of a style object, nested condition blocks included.\n*\n* Merged style objects are cached, so anything handed to user code has to be\n* copied first: a caller mutating what it received would otherwise change what\n* every later caller reads back. `css.raw()` and `cva.raw()` are those boundaries.\n*\n* Kept separate from `mergeProps` deliberately. Merging is on the hot path — it\n* runs on every `css()` cache miss and on every render of a pattern component\n* under `jsxStyleProps: 'minimal'` — and copying there charges every caller for a\n* guarantee only the two `raw()` helpers need. Measured on a realistic style\n* object (5 base properties, 4 condition blocks) that was roughly twice the cost\n* of merging alone.\n*/\nfunction cloneStyles(styles) {\n if (Array.isArray(styles)) return styles.map((value) => cloneStyles(value));\n if (!isObject(styles)) return styles;\n const out = {};\n for (const key of Object.keys(styles)) {\n if (OMIT.has(key)) continue;\n out[key] = cloneStyles(styles[key]);\n }\n return out;\n}\n//#endregion\n//#region src/compact.ts\nfunction compact(value) {\n return Object.fromEntries(Object.entries(value ?? {}).filter(([_, value]) => value !== void 0));\n}\n//#endregion\n//#region src/leaf-class.ts\n/**\n* The class a single dynamic style leaf resolves to, given the prefix its property and\n* condition path produce.\n*\n* ## Why this can exist at all\n*\n* `css()` builds a class from the value alone — `utility.transform` is string\n* construction over a static map, and nothing consults which rules were actually emitted.\n* So `css({ color: tone })` already returns `c_<tone>` for a value the extractor never\n* saw, with no CSS behind it. Reproducing that string here cannot be less correct than\n* the call it replaces; it just skips the object literal, the merge and the memo.\n*\n* ## Why it is not a template literal\n*\n* Three shapes do not reduce to `prefix + value`, and all three return `undefined` so the\n* caller runs `css()` instead:\n*\n* - An array is expanded to a responsive object by `normalizeStyleObject`, so it produces\n* one class per breakpoint rather than one class.\n* - An object is a condition block, walked into for the same reason.\n* - `null` and `undefined` are skipped by the walk entirely, which is an empty string\n* rather than a class — that one is answered here, since it needs no `css()` call.\n*\n* ## Why the character scan\n*\n* The remaining work — collapsing whitespace, stripping `!important`, turning spaces into\n* underscores — is three regexes, and paying them per call makes this *slower* than a\n* memo hit. Almost no token value contains whitespace or `!`, so one scan for the\n* characters that make any of it necessary sends the common value straight to a\n* concatenation. A false positive only costs the slow path, so the scan errs wide.\n*/\nfunction leafClass(prefix, value) {\n if (value == null) return \"\";\n const type = typeof value;\n if (type === \"number\" || type === \"boolean\") return `${prefix}${value}`;\n if (type !== \"string\") return void 0;\n const str = value;\n for (let index = 0; index < str.length; index++) {\n const code = str.charCodeAt(index);\n if (code <= 33 || code === 160 || code === 5760 || code >= 8192) return slowLeaf(prefix, str);\n }\n return `${prefix}${str}`;\n}\n/** The full pipeline `createCss` runs, for a value that needs it. */\nfunction slowLeaf(prefix, value) {\n const important = isImportant(value);\n const className = `${prefix}${withoutSpace(withoutImportant(sanitize(value)))}`;\n return important ? `${className}!` : className;\n}\n//#endregion\n//#region src/hypenate-property.ts\nconst wordRegex = /([A-Z])/g;\nconst msRegex = /^ms-/;\nconst hypenateProperty = memo((property) => {\n if (property.startsWith(\"--\")) return property;\n return property.replace(wordRegex, \"-$1\").replace(msRegex, \"-ms-\").toLowerCase();\n});\n//#endregion\n//#region src/is-css-function.ts\nconst fnRegExp = new RegExp(`^(${[\n \"min\",\n \"max\",\n \"clamp\",\n \"calc\"\n].join(\"|\")})\\\\(.*\\\\)`);\nconst isCssFunction = (v) => typeof v === \"string\" && fnRegExp.test(v);\n//#endregion\n//#region src/is-css-unit.ts\nconst lengthUnitsPattern = `(?:${\"cm,mm,Q,in,pc,pt,px,em,ex,ch,rem,lh,rlh,vw,vh,vmin,vmax,vb,vi,svw,svh,lvw,lvh,dvw,dvh,cqw,cqh,cqi,cqb,cqmin,cqmax,%\".split(\",\").join(\"|\")})`;\nconst lengthRegExp = new RegExp(`^[+-]?[0-9]*.?[0-9]+(?:[eE][+-]?[0-9]+)?${lengthUnitsPattern}$`);\nconst isCssUnit = (v) => typeof v === \"string\" && lengthRegExp.test(v);\n//#endregion\n//#region src/is-css-var.ts\nconst isCssVar = (v) => typeof v === \"string\" && /^var\\(--.+\\)$/.test(v);\n//#endregion\n//#region src/pattern-fns.ts\nconst patternFns = {\n map: mapObject,\n isCssFunction,\n isCssVar,\n isCssUnit\n};\nconst getPatternStyles = (pattern, styles) => {\n if (!pattern?.defaultValues) return styles;\n const defaults = typeof pattern.defaultValues === \"function\" ? pattern.defaultValues(styles) : pattern.defaultValues;\n return Object.assign({}, defaults, compact(styles));\n};\n//#endregion\n//#region src/slot.ts\nconst getSlotRecipes = (recipe = {}) => {\n const init = (slot) => ({\n className: [recipe.className, slot].filter(Boolean).join(\"__\"),\n base: recipe.base?.[slot] ?? {},\n variants: {},\n defaultVariants: recipe.defaultVariants ?? {},\n compoundVariants: recipe.compoundVariants ? getSlotCompoundVariant(recipe.compoundVariants, slot) : []\n });\n const recipeParts = (recipe.slots ?? []).map((slot) => [slot, init(slot)]);\n for (const [variantsKey, variantsSpec] of Object.entries(recipe.variants ?? {})) for (const [variantKey, variantSpec] of Object.entries(variantsSpec)) recipeParts.forEach(([slot, slotRecipe]) => {\n slotRecipe.variants[variantsKey] ??= {};\n slotRecipe.variants[variantsKey][variantKey] = variantSpec[slot] ?? {};\n });\n return Object.fromEntries(recipeParts);\n};\nconst getSlotCompoundVariant = (compoundVariants, slotName) => compoundVariants.filter((compoundVariant) => compoundVariant.css[slotName]).map((compoundVariant) => ({\n ...compoundVariant,\n css: compoundVariant.css[slotName]\n}));\n//#endregion\n//#region src/split-props.ts\n/**\n* Deal a props object into one bucket per key group, plus a final bucket for the rest.\n* A key goes to the first group that claims it.\n*\n* ## Why the descriptor is read per key rather than in bulk\n*\n* This used to call `Object.getOwnPropertyDescriptors` for the whole object and\n* `defineProperty` for every key it moved. Copying plain values instead is 2.4–2.9x faster\n* on the shapes that allow it, but it is only correct where props are data — and they are\n* not always. Solid compiles props to accessors, so reading one eagerly runs whatever it\n* wraps: splitting a component's props would construct its children before the surrounding\n* provider exists.\n*\n* So the descriptor is fetched per key, and the value path is taken only when it changes\n* nothing observable. An accessor keeps its laziness, a non-enumerable key keeps its\n* invisibility, and `__proto__` is defined rather than assigned so it stays an own\n* property instead of reaching the prototype setter.\n*\n* The one thing the value path drops is `writable`/`configurable`, so a bucket key taken\n* from frozen props is writable where it used to be frozen. Nothing here relies on that,\n* and preserving it would mean `defineProperty` on the common path — the cost this exists\n* to avoid. Keys that take the descriptor path keep theirs, so a bucket can be\n* inconsistent in that one respect.\n*\n* Key order within a bucket is preserved exactly. It is not cosmetic: `cva` merges\n* variant props in iteration order, and the parser reads the rest bucket as the style\n* props it encodes, so order reaches the emitted CSS.\n*/\nfunction splitProps(props, ...keys) {\n const allKeys = Object.getOwnPropertyNames(props);\n const own = new Set(allKeys);\n const taken = /* @__PURE__ */ new Set();\n const split = (group) => {\n const clone = {};\n for (let i = 0; i < group.length; i++) {\n const key = group[i];\n if (taken.has(key) || !own.has(key)) continue;\n const descriptor = Object.getOwnPropertyDescriptor(props, key);\n if (!descriptor) continue;\n if (\"get\" in descriptor || \"set\" in descriptor || !descriptor.enumerable || key === \"__proto__\") Object.defineProperty(clone, key, descriptor);\n else clone[key] = descriptor.value;\n taken.add(key);\n }\n return clone;\n };\n /**\n * The predicate is called with the key alone.\n *\n * Handing it to `filter` passes `(key, index, allKeys)`. A one-parameter predicate cannot\n * see the extras, but a memoized one reads its whole argument list — and the predicates\n * that arrive here are memoized, `isCssProperty` among them. So the memo hashed the entire\n * key array once per prop, and keyed its cache on it: two elements with different prop sets\n * shared no entry even for the same prop name.\n *\n * Worth ~9.7x on that path, and nothing at all on a plain predicate — which is why the\n * bench below it needs a memoized case to see this at all.\n *\n * A loop rather than `filter((k) => key(k))` because the wrapper allocates a closure per\n * group. The two measure the same to within noise; the loop just does not need one.\n */\n const matching = (predicate) => {\n const group = [];\n for (let i = 0; i < allKeys.length; i++) {\n const key = allKeys[i];\n if (predicate(key)) group.push(key);\n }\n return group;\n };\n return keys.map((key) => split(Array.isArray(key) ? key : matching(key))).concat(split(allKeys));\n}\n//#endregion\n//#region src/uniq.ts\nconst uniq = (...items) => {\n const set = items.reduce((acc, currItems) => {\n if (currItems) currItems.forEach((item) => acc.add(item));\n return acc;\n }, /* @__PURE__ */ new Set([]));\n return Array.from(set);\n};\n//#endregion\nexport { cloneStyles, compact, createCss, createMergeCss, filterBaseConditions, getPatternStyles, getSlotCompoundVariant, getSlotRecipes, hypenateProperty, isBaseCondition, isObject, leafClass, mapObject, memo, mergeProps, patternFns, splitProps, toHash, uniq, walkObject, withoutSpace };\n";
|
|
672
|
+
var content$10 = "//#region src/assert.ts\nfunction isObject(value) {\n return typeof value === \"object\" && value != null && !Array.isArray(value);\n}\nconst isObjectOrArray = (obj) => typeof obj === \"object\" && obj !== null;\n//#endregion\n//#region src/condition.ts\nconst isBaseCondition = (v) => v === \"base\";\nfunction filterBaseConditions(c) {\n return c.slice().filter((v) => !isBaseCondition(v));\n}\n//#endregion\n//#region src/hash.ts\nfunction toChar(code) {\n return String.fromCharCode(code + (code > 25 ? 39 : 97));\n}\nfunction toName(code) {\n let name = \"\";\n let x;\n for (x = Math.abs(code); x > 52; x = x / 52 | 0) name = toChar(x % 52) + name;\n return toChar(x % 52) + name;\n}\nfunction toPhash(h, x) {\n let i = x.length;\n while (i) h = h * 33 ^ x.charCodeAt(--i);\n return h;\n}\nfunction toHash(value) {\n return toName(toPhash(5381, value) >>> 0);\n}\n//#endregion\n//#region src/important.ts\nconst importantRegex = /\\s*!(important)?/i;\nconst whitespaceRegex = /\\s/;\n/**\n* Collapse every run of whitespace to a single space, which is what the class name is\n* built from. Exported because `leafClass` has to reproduce this exact pipeline, and a\n* second copy of it would be free to drift from the one `createCss` runs.\n*/\nfunction sanitize(value) {\n if (typeof value !== \"string\") return value;\n const collapsed = whitespaceRegex.test(value) ? value.replaceAll(/[\\n\\s]+/g, \" \") : value;\n return collapsed.includes(\"\\0\") ? collapsed.replaceAll(\"\\0\", \"\") : collapsed;\n}\nfunction isImportant(value) {\n if (typeof value !== \"string\") return false;\n return value.includes(\"!\") && importantRegex.test(value);\n}\nfunction withoutImportant(value) {\n if (typeof value !== \"string\") return value;\n if (!value.includes(\"!\")) return value.trim();\n return value.replace(importantRegex, \"\").trim();\n}\nfunction withoutSpace(str) {\n if (typeof str !== \"string\") return str;\n return str.includes(\" \") ? str.replaceAll(\" \", \"_\") : str;\n}\n//#endregion\n//#region src/memo.ts\n/**\n* Bounded argument memo used by the generated runtime (`css`, patterns, `cva`, recipes).\n*\n* Two regimes, picked per call:\n*\n* - Arguments that are flat (objects of primitives) take a cheap structural hash\n* and are confirmed with an exact comparison, so a hash collision can never\n* serve the wrong result. This is the shape `css({ ... })` has.\n* - Anything nested falls back to `JSON.stringify`, which V8 does faster than a\n* JS walk.\n*\n* The second point is the counter-intuitive one, and it has been measured rather\n* than assumed. Extending the structural hash to recurse — so nested styles could\n* take the fast path too — is *slower*, because it trades one native serialization\n* for two JS walks (hash, then the deep equality that confirms it). Over 10k\n* iterations per shape:\n*\n* shape stringify recursive hash + deep equal\n* flat 1.06ms 2.09ms\n* _hover 1.00ms 2.16ms\n* responsive 1.23ms 2.15ms\n* realistic 2.32ms 5.84ms\n* nested 3 deep 1.22ms 2.35ms\n*\n* So a nested `css()` call costing several times a flat one is not a defect here.\n* It is the floor for a value-keyed memo in JS, and the way to avoid it is to not\n* make the call — see the build-time fold in `@bamboocss/vite`.\n*\n* Both regimes key on *values*, never on object identity: mutating a style object\n* between calls changes its hash, so the next call misses and recomputes rather\n* than serving a stale class. Keying nested arguments on the identity of the inner\n* objects would skip serialization entirely, but it cannot detect a mutation, and\n* \"same object, different contents\" is exactly what a style object built per render\n* looks like.\n*\n* Both caches are bounded. An unbounded memo is a leak in any long-lived process\n* (SSR), where the set of distinct style objects grows without limit.\n*/\n/**\n* Distinct hashes held per memoized function before the cache rotates.\n*\n* This bounds *buckets*, not entries: a bucket keeps up to `MAX_BUCKET` colliding\n* argument lists, so the ceiling is `MAX_ENTRIES * MAX_BUCKET` live entries, and\n* twice that across both generations, since the previous one is retained until the\n* next rotation. Collisions are rare in practice, so the realistic figure is close\n* to `MAX_ENTRIES` — but the worst case is what matters when sizing a long-lived\n* process, so state it plainly.\n*\n* Rotation beats evicting the oldest key: single-key eviction is worst-case for a\n* working set that cycles, because it drops exactly the entry about to be needed.\n* Measured on a cycling set of 20k styles, one-at-a-time eviction cost ~719ns/op\n* against ~189ns unbounded, while rotation holds ~274ns. On realistic skewed\n* access rotation is at or below the unbounded cost.\n*/\nconst MAX_ENTRIES = 1e3;\n/** Entries kept per hash bucket, to bound the cost of a collision scan. */\nconst MAX_BUCKET = 8;\n/**\n* DJB2 over the arguments' own keys and primitive values.\n* Returns `null` for anything nested, which routes the call to the string key.\n*/\nconst flatHashOrNull = (args) => {\n let h = 5381;\n for (let a = 0; a < args.length; a++) {\n const obj = args[a];\n if (obj === null || typeof obj !== \"object\") {\n const t = typeof obj;\n if (t === \"string\") for (let i = 0; i < obj.length; i++) h = h * 33 ^ obj.charCodeAt(i);\n else if (t === \"number\") h = h * 33 ^ (obj | 0);\n else if (t === \"boolean\") h = h * 33 ^ (obj ? 991 : 997);\n else h = h * 33 ^ 3;\n continue;\n }\n if (Array.isArray(obj)) h = h * 33 ^ 7;\n else {\n const proto = Object.getPrototypeOf(obj);\n if (proto !== Object.prototype && proto !== null) return null;\n }\n for (const k in obj) {\n const v = obj[k];\n const tv = typeof v;\n if (v !== null && tv === \"object\") return null;\n for (let i = 0; i < k.length; i++) h = h * 33 ^ k.charCodeAt(i);\n if (tv === \"string\") for (let i = 0; i < v.length; i++) h = h * 33 ^ v.charCodeAt(i);\n else if (tv === \"number\") h = h * 33 ^ (v | 0);\n else if (tv === \"boolean\") h = h * 33 ^ (v ? 991 : 997);\n else h = h * 33 ^ 2;\n }\n }\n return h >>> 0;\n};\n/**\n* Value snapshot of the arguments, taken once at insert.\n*\n* The cache must not hold the caller's objects: a style object can capture a much\n* larger graph, and keeping it alive until the cache rotates changes GC behaviour\n* for code that never asked to be cached. Only the flat path reaches here, so a\n* shallow copy contains primitives only and retains nothing.\n*\n* Comparing against a copy also removes the last way a mutation could be missed.\n* Were the caller's own object stored, `oa === ob` would short-circuit the value\n* comparison, and a mutation that happened to preserve the hash would return the\n* stale entry. Against a copy that check can only ever be true for equal\n* primitives.\n*/\nconst snapshotArgs = (args) => {\n const values = [];\n const counts = [];\n for (let i = 0; i < args.length; i++) {\n const o = args[i];\n if (o !== null && typeof o === \"object\") {\n const copy = Array.isArray(o) ? [] : {};\n let n = 0;\n for (const k in o) {\n copy[k] = o[k];\n n++;\n }\n values.push(copy);\n counts.push(n);\n } else {\n values.push(o);\n counts.push(0);\n }\n }\n return {\n values,\n counts\n };\n};\n/**\n* Exact match, so a `flatHashOrNull` collision is resolved rather than trusted.\n* `bCounts` is the cached side's key count; comparing against it avoids the\n* `Object.keys()` allocation this would otherwise make on every cache hit.\n*/\nconst flatArgsEqual = (a, b, bCounts) => {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n const oa = a[i];\n const ob = b[i];\n if (oa === ob) continue;\n if (oa === null || ob === null || typeof oa !== \"object\" || typeof ob !== \"object\") return false;\n if (Array.isArray(oa) !== Array.isArray(ob)) return false;\n let n = 0;\n for (const k in oa) {\n if (oa[k] !== ob[k]) return false;\n n++;\n }\n if (n !== bCounts[i]) return false;\n }\n return true;\n};\nconst memo = (fn) => {\n let buckets = /* @__PURE__ */ new Map();\n let priorBuckets = /* @__PURE__ */ new Map();\n let strings = /* @__PURE__ */ new Map();\n let priorStrings = /* @__PURE__ */ new Map();\n /**\n * One scalar argument, keyed directly.\n *\n * This is the shape of the hottest callers — `isCssProperty(prop)` runs per prop\n * per render — and a plain map lookup beats hashing, bucket scanning and\n * snapshotting for it. Distinct types stay distinct keys, so `1` and `'1'` do not\n * share an entry.\n */\n let scalars = /* @__PURE__ */ new Map();\n let priorScalars = /* @__PURE__ */ new Map();\n const scan = (bucket, args) => {\n if (bucket) for (let i = 0; i < bucket.length; i++) {\n const entry = bucket[i];\n if (flatArgsEqual(args, entry.values, entry.counts)) return entry;\n }\n };\n const get = (...args) => {\n if (args.length === 1) {\n const only = args[0];\n if (only === null || typeof only !== \"object\") {\n if (scalars.has(only)) return scalars.get(only);\n if (priorScalars.has(only)) {\n const promoted = priorScalars.get(only);\n scalars.set(only, promoted);\n return promoted;\n }\n const out = fn(only);\n scalars.set(only, out);\n if (scalars.size > MAX_ENTRIES) {\n priorScalars = scalars;\n scalars = /* @__PURE__ */ new Map();\n }\n return out;\n }\n }\n const hash = flatHashOrNull(args);\n if (hash !== null) {\n let bucket = buckets.get(hash);\n const hit = scan(bucket, args);\n if (hit) return hit.out;\n const priorHit = scan(priorBuckets.get(hash), args);\n if (priorHit) {\n if (!bucket) {\n bucket = [];\n buckets.set(hash, bucket);\n }\n bucket.push(priorHit);\n if (bucket.length > MAX_BUCKET) bucket.shift();\n return priorHit.out;\n }\n const snap = snapshotArgs(args);\n const out = fn(...args);\n if (!bucket) {\n bucket = [];\n buckets.set(hash, bucket);\n }\n bucket.push({\n values: snap.values,\n counts: snap.counts,\n out\n });\n if (bucket.length > MAX_BUCKET) bucket.shift();\n if (buckets.size > MAX_ENTRIES) {\n priorBuckets = buckets;\n buckets = /* @__PURE__ */ new Map();\n }\n return out;\n }\n const key = JSON.stringify(args);\n if (strings.has(key)) return strings.get(key);\n if (priorStrings.has(key)) {\n const promoted = priorStrings.get(key);\n strings.set(key, promoted);\n return promoted;\n }\n const out = fn(...args);\n strings.set(key, out);\n if (strings.size > MAX_ENTRIES) {\n priorStrings = strings;\n strings = /* @__PURE__ */ new Map();\n }\n return out;\n };\n return get;\n};\n//#endregion\n//#region src/merge-props.ts\nconst MERGE_OMIT = new Set([\n \"__proto__\",\n \"constructor\",\n \"prototype\"\n]);\nfunction mergeProps(...sources) {\n return sources.reduce((prev, obj) => {\n if (!obj) return prev;\n Object.keys(obj).forEach((key) => {\n if (MERGE_OMIT.has(key)) return;\n const prevValue = prev[key];\n const value = obj[key];\n if (isObject(prevValue) && isObject(value)) prev[key] = mergeProps(prevValue, value);\n else prev[key] = value;\n });\n return prev;\n }, {});\n}\n//#endregion\n//#region src/walk-object.ts\nconst isNotNullish = (element) => element != null;\nfunction walkObject(target, predicate, options = {}) {\n const { stop, getKey } = options;\n function inner(value, path = []) {\n if (isObjectOrArray(value)) {\n const result = {};\n for (const [prop, child] of Object.entries(value)) {\n const key = getKey?.(prop, child) ?? prop;\n const childPath = [...path, key];\n if (stop?.(value, childPath)) return predicate(value, path);\n const next = inner(child, childPath);\n if (isNotNullish(next)) result[key] = next;\n }\n return result;\n }\n return predicate(value, path);\n }\n return inner(target);\n}\nfunction mapObject(obj, fn) {\n if (Array.isArray(obj)) return obj.map((value) => fn(value));\n if (!isObject(obj)) return fn(obj);\n return walkObject(obj, (value) => fn(value));\n}\n//#endregion\n//#region src/normalize-style-object.ts\nfunction toResponsiveObject(values, breakpoints) {\n return values.reduce((acc, current, index) => {\n const key = breakpoints[index];\n if (current != null) acc[key] = current;\n return acc;\n }, {});\n}\n/**\n* Whether walking the object would only rebuild it.\n*\n* Normalizing does three things: it renames a shorthand to its longhand, expands a responsive\n* array into a breakpoint object, and drops nullish leaves. A flat object of plain values\n* written in longhand needs none of them, and that is most of what `css()` is handed — but it\n* still paid for a full rebuild plus a path array per key.\n*\n* Every clause has to be exact, since a false positive returns an object the walk would have\n* changed. Nullish is one of them: a leaf the walk removes must not survive, or a later merge\n* would see it override the value beneath it. The array check is another, and it is on the\n* container as well as the values — `stop` is handed the container, so an array arriving at\n* the top level becomes a breakpoint object rather than being walked into.\n*\n* `for...in` reads inherited keys the walk ignores, which is safe in the only direction it can\n* be wrong — an extra key can send this to the slow path, never past it.\n*\n* It does read every value, as `compactStyles` and the argument memo already do, so an\n* accessor prop is read once more than before. Style props are values by the time they get\n* here and reading one has no effect, but it is the reason this cannot be reordered to read\n* lazily.\n*/\nfunction needsNoNormalizing(styles, resolveShorthand) {\n if (Array.isArray(styles)) return false;\n for (const key in styles) {\n const value = styles[key];\n if (value == null || typeof value === \"object\") return false;\n if (resolveShorthand !== void 0 && resolveShorthand(key) !== key) return false;\n }\n return true;\n}\n/**\n* The result may be the argument itself rather than a fresh object, so callers have to treat\n* it as read-only. Every one of them does today: merging accumulates into its own object and\n* the two `raw()` helpers clone at the boundary.\n*/\nfunction normalizeStyleObject(styles, context, shorthand = true) {\n const { utility, conditions } = context;\n const { hasShorthand, resolveShorthand } = utility;\n if (needsNoNormalizing(styles, shorthand && hasShorthand ? resolveShorthand : void 0)) return styles;\n return walkObject(styles, (value) => {\n return Array.isArray(value) ? toResponsiveObject(value, conditions.breakpoints.keys) : value;\n }, {\n stop: (value) => Array.isArray(value),\n getKey: shorthand ? (prop) => hasShorthand ? resolveShorthand(prop) : prop : void 0\n });\n}\n//#endregion\n//#region src/classname.ts\nconst fallbackCondition = {\n shift: (v) => v,\n finalize: (v) => v,\n breakpoints: { keys: [] }\n};\nconst ENTRY_SEP = \"]___[\";\nconst COND_SEP = \"<___>\";\nfunction createCss(context) {\n const { utility, hash, grouped, conditions: conds = fallbackCondition } = context;\n const { prefix } = utility;\n const formatClassName = prefix ? (str) => str ? `${prefix}-${str}` : prefix : (str) => str || \"\";\n const hashFn = (conditions, className) => {\n if (hash) {\n const baseArray = [...conds.finalize(conditions), className];\n return formatClassName(utility.toHash(baseArray, toHash));\n }\n const finalized = conds.finalize(conditions);\n if (finalized.length === 0) return formatClassName(className);\n return [...finalized, formatClassName(className)].join(\":\");\n };\n if (grouped) return memo(({ base, ...styles } = {}) => {\n const normalizedObject = normalizeStyleObject(Object.assign(styles, base), context);\n const hashes = [];\n walkObject(normalizedObject, (value, paths) => {\n if (value == null) return;\n const [prop, ...allConditions] = conds.shift(paths);\n const conditions = filterBaseConditions(allConditions);\n const parts = [`${prop}${ENTRY_SEP}value:${value}`];\n if (conditions.length) parts.push(`cond:${conditions.join(COND_SEP)}`);\n hashes.push(parts.join(ENTRY_SEP));\n });\n if (hashes.length === 0) return \"\";\n hashes.sort();\n const groupId = hashes.join(\"|\");\n return formatClassName(utility.toHash([\"grouped\", groupId], toHash));\n });\n return memo(({ base, ...styles } = {}) => {\n const normalizedObject = normalizeStyleObject(Object.assign(styles, base), context);\n const classNames = /* @__PURE__ */ new Set();\n walkObject(normalizedObject, (value, paths) => {\n if (value == null) return;\n const important = isImportant(value);\n const [prop, ...allConditions] = conds.shift(paths);\n let className = hashFn(filterBaseConditions(allConditions), utility.transform(prop, withoutImportant(sanitize(value))).className);\n if (important) className = `${className}!`;\n classNames.add(className);\n });\n return Array.from(classNames).join(\" \");\n });\n}\n/**\n* Whether a style object carries anything `compact` would have kept.\n*\n* The question `compactStyles` asks is only ever \"is this empty once undefined values are\n* dropped\", but it used to answer it by building the compacted object and then a key array\n* for it, then throwing both away. `Object.keys` enumerates exactly what `compact`'s\n* `Object.entries` did — own, enumerable, string-keyed — so this is the same predicate\n* without the two allocations, and it stops at the first value that settles it.\n*/\nfunction hasDefinedValue(style) {\n const keys = Object.keys(style);\n for (let i = 0; i < keys.length; i++) if (style[keys[i]] !== void 0) return true;\n return false;\n}\nfunction compactStyles(...styles) {\n return styles.flat().filter((style) => isObject(style) && hasDefinedValue(style));\n}\nfunction createMergeCss(context) {\n function resolve(styles) {\n const allStyles = compactStyles(...styles);\n if (allStyles.length === 1) return allStyles;\n return allStyles.map((style) => normalizeStyleObject(style, context));\n }\n function mergeCss(...styles) {\n return mergeProps(...resolve(styles));\n }\n function assignCss(...styles) {\n return Object.assign({}, ...resolve(styles));\n }\n return {\n mergeCss: memo(mergeCss),\n assignCss\n };\n}\n//#endregion\n//#region src/clone-styles.ts\nconst OMIT = new Set([\n \"__proto__\",\n \"constructor\",\n \"prototype\"\n]);\n/**\n* Independent copy of a style object, nested condition blocks included.\n*\n* Merged style objects are cached, so anything handed to user code has to be\n* copied first: a caller mutating what it received would otherwise change what\n* every later caller reads back. `css.raw()` and `cva.raw()` are those boundaries.\n*\n* Kept separate from `mergeProps` deliberately. Merging is on the hot path — it\n* runs on every `css()` cache miss and on every render of a pattern component\n* under `jsxStyleProps: 'minimal'` — and copying there charges every caller for a\n* guarantee only the two `raw()` helpers need. Measured on a realistic style\n* object (5 base properties, 4 condition blocks) that was roughly twice the cost\n* of merging alone.\n*/\nfunction cloneStyles(styles) {\n if (Array.isArray(styles)) return styles.map((value) => cloneStyles(value));\n if (!isObject(styles)) return styles;\n const out = {};\n for (const key of Object.keys(styles)) {\n if (OMIT.has(key)) continue;\n out[key] = cloneStyles(styles[key]);\n }\n return out;\n}\n//#endregion\n//#region src/compact.ts\nfunction compact(value) {\n return Object.fromEntries(Object.entries(value ?? {}).filter(([_, value]) => value !== void 0));\n}\n//#endregion\n//#region src/leaf-class.ts\n/**\n* The class a single dynamic style leaf resolves to, given the prefix its property and\n* condition path produce.\n*\n* ## Why this can exist at all\n*\n* `css()` builds a class from the value alone — `utility.transform` is string\n* construction over a static map, and nothing consults which rules were actually emitted.\n* So `css({ color: tone })` already returns `c_<tone>` for a value the extractor never\n* saw, with no CSS behind it. Reproducing that string here cannot be less correct than\n* the call it replaces; it just skips the object literal, the merge and the memo.\n*\n* ## Why it is not a template literal\n*\n* Three shapes do not reduce to `prefix + value`, and all three return `undefined` so the\n* caller runs `css()` instead:\n*\n* - An array is expanded to a responsive object by `normalizeStyleObject`, so it produces\n* one class per breakpoint rather than one class.\n* - An object is a condition block, walked into for the same reason.\n* - `null` and `undefined` are skipped by the walk entirely, which is an empty string\n* rather than a class — that one is answered here, since it needs no `css()` call.\n*\n* ## Why the character scan\n*\n* The remaining work — collapsing whitespace, stripping `!important`, turning spaces into\n* underscores — is three regexes, and paying them per call makes this *slower* than a\n* memo hit. Almost no token value contains whitespace or `!`, so one scan for the\n* characters that make any of it necessary sends the common value straight to a\n* concatenation. A false positive only costs the slow path, so the scan errs wide.\n*/\nfunction leafClass(prefix, value) {\n if (value == null) return \"\";\n const type = typeof value;\n if (type === \"number\" || type === \"boolean\") return `${prefix}${value}`;\n if (type !== \"string\") return void 0;\n const str = value;\n for (let index = 0; index < str.length; index++) {\n const code = str.charCodeAt(index);\n if (code <= 33 || code === 160 || code === 5760 || code >= 8192) return slowLeaf(prefix, str);\n }\n return `${prefix}${str}`;\n}\n/** The full pipeline `createCss` runs, for a value that needs it. */\nfunction slowLeaf(prefix, value) {\n const important = isImportant(value);\n const className = `${prefix}${withoutSpace(withoutImportant(sanitize(value)))}`;\n return important ? `${className}!` : className;\n}\n//#endregion\n//#region src/hypenate-property.ts\nconst wordRegex = /([A-Z])/g;\nconst msRegex = /^ms-/;\nconst hypenateProperty = memo((property) => {\n if (property.startsWith(\"--\")) return property;\n return property.replace(wordRegex, \"-$1\").replace(msRegex, \"-ms-\").toLowerCase();\n});\n//#endregion\n//#region src/is-css-function.ts\nconst fnRegExp = new RegExp(`^(${[\n \"min\",\n \"max\",\n \"clamp\",\n \"calc\"\n].join(\"|\")})\\\\(.*\\\\)`);\nconst isCssFunction = (v) => typeof v === \"string\" && fnRegExp.test(v);\n//#endregion\n//#region src/is-css-unit.ts\nconst lengthUnitsPattern = `(?:${\"cm,mm,Q,in,pc,pt,px,em,ex,ch,rem,lh,rlh,vw,vh,vmin,vmax,vb,vi,svw,svh,lvw,lvh,dvw,dvh,cqw,cqh,cqi,cqb,cqmin,cqmax,%\".split(\",\").join(\"|\")})`;\nconst lengthRegExp = new RegExp(`^[+-]?[0-9]*.?[0-9]+(?:[eE][+-]?[0-9]+)?${lengthUnitsPattern}$`);\nconst isCssUnit = (v) => typeof v === \"string\" && lengthRegExp.test(v);\n//#endregion\n//#region src/is-css-var.ts\nconst isCssVar = (v) => typeof v === \"string\" && /^var\\(--.+\\)$/.test(v);\n//#endregion\n//#region src/pattern-fns.ts\nconst patternFns = {\n map: mapObject,\n isCssFunction,\n isCssVar,\n isCssUnit\n};\nconst getPatternStyles = (pattern, styles) => {\n if (!pattern?.defaultValues) return styles;\n const defaults = typeof pattern.defaultValues === \"function\" ? pattern.defaultValues(styles) : pattern.defaultValues;\n return Object.assign({}, defaults, compact(styles));\n};\n//#endregion\n//#region src/slot.ts\nconst getSlotRecipes = (recipe = {}) => {\n const init = (slot) => ({\n className: [recipe.className, slot].filter(Boolean).join(\"__\"),\n base: recipe.base?.[slot] ?? {},\n variants: {},\n defaultVariants: recipe.defaultVariants ?? {},\n compoundVariants: recipe.compoundVariants ? getSlotCompoundVariant(recipe.compoundVariants, slot) : []\n });\n const recipeParts = (recipe.slots ?? []).map((slot) => [slot, init(slot)]);\n for (const [variantsKey, variantsSpec] of Object.entries(recipe.variants ?? {})) for (const [variantKey, variantSpec] of Object.entries(variantsSpec)) recipeParts.forEach(([slot, slotRecipe]) => {\n slotRecipe.variants[variantsKey] ??= {};\n slotRecipe.variants[variantsKey][variantKey] = variantSpec[slot] ?? {};\n });\n return Object.fromEntries(recipeParts);\n};\nconst getSlotCompoundVariant = (compoundVariants, slotName) => compoundVariants.filter((compoundVariant) => compoundVariant.css[slotName]).map((compoundVariant) => ({\n ...compoundVariant,\n css: compoundVariant.css[slotName]\n}));\n//#endregion\n//#region src/split-props.ts\n/**\n* Deal a props object into one bucket per key group, plus a final bucket for the rest.\n* A key goes to the first group that claims it.\n*\n* ## Why the descriptor is read per key rather than in bulk\n*\n* This used to call `Object.getOwnPropertyDescriptors` for the whole object and\n* `defineProperty` for every key it moved. Copying plain values instead is 2.4–2.9x faster\n* on the shapes that allow it, but it is only correct where props are data — and they are\n* not always. Solid compiles props to accessors, so reading one eagerly runs whatever it\n* wraps: splitting a component's props would construct its children before the surrounding\n* provider exists.\n*\n* So the descriptor is fetched per key, and the value path is taken only when it changes\n* nothing observable. An accessor keeps its laziness, a non-enumerable key keeps its\n* invisibility, and `__proto__` is defined rather than assigned so it stays an own\n* property instead of reaching the prototype setter.\n*\n* The one thing the value path drops is `writable`/`configurable`, so a bucket key taken\n* from frozen props is writable where it used to be frozen. Nothing here relies on that,\n* and preserving it would mean `defineProperty` on the common path — the cost this exists\n* to avoid. Keys that take the descriptor path keep theirs, so a bucket can be\n* inconsistent in that one respect.\n*\n* Key order within a bucket is preserved exactly. It is not cosmetic: `cva` merges\n* variant props in iteration order, and the parser reads the rest bucket as the style\n* props it encodes, so order reaches the emitted CSS.\n*/\nfunction splitProps(props, ...keys) {\n const allKeys = Object.getOwnPropertyNames(props);\n const own = new Set(allKeys);\n const taken = /* @__PURE__ */ new Set();\n const split = (group) => {\n const clone = {};\n for (let i = 0; i < group.length; i++) {\n const key = group[i];\n if (taken.has(key) || !own.has(key)) continue;\n const descriptor = Object.getOwnPropertyDescriptor(props, key);\n if (!descriptor) continue;\n if (\"get\" in descriptor || \"set\" in descriptor || !descriptor.enumerable || key === \"__proto__\") Object.defineProperty(clone, key, descriptor);\n else clone[key] = descriptor.value;\n taken.add(key);\n }\n return clone;\n };\n /**\n * The predicate is called with the key alone.\n *\n * Handing it to `filter` passes `(key, index, allKeys)`. A one-parameter predicate cannot\n * see the extras, but a memoized one reads its whole argument list — and the predicates\n * that arrive here are memoized, `isCssProperty` among them. So the memo hashed the entire\n * key array once per prop, and keyed its cache on it: two elements with different prop sets\n * shared no entry even for the same prop name.\n *\n * Worth ~9.7x on that path, and nothing at all on a plain predicate — which is why the\n * bench below it needs a memoized case to see this at all.\n *\n * A loop rather than `filter((k) => key(k))` because the wrapper allocates a closure per\n * group. The two measure the same to within noise; the loop just does not need one.\n */\n const matching = (predicate) => {\n const group = [];\n for (let i = 0; i < allKeys.length; i++) {\n const key = allKeys[i];\n if (predicate(key)) group.push(key);\n }\n return group;\n };\n return keys.map((key) => split(Array.isArray(key) ? key : matching(key))).concat(split(allKeys));\n}\n//#endregion\n//#region src/uniq.ts\nconst uniq = (...items) => {\n const set = items.reduce((acc, currItems) => {\n if (currItems) currItems.forEach((item) => acc.add(item));\n return acc;\n }, /* @__PURE__ */ new Set([]));\n return Array.from(set);\n};\n//#endregion\n//#region src/view-transition.ts\n/**\n* The slots a `viewTransition()` bag accepts, in the order they are emitted.\n*\n* Anything else in the options object is ignored — including by the hash, so adding a\n* comment key or spreading an unrelated object does not fork the class name.\n*/\nconst viewTransitionSlots = [\n \"group\",\n \"imagePair\",\n \"old\",\n \"new\"\n];\n/**\n* Serialize a value with object keys sorted, so two style objects that differ only in\n* authoring order hash to the same class.\n*\n* Values JSON has no encoding for (`undefined`, functions, symbols) serialize as `null`\n* rather than being dropped. Nothing reaches here holding one — `filterSlots` removes\n* nullish slots, and the extractor drops nullish leaves — so this is only a floor: it\n* keeps a hole in an array from shifting the elements after it.\n*\n* Not exported. The name promises a general-purpose stable serializer, and this is not\n* one; `viewTransitionClassName` is the whole reason it exists.\n*/\nfunction stableStringify(value) {\n if (value === null) return \"null\";\n const type = typeof value;\n if (type === \"boolean\") return value ? \"true\" : \"false\";\n if (type === \"number\") return Number.isFinite(value) ? String(value) : \"null\";\n if (type === \"string\") return JSON.stringify(value);\n if (Array.isArray(value)) {\n let out = \"[\";\n for (let index = 0; index < value.length; index++) {\n if (index) out += \",\";\n out += stableStringify(value[index]);\n }\n return out + \"]\";\n }\n if (type === \"object\") {\n const keys = Object.keys(value).sort();\n let out = \"{\";\n for (let index = 0; index < keys.length; index++) {\n if (index) out += \",\";\n const key = keys[index];\n out += JSON.stringify(key) + \":\" + stableStringify(value[key]);\n }\n return out + \"}\";\n }\n return \"null\";\n}\n/**\n* Keep the four known slots, dropping any that is nullish.\n*\n* Both halves of the contract have to agree on what an empty slot is, and only one of\n* them gets a choice. The extractor evaluates the source, and a nullish property is gone\n* from what it hands over — `{ new: undefined }` and `{}` reach the build identically.\n* So absent, `undefined` and `null` collapse here too, and `new: enabled ? {…} : null`\n* hashes to the bag it actually styles.\n*/\nfunction filterSlots(options) {\n const filtered = {};\n if (!options || typeof options !== \"object\") return filtered;\n for (const slot of viewTransitionSlots) {\n const value = options[slot];\n if (value != null) filtered[slot] = value;\n }\n return filtered;\n}\n/**\n* The class a `viewTransition()` call resolves to.\n*\n* This is the whole contract between the build and the runtime: the extractor hashes the\n* options it found in the source, the generated `viewTransition()` hashes the options it\n* is called with, and the CSS only reaches the element if the two agree. They agree by\n* construction — both call this function — but only over what the extractor can see. A\n* value it cannot resolve statically is absent from its side and present on the runtime's,\n* which is why a bag has to be a static object literal to be styled at all.\n*\n* The class is used twice, as the `view-transition-class` value and as the argument to\n* `::view-transition-*(.cls)`, so the prefix applies to both.\n*/\nfunction viewTransitionClassName(options, prefix = \"\") {\n const base = \"vt_\" + toHash(stableStringify(filterSlots(options)));\n return prefix ? prefix + \"-\" + base : base;\n}\n//#endregion\nexport { cloneStyles, compact, createCss, createMergeCss, filterBaseConditions, getPatternStyles, getSlotCompoundVariant, getSlotRecipes, hypenateProperty, isBaseCondition, isObject, leafClass, mapObject, memo, mergeProps, patternFns, splitProps, toHash, uniq, viewTransitionClassName, walkObject, withoutSpace };\n";
|
|
456
673
|
//#endregion
|
|
457
674
|
//#region src/artifacts/generated/normalize-html.mjs.json
|
|
458
675
|
var content$9 = "//#region src/normalize-html.ts\nconst htmlProps = [\n \"htmlSize\",\n \"htmlTranslate\",\n \"htmlWidth\",\n \"htmlHeight\"\n];\nfunction convert(key) {\n return htmlProps.includes(key) ? key.replace(\"html\", \"\").toLowerCase() : key;\n}\nfunction normalizeHTMLProps(props) {\n return Object.fromEntries(Object.entries(props).map(([key, value]) => [convert(key), value]));\n}\nnormalizeHTMLProps.keys = htmlProps;\n//#endregion\nexport { normalizeHTMLProps };\n";
|
|
@@ -4539,7 +4756,7 @@ var content$2 = "import type { Pseudos } from './csstype'\n\ntype AriaAttributes
|
|
|
4539
4756
|
var content$1 = "interface ConditionOptions {\n /**\n * The conditions to generate for the rule.\n * @example ['hover', 'focus']\n */\n conditions?: string[]\n /**\n * Whether to generate responsive styles for the rule.\n */\n responsive?: boolean\n}\n\nexport interface CssRule extends ConditionOptions {\n /**\n * The css properties to generate utilities for.\n * @example ['margin', 'padding']\n */\n properties: {\n [property: string]: Array<string | number>\n }\n}\n\ninterface RecipeRuleVariants {\n [variant: string]: boolean | string[]\n}\n\nexport type RecipeRuleObject = RecipeRuleVariants & ConditionOptions\nexport type RecipeRule = '*' | RecipeRuleObject\n\nexport type PatternRule = '*' | CssRule\n\nexport interface StaticCssOptions {\n /**\n * The css utility classes to generate.\n */\n css?: CssRule[]\n /**\n * The css recipes to generate.\n */\n recipes?:\n | '*'\n | {\n [recipe: string]: RecipeRule[]\n }\n /**\n * The css patterns to generate.\n */\n patterns?: {\n [pattern: string]: PatternRule[]\n }\n /**\n * The CSS themes to generate\n */\n themes?: string[]\n}\n";
|
|
4540
4757
|
//#endregion
|
|
4541
4758
|
//#region src/artifacts/generated/system-types.d.ts.json
|
|
4542
|
-
var content = "import type { ConditionalValue, Nested } from './conditions'\nimport type { AtRule, Globals, PropertiesFallback } from './csstype'\nimport type { SystemProperties, CssVarProperties } from './style-props'\n\ntype String = string & {}\ntype Number = number & {}\n\nexport type Pretty<T> = { [K in keyof T]: T[K] } & {}\n\nexport type DistributiveOmit<T, K extends keyof any> = T extends unknown ? Omit<T, K> : never\n\nexport type DistributiveUnion<T, U> = {\n [K in keyof T]: K extends keyof U ? U[K] | T[K] : T[K]\n} & DistributiveOmit<U, keyof T>\n\nexport type Assign<T, U> = {\n [K in keyof T]: K extends keyof U ? U[K] : T[K]\n} & U\n\n/* -----------------------------------------------------------------------------\n * Native css properties\n * -----------------------------------------------------------------------------*/\n\ntype CornerShapeValue = 'round' | 'square' | 'bevel' | 'scoop' | 'notch' | 'squircle' | `superellipse(${number})`\n\nexport interface ModernCssProperties {\n /**\n * Controls whether the entire element should be draggable instead of its contents.\n */\n WebkitUserDrag?: Globals | 'auto' | 'element' | 'none'\n\n /**\n * Specifies whether an element can be used to drag the entire app window (Electron).\n */\n WebkitAppRegion?: Globals | 'drag' | 'no-drag'\n\n /**\n * Sets the horizontal spacing between table borders.\n */\n WebkitBorderHorizontalSpacing?: Globals | String | Number\n\n /**\n * Sets the vertical spacing between table borders.\n */\n WebkitBorderVerticalSpacing?: Globals | String | Number\n\n /**\n * Controls the display of text content for security purposes (e.g., password fields).\n */\n WebkitTextSecurity?: Globals | 'none' | 'circle' | 'disc' | 'square'\n\n /**\n * Specifies the shape of a box's corners within the area defined by the border-radius property.\n * @experimental\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/corner-shape\n */\n cornerShape?:\n | Globals\n | CornerShapeValue\n | `${CornerShapeValue} ${CornerShapeValue}`\n | `${CornerShapeValue} ${CornerShapeValue} ${CornerShapeValue}`\n | `${CornerShapeValue} ${CornerShapeValue} ${CornerShapeValue} ${CornerShapeValue}`\n | String\n}\n\nexport type CssProperty = keyof PropertiesFallback\n\nexport interface CssProperties extends PropertiesFallback<String | Number>, CssVarProperties, ModernCssProperties {}\n\nexport interface CssKeyframes {\n [name: string]: {\n [time: string]: CssProperties\n }\n}\n\n/* -----------------------------------------------------------------------------\n * Conditional css properties\n * -----------------------------------------------------------------------------*/\n\ninterface GenericProperties {\n [key: string]: ConditionalValue<String | Number | boolean>\n}\n\n/* -----------------------------------------------------------------------------\n * Native css props\n * -----------------------------------------------------------------------------*/\n\nexport type NestedCssProperties = Nested<CssProperties>\n\nexport type SystemStyleObject = Omit<Nested<SystemProperties & CssVarProperties>, 'base'>\n\nexport interface GlobalStyleObject {\n [selector: string]: SystemStyleObject\n}\nexport interface ExtendableGlobalStyleObject {\n [selector: string]: SystemStyleObject | undefined\n extend?: GlobalStyleObject | undefined\n}\n\n/* -----------------------------------------------------------------------------\n * Composition (text styles, layer styles)\n * -----------------------------------------------------------------------------*/\n\ntype FilterStyleObject<P extends string> = {\n [K in P]?: K extends keyof SystemStyleObject ? SystemStyleObject[K] : unknown\n}\n\nexport type CompositionStyleObject<Property extends string> = Nested<FilterStyleObject<Property> & CssVarProperties>\n\n/* -----------------------------------------------------------------------------\n * Font face\n * -----------------------------------------------------------------------------*/\n\nexport type GlobalFontfaceRule = Omit<AtRule.FontFaceFallback, 'src'> & Required<Pick<AtRule.FontFaceFallback, 'src'>>\n\nexport type FontfaceRule = Omit<GlobalFontfaceRule, 'fontFamily'>\n\nexport interface GlobalFontface {\n [name: string]: FontfaceRule | FontfaceRule[]\n}\n\nexport interface ExtendableGlobalFontface {\n [name: string]: FontfaceRule | FontfaceRule[] | GlobalFontface | undefined\n extend?: GlobalFontface | undefined\n}\n\n/* -----------------------------------------------------------------------------\n * Jsx style props\n * -----------------------------------------------------------------------------*/\ninterface WithCss {\n css?: SystemStyleObject | SystemStyleObject[]\n}\n\nexport type JsxStyleProps = SystemStyleObject & WithCss\n\nexport interface PatchedHTMLProps {\n htmlWidth?: string | number\n htmlHeight?: string | number\n htmlTranslate?: 'yes' | 'no' | undefined\n htmlContent?: string\n}\n\nexport type OmittedHTMLProps = 'color' | 'translate' | 'transition' | 'width' | 'height' | 'content'\n\ntype WithHTMLProps<T> = DistributiveOmit<T, OmittedHTMLProps> & PatchedHTMLProps\n\nexport type JsxHTMLProps<T extends Record<string, any>, P extends Record<string, any> = {}> = Assign<\n WithHTMLProps<T>,\n P\n>\n";
|
|
4759
|
+
var content = "import type { ConditionalValue, Nested } from './conditions'\nimport type { AtRule, Globals, PropertiesFallback } from './csstype'\nimport type { SystemProperties, CssVarProperties } from './style-props'\n\ntype String = string & {}\ntype Number = number & {}\n\nexport type Pretty<T> = { [K in keyof T]: T[K] } & {}\n\nexport type DistributiveOmit<T, K extends keyof any> = T extends unknown ? Omit<T, K> : never\n\nexport type DistributiveUnion<T, U> = {\n [K in keyof T]: K extends keyof U ? U[K] | T[K] : T[K]\n} & DistributiveOmit<U, keyof T>\n\nexport type Assign<T, U> = {\n [K in keyof T]: K extends keyof U ? U[K] : T[K]\n} & U\n\n/* -----------------------------------------------------------------------------\n * Native css properties\n * -----------------------------------------------------------------------------*/\n\ntype CornerShapeValue = 'round' | 'square' | 'bevel' | 'scoop' | 'notch' | 'squircle' | `superellipse(${number})`\n\nexport interface ModernCssProperties {\n /**\n * Controls whether the entire element should be draggable instead of its contents.\n */\n WebkitUserDrag?: Globals | 'auto' | 'element' | 'none'\n\n /**\n * Specifies whether an element can be used to drag the entire app window (Electron).\n */\n WebkitAppRegion?: Globals | 'drag' | 'no-drag'\n\n /**\n * Sets the horizontal spacing between table borders.\n */\n WebkitBorderHorizontalSpacing?: Globals | String | Number\n\n /**\n * Sets the vertical spacing between table borders.\n */\n WebkitBorderVerticalSpacing?: Globals | String | Number\n\n /**\n * Controls the display of text content for security purposes (e.g., password fields).\n */\n WebkitTextSecurity?: Globals | 'none' | 'circle' | 'disc' | 'square'\n\n /**\n * Specifies the shape of a box's corners within the area defined by the border-radius property.\n * @experimental\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/corner-shape\n */\n cornerShape?:\n | Globals\n | CornerShapeValue\n | `${CornerShapeValue} ${CornerShapeValue}`\n | `${CornerShapeValue} ${CornerShapeValue} ${CornerShapeValue}`\n | `${CornerShapeValue} ${CornerShapeValue} ${CornerShapeValue} ${CornerShapeValue}`\n | String\n}\n\nexport type CssProperty = keyof PropertiesFallback\n\nexport interface CssProperties extends PropertiesFallback<String | Number>, CssVarProperties, ModernCssProperties {}\n\nexport interface CssKeyframes {\n [name: string]: {\n [time: string]: CssProperties\n }\n}\n\n/* -----------------------------------------------------------------------------\n * Conditional css properties\n * -----------------------------------------------------------------------------*/\n\ninterface GenericProperties {\n [key: string]: ConditionalValue<String | Number | boolean>\n}\n\n/* -----------------------------------------------------------------------------\n * Native css props\n * -----------------------------------------------------------------------------*/\n\nexport type NestedCssProperties = Nested<CssProperties>\n\nexport type SystemStyleObject = Omit<Nested<SystemProperties & CssVarProperties>, 'base'>\n\n/**\n * The four `::view-transition-*` pseudo-elements a `viewTransition()` bag can style.\n *\n * `imagePair` is camelCase here and emitted as `::view-transition-image-pair`, matching\n * how every other property in a style object is authored.\n */\nexport interface ViewTransitionStyleObject {\n group?: SystemStyleObject\n imagePair?: SystemStyleObject\n old?: SystemStyleObject\n new?: SystemStyleObject\n}\n\nexport type ViewTransitionFn = (options: ViewTransitionStyleObject) => string\n\nexport interface GlobalStyleObject {\n [selector: string]: SystemStyleObject\n}\nexport interface ExtendableGlobalStyleObject {\n [selector: string]: SystemStyleObject | undefined\n extend?: GlobalStyleObject | undefined\n}\n\n/* -----------------------------------------------------------------------------\n * Composition (text styles, layer styles)\n * -----------------------------------------------------------------------------*/\n\ntype FilterStyleObject<P extends string> = {\n [K in P]?: K extends keyof SystemStyleObject ? SystemStyleObject[K] : unknown\n}\n\nexport type CompositionStyleObject<Property extends string> = Nested<FilterStyleObject<Property> & CssVarProperties>\n\n/* -----------------------------------------------------------------------------\n * Font face\n * -----------------------------------------------------------------------------*/\n\nexport type GlobalFontfaceRule = Omit<AtRule.FontFaceFallback, 'src'> & Required<Pick<AtRule.FontFaceFallback, 'src'>>\n\nexport type FontfaceRule = Omit<GlobalFontfaceRule, 'fontFamily'>\n\nexport interface GlobalFontface {\n [name: string]: FontfaceRule | FontfaceRule[]\n}\n\nexport interface ExtendableGlobalFontface {\n [name: string]: FontfaceRule | FontfaceRule[] | GlobalFontface | undefined\n extend?: GlobalFontface | undefined\n}\n\n/* -----------------------------------------------------------------------------\n * Jsx style props\n * -----------------------------------------------------------------------------*/\ninterface WithCss {\n css?: SystemStyleObject | SystemStyleObject[]\n}\n\nexport type JsxStyleProps = SystemStyleObject & WithCss\n\nexport interface PatchedHTMLProps {\n htmlWidth?: string | number\n htmlHeight?: string | number\n htmlTranslate?: 'yes' | 'no' | undefined\n htmlContent?: string\n}\n\nexport type OmittedHTMLProps = 'color' | 'translate' | 'transition' | 'width' | 'height' | 'content'\n\ntype WithHTMLProps<T> = DistributiveOmit<T, OmittedHTMLProps> & PatchedHTMLProps\n\nexport type JsxHTMLProps<T extends Record<string, any>, P extends Record<string, any> = {}> = Assign<\n WithHTMLProps<T>,\n P\n>\n";
|
|
4543
4760
|
//#endregion
|
|
4544
4761
|
//#region src/artifacts/types/generated.ts
|
|
4545
4762
|
function getGeneratedTypes(ctx) {
|
|
@@ -4622,11 +4839,21 @@ function generatePropTypes(ctx) {
|
|
|
4622
4839
|
type Important = ImportantMark | WhitespaceImportant
|
|
4623
4840
|
type WithImportant<T> = [T] extends [string] ? \`\${T}\${Important}\` & { __important?: true } : never
|
|
4624
4841
|
|
|
4842
|
+
/**
|
|
4843
|
+
* A list of candidate values, most-preferred first, emitted as repeated declarations so the
|
|
4844
|
+
* browser keeps the last one it understands.
|
|
4845
|
+
*
|
|
4846
|
+
* @example
|
|
4847
|
+
* css({ height: 'fallback(100dvh, 100vh)' })
|
|
4848
|
+
*/
|
|
4849
|
+
type FallbackValue = \`fallback(\${string})\`
|
|
4850
|
+
|
|
4625
4851
|
/**
|
|
4626
4852
|
* Only relevant when using \`strictTokens\` or \`strictPropertyValues\` in your config.
|
|
4627
4853
|
* - Allows you to use an escape hatch (e.g. \`[123px]\`) to use any string as a value.
|
|
4628
4854
|
* - Allows you to use a color opacity modifier (e.g. \`red/300\`) with known color values.
|
|
4629
4855
|
* - Allows you to use an important mark (e.g. \`!\` or \`!important\`) in the value.
|
|
4856
|
+
* - Allows you to use a fallback list (e.g. \`fallback(100dvh, 100vh)\`).
|
|
4630
4857
|
*
|
|
4631
4858
|
* This is useful when you want to use a value that is not defined in the config or want to opt-out of the defaults.
|
|
4632
4859
|
*
|
|
@@ -4638,7 +4865,13 @@ function generatePropTypes(ctx) {
|
|
|
4638
4865
|
* @see https://bamboocss.com/docs/concepts/writing-styles#stricttokens
|
|
4639
4866
|
* @see https://bamboocss.com/docs/concepts/writing-styles#strictpropertyvalues
|
|
4640
4867
|
*/
|
|
4641
|
-
export type WithEscapeHatch<T> =
|
|
4868
|
+
export type WithEscapeHatch<T> =
|
|
4869
|
+
| T
|
|
4870
|
+
| \`[\${string}]\`
|
|
4871
|
+
| FallbackValue
|
|
4872
|
+
| WithImportant<FallbackValue>
|
|
4873
|
+
| WithColorOpacityModifier<T>
|
|
4874
|
+
| WithImportant<T>
|
|
4642
4875
|
|
|
4643
4876
|
/**
|
|
4644
4877
|
* Will restrict the value of properties that have predefined values to those values only.
|
|
@@ -5246,7 +5479,7 @@ function setupSva(ctx) {
|
|
|
5246
5479
|
};
|
|
5247
5480
|
}
|
|
5248
5481
|
function setupCx(ctx) {
|
|
5249
|
-
const code = generateCx();
|
|
5482
|
+
const code = generateCx(ctx);
|
|
5250
5483
|
return {
|
|
5251
5484
|
id: "cx",
|
|
5252
5485
|
dir: ctx.paths.css,
|
package/dist/index.mjs
CHANGED
|
@@ -117,7 +117,7 @@ function generateCssFn(ctx) {
|
|
|
117
117
|
const { separator, getPropShorthands } = utility;
|
|
118
118
|
return {
|
|
119
119
|
dts: outdent`
|
|
120
|
-
${ctx.file.importType("SystemStyleObject", "../types/index")}
|
|
120
|
+
${ctx.file.importType("SystemStyleObject, ViewTransitionFn", "../types/index")}
|
|
121
121
|
|
|
122
122
|
type Styles = SystemStyleObject | undefined | null | false
|
|
123
123
|
|
|
@@ -139,6 +139,39 @@ function generateCssFn(ctx) {
|
|
|
139
139
|
|
|
140
140
|
export declare const css: CssFunction;
|
|
141
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Build a fallback value: a list of candidates, most-preferred first, emitted as repeated
|
|
144
|
+
* declarations so the browser keeps the last one it understands.
|
|
145
|
+
*
|
|
146
|
+
* Sugar for the string form — \`fallback('100dvh', '100vh')\` is \`'fallback(100dvh, 100vh)'\`.
|
|
147
|
+
* The candidates are not individually type-checked, the same trade the \`[...]\` escape
|
|
148
|
+
* hatch makes.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* css({ height: fallback('calc(100dvh - 100px)', 'calc(100vh - 100px)') })
|
|
152
|
+
*
|
|
153
|
+
* @see https://bamboocss.com/docs/concepts/writing-styles#fallback-values
|
|
154
|
+
*/
|
|
155
|
+
export declare function fallback(preferred: string | number, ...rest: Array<string | number>): \`fallback(\${string})\`;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Style the View Transitions API and get back one stable class for the bag.
|
|
159
|
+
*
|
|
160
|
+
* The class is applied through \`view-transition-class\`, so the same transition can be
|
|
161
|
+
* shared by any number of elements. You still set \`view-transition-name\` yourself —
|
|
162
|
+
* it has to be unique per element, so bamboo cannot share it for you.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* const slide = viewTransition({
|
|
166
|
+
* group: { animationDuration: '0.4s' },
|
|
167
|
+
* old: { animationName: 'slide-out' },
|
|
168
|
+
* new: { animationName: 'slide-in' },
|
|
169
|
+
* })
|
|
170
|
+
*
|
|
171
|
+
* @see https://bamboocss.com/docs/concepts/view-transitions
|
|
172
|
+
*/
|
|
173
|
+
export declare const viewTransition: ViewTransitionFn;
|
|
174
|
+
|
|
142
175
|
/**
|
|
143
176
|
* Internal. Emitted for the source transform, which rewrites a single dynamic style
|
|
144
177
|
* leaf into a call to this. Not part of the authoring API.
|
|
@@ -146,7 +179,7 @@ function generateCssFn(ctx) {
|
|
|
146
179
|
export declare const cssLeaf: (prefix: string, prop: string, value: unknown) => string;
|
|
147
180
|
`,
|
|
148
181
|
js: outdent`
|
|
149
|
-
${ctx.file.import("cloneStyles, createCss, createMergeCss, hypenateProperty, leafClass, memo, withoutSpace", "../helpers")}
|
|
182
|
+
${ctx.file.import("cloneStyles, createCss, createMergeCss, hypenateProperty, leafClass, memo, viewTransitionClassName, withoutSpace", "../helpers")}
|
|
150
183
|
${ctx.file.import("sortConditions, finalizeConditions", "./conditions")}
|
|
151
184
|
|
|
152
185
|
const utilities = "${utility.entries().map(([prop, className]) => {
|
|
@@ -211,6 +244,16 @@ function generateCssFn(ctx) {
|
|
|
211
244
|
return className === undefined ? css({ [prop]: value }) : className
|
|
212
245
|
}
|
|
213
246
|
|
|
247
|
+
// Sugar for the string form, so the feature has an import to discover, a signature to
|
|
248
|
+
// hover and a name the editor can complete. The extractor evaluates the call, so the
|
|
249
|
+
// value reaching \`css()\` is the same literal either way.
|
|
250
|
+
export const fallback = (...values) => \`fallback($\{values.join(', ')})\`
|
|
251
|
+
|
|
252
|
+
// The class is the whole return value — the CSS behind it was emitted at build time
|
|
253
|
+
// from the same options, hashed by this same function. A call the extractor never saw
|
|
254
|
+
// still returns a class, exactly as \`css()\` does for a value it never saw.
|
|
255
|
+
export const viewTransition = (options) => viewTransitionClassName(options, ${JSON.stringify(prefix.className ?? "")})
|
|
256
|
+
|
|
214
257
|
export const { mergeCss, assignCss } = createMergeCss(context)
|
|
215
258
|
`
|
|
216
259
|
};
|
|
@@ -394,31 +437,205 @@ function generateCvaFn(ctx) {
|
|
|
394
437
|
}
|
|
395
438
|
//#endregion
|
|
396
439
|
//#region src/artifacts/js/cx.ts
|
|
397
|
-
|
|
440
|
+
const dts = outdent$1`
|
|
441
|
+
type Argument = string | boolean | null | undefined | Argument[]
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Join classNames into a single string, with the last conflicting utility winning.
|
|
445
|
+
*
|
|
446
|
+
* \`cx('px_4', 'px_2')\` is \`'px_2'\`: two classes that set the same property under the
|
|
447
|
+
* same conditions cannot both apply, and which one the browser picks would otherwise
|
|
448
|
+
* depend on their order in the stylesheet rather than on the order you passed them.
|
|
449
|
+
* Classes bamboo did not generate are left alone, duplicates included.
|
|
450
|
+
*/
|
|
451
|
+
export declare function cx(...args: Argument[]): string
|
|
452
|
+
`;
|
|
453
|
+
/**
|
|
454
|
+
* The plain concatenating \`cx\`, for when the class names carry nothing to merge on.
|
|
455
|
+
*
|
|
456
|
+
* With \`hash.className\` every class is an opaque hash, so there is no property to compare
|
|
457
|
+
* and no merge to do — emitting the matcher would only cost bytes on a path that runs in
|
|
458
|
+
* the browser on every render.
|
|
459
|
+
*/
|
|
460
|
+
function concatOnly() {
|
|
461
|
+
return outdent$1`
|
|
462
|
+
function cx(...args) {
|
|
463
|
+
let str = ''
|
|
464
|
+
|
|
465
|
+
for (let i = 0; i < args.length; i++) {
|
|
466
|
+
const arg = args[i]
|
|
467
|
+
if (!arg) continue
|
|
468
|
+
// Arrays are part of the declared type, so this branch has to handle them even
|
|
469
|
+
// though it does no merging — returning '' for \`cx(['a', 'b'])\` would be a lie.
|
|
470
|
+
const part = Array.isArray(arg) ? cx(...arg) : typeof arg === 'string' ? arg : ''
|
|
471
|
+
if (!part) continue
|
|
472
|
+
str && (str += ' ')
|
|
473
|
+
str += part
|
|
474
|
+
}
|
|
475
|
+
return str
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
export { cx }
|
|
479
|
+
`;
|
|
480
|
+
}
|
|
481
|
+
function generateCx(ctx) {
|
|
482
|
+
const { utility, hash, prefix } = ctx;
|
|
483
|
+
const separatorChar = utility.separator;
|
|
484
|
+
const utilityClassNames = [...new Set(utility.keys().map((key) => {
|
|
485
|
+
const withEmptyValue = utility.getClassName(utility.resolveShorthand(key), "");
|
|
486
|
+
return withEmptyValue.endsWith(separatorChar) ? withEmptyValue.slice(0, -separatorChar.length) : withEmptyValue;
|
|
487
|
+
}))].sort();
|
|
488
|
+
const recipeClassNames = [...new Set(ctx.recipes.details.map((node) => node.className))].filter(Boolean).sort();
|
|
489
|
+
if (hash.className || utilityClassNames.length === 0) return {
|
|
490
|
+
js: concatOnly(),
|
|
491
|
+
dts
|
|
492
|
+
};
|
|
398
493
|
return {
|
|
399
494
|
js: outdent$1`
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
495
|
+
const cxSeparator = ${JSON.stringify(utility.separator)}
|
|
496
|
+
const cxPrefix = ${prefix.className ? JSON.stringify(prefix.className + "-") : "''"}
|
|
497
|
+
const cxUtilities = new Set(${JSON.stringify(utilityClassNames.join(","))}.split(','))
|
|
498
|
+
const cxRecipes = ${recipeClassNames.length ? `new Set(${JSON.stringify(recipeClassNames.join(","))}.split(','))` : "null"}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* The declaration a bamboo class sets: its condition path plus the property, without the
|
|
502
|
+
* value. Two classes sharing one are alternatives, and only the last can apply.
|
|
503
|
+
*
|
|
504
|
+
* \`null\` for anything that is not a bamboo class, which is then never merged.
|
|
505
|
+
*/
|
|
506
|
+
function mergeKey(className) {
|
|
507
|
+
let end = className.length
|
|
508
|
+
|
|
509
|
+
// \`c_red\` and \`c_red!\` are the same declaration. Argument order decides between them,
|
|
510
|
+
// which is the point of this function — the cascade would always pick the important
|
|
511
|
+
// one no matter which the caller asked for.
|
|
512
|
+
if (end > 0 && className.charCodeAt(end - 1) === 33) end -= 1
|
|
513
|
+
if (end === 0) return null
|
|
514
|
+
|
|
515
|
+
// The last colon ends the condition path — but only one outside brackets, since an
|
|
516
|
+
// arbitrary selector carries its own: \`[&[data-x="a:b"]]:px_4\`.
|
|
517
|
+
let depth = 0
|
|
518
|
+
let lastColon = -1
|
|
519
|
+
for (let i = 0; i < end; i++) {
|
|
520
|
+
const code = className.charCodeAt(i)
|
|
521
|
+
if (code === 91) depth++
|
|
522
|
+
else if (code === 93) depth--
|
|
523
|
+
else if (code === 58 && depth === 0) lastColon = i
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Conditions come before the prefix — \`hover:bam-px_4\` — so the prefix is skipped
|
|
527
|
+
// after the condition path, not before it. When a prefix is configured every class
|
|
528
|
+
// bamboo emits carries it, so one that does not is by definition someone else's.
|
|
529
|
+
let propStart = lastColon + 1
|
|
530
|
+
if (cxPrefix) {
|
|
531
|
+
if (!className.startsWith(cxPrefix, propStart)) return null
|
|
532
|
+
propStart += cxPrefix.length
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// A recipe owns its whole class, bare or with a \`--variant\` suffix, whatever it
|
|
536
|
+
// looks like to the utility matcher below.
|
|
537
|
+
if (cxRecipes !== null) {
|
|
538
|
+
const variantIdx = className.indexOf('--', propStart)
|
|
539
|
+
const base = variantIdx === -1 ? className.slice(propStart, end) : className.slice(propStart, variantIdx)
|
|
540
|
+
if (cxRecipes.has(base)) return null
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// The longest registered utility name wins, not the first separator. Utility names
|
|
544
|
+
// contain the separator themselves under \`separator: '-'\` — \`bd-w\`, \`ov-x\`,
|
|
545
|
+
// \`translate-x\` — and their leading segment is often a utility too, so stopping at the
|
|
546
|
+
// first \`-\` would key \`bd-w-4px\` and \`bd-c-red\` both on \`bd\` and drop one of them.
|
|
547
|
+
let property = null
|
|
548
|
+
let sepIdx = className.indexOf(cxSeparator, propStart)
|
|
549
|
+
while (sepIdx > propStart && sepIdx < end) {
|
|
550
|
+
const candidate = className.slice(propStart, sepIdx)
|
|
551
|
+
if (cxUtilities.has(candidate)) property = candidate
|
|
552
|
+
sepIdx = className.indexOf(cxSeparator, sepIdx + 1)
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Only a class bamboo generated for a utility. A recipe class or a hand-written one
|
|
556
|
+
// may well contain the separator, and merging on the text before it would drop a
|
|
557
|
+
// class the caller meant to keep.
|
|
558
|
+
if (property === null) return null
|
|
559
|
+
|
|
560
|
+
return lastColon === -1 ? property : className.slice(0, lastColon) + ':' + property
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function isClassWhitespace(code) {
|
|
564
|
+
return code === 32 || code === 9 || code === 10 || code === 12 || code === 13
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function flattenParts(parts, out) {
|
|
568
|
+
for (let i = 0; i < parts.length; i++) {
|
|
569
|
+
const part = parts[i]
|
|
570
|
+
if (!part) continue
|
|
571
|
+
if (Array.isArray(part)) flattenParts(part, out)
|
|
572
|
+
else if (typeof part === 'string') out.push(part)
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function mergeClassStrings(classes) {
|
|
577
|
+
const seen = new Map()
|
|
578
|
+
const order = []
|
|
579
|
+
let id = 0
|
|
580
|
+
|
|
581
|
+
for (let c = 0; c < classes.length; c++) {
|
|
582
|
+
const cls = classes[c]
|
|
583
|
+
let tokenStart = 0
|
|
584
|
+
|
|
585
|
+
for (let i = 0; i <= cls.length; i++) {
|
|
586
|
+
// The class attribute splits on all ASCII whitespace, not just the space, and a
|
|
587
|
+
// multi-line template literal is an ordinary way to write one.
|
|
588
|
+
if (i !== cls.length && !isClassWhitespace(cls.charCodeAt(i))) continue
|
|
589
|
+
if (i === tokenStart) {
|
|
590
|
+
tokenStart = i + 1
|
|
591
|
+
continue
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const token = cls.slice(tokenStart, i)
|
|
595
|
+
tokenStart = i + 1
|
|
596
|
+
|
|
597
|
+
const key = mergeKey(token)
|
|
598
|
+
if (key !== null) {
|
|
599
|
+
// Keeps the first position and the last value, so a later override lands where
|
|
600
|
+
// the class it replaces already sat.
|
|
601
|
+
if (!seen.has(key)) order.push(key)
|
|
602
|
+
seen.set(key, token)
|
|
603
|
+
} else {
|
|
604
|
+
// Not ours to reason about — kept as written, duplicates and all.
|
|
605
|
+
const uniqueKey = '\\0' + id++
|
|
606
|
+
order.push(uniqueKey)
|
|
607
|
+
seen.set(uniqueKey, token)
|
|
608
|
+
}
|
|
409
609
|
}
|
|
410
610
|
}
|
|
611
|
+
|
|
612
|
+
if (order.length === 0) return ''
|
|
613
|
+
let str = seen.get(order[0])
|
|
614
|
+
for (let i = 1; i < order.length; i++) str += ' ' + seen.get(order[i])
|
|
411
615
|
return str
|
|
412
616
|
}
|
|
413
617
|
|
|
618
|
+
function cx() {
|
|
619
|
+
// Everything that produces a bamboo class string — \`css()\`, a recipe, a nested \`cx\` —
|
|
620
|
+
// emits one that is already conflict-free, so a lone string has nothing to merge and
|
|
621
|
+
// tokenizing it is pure cost. This is the hot path: \`cx(staticClasses, props.className)\`
|
|
622
|
+
// with no \`className\` passed.
|
|
623
|
+
if (arguments.length === 1) {
|
|
624
|
+
const only = arguments[0]
|
|
625
|
+
if (typeof only === 'string') return only
|
|
626
|
+
if (!only) return ''
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const flat = []
|
|
630
|
+
flattenParts(arguments, flat)
|
|
631
|
+
if (flat.length === 0) return ''
|
|
632
|
+
if (flat.length === 1) return flat[0]
|
|
633
|
+
return mergeClassStrings(flat)
|
|
634
|
+
}
|
|
635
|
+
|
|
414
636
|
export { cx }
|
|
415
637
|
`,
|
|
416
|
-
dts
|
|
417
|
-
type Argument = string | boolean | null | undefined
|
|
418
|
-
|
|
419
|
-
/** Conditionally join classNames into a single string */
|
|
420
|
-
export declare function cx(...args: Argument[]): string
|
|
421
|
-
`
|
|
638
|
+
dts
|
|
422
639
|
};
|
|
423
640
|
}
|
|
424
641
|
//#endregion
|
|
@@ -426,7 +643,7 @@ function generateCx() {
|
|
|
426
643
|
var content$11 = "//#region src/astish.ts\nconst newRule = /(?:([\\u0080-\\uFFFF\\w-%@]+) *:? *([^{;]+?);|([^;}{]*?) *{)|(}\\s*)/g;\nconst ruleClean = /\\/\\*[^]*?\\*\\/| +/g;\nconst ruleNewline = /\\n+/g;\nconst empty = \" \";\nconst astish = (val, tree = [{}]) => {\n if (!val) return tree[0];\n let block, left;\n while (block = newRule.exec(val.replace(ruleClean, \"\"))) if (block[4]) tree.shift();\n else if (block[3]) {\n left = block[3].replace(ruleNewline, empty).trim();\n if (!left.includes(\"&\") && !left.startsWith(\"@\")) left = \"& \" + left;\n tree.unshift(tree[0][left] = tree[0][left] || {});\n } else tree[0][block[1]] = block[2].replace(ruleNewline, empty).trim();\n return tree[0];\n};\n//#endregion\nexport { astish };\n";
|
|
427
644
|
//#endregion
|
|
428
645
|
//#region src/artifacts/generated/helpers.mjs.json
|
|
429
|
-
var content$10 = "//#region src/assert.ts\nfunction isObject(value) {\n return typeof value === \"object\" && value != null && !Array.isArray(value);\n}\nconst isObjectOrArray = (obj) => typeof obj === \"object\" && obj !== null;\n//#endregion\n//#region src/condition.ts\nconst isBaseCondition = (v) => v === \"base\";\nfunction filterBaseConditions(c) {\n return c.slice().filter((v) => !isBaseCondition(v));\n}\n//#endregion\n//#region src/hash.ts\nfunction toChar(code) {\n return String.fromCharCode(code + (code > 25 ? 39 : 97));\n}\nfunction toName(code) {\n let name = \"\";\n let x;\n for (x = Math.abs(code); x > 52; x = x / 52 | 0) name = toChar(x % 52) + name;\n return toChar(x % 52) + name;\n}\nfunction toPhash(h, x) {\n let i = x.length;\n while (i) h = h * 33 ^ x.charCodeAt(--i);\n return h;\n}\nfunction toHash(value) {\n return toName(toPhash(5381, value) >>> 0);\n}\n//#endregion\n//#region src/important.ts\nconst importantRegex = /\\s*!(important)?/i;\nconst whitespaceRegex = /\\s/;\n/**\n* Collapse every run of whitespace to a single space, which is what the class name is\n* built from. Exported because `leafClass` has to reproduce this exact pipeline, and a\n* second copy of it would be free to drift from the one `createCss` runs.\n*/\nfunction sanitize(value) {\n if (typeof value !== \"string\") return value;\n return whitespaceRegex.test(value) ? value.replaceAll(/[\\n\\s]+/g, \" \") : value;\n}\nfunction isImportant(value) {\n if (typeof value !== \"string\") return false;\n return value.includes(\"!\") && importantRegex.test(value);\n}\nfunction withoutImportant(value) {\n if (typeof value !== \"string\") return value;\n if (!value.includes(\"!\")) return value.trim();\n return value.replace(importantRegex, \"\").trim();\n}\nfunction withoutSpace(str) {\n if (typeof str !== \"string\") return str;\n return str.includes(\" \") ? str.replaceAll(\" \", \"_\") : str;\n}\n//#endregion\n//#region src/memo.ts\n/**\n* Bounded argument memo used by the generated runtime (`css`, patterns, `cva`, recipes).\n*\n* Two regimes, picked per call:\n*\n* - Arguments that are flat (objects of primitives) take a cheap structural hash\n* and are confirmed with an exact comparison, so a hash collision can never\n* serve the wrong result. This is the shape `css({ ... })` has.\n* - Anything nested falls back to `JSON.stringify`, which V8 does faster than a\n* JS walk.\n*\n* The second point is the counter-intuitive one, and it has been measured rather\n* than assumed. Extending the structural hash to recurse — so nested styles could\n* take the fast path too — is *slower*, because it trades one native serialization\n* for two JS walks (hash, then the deep equality that confirms it). Over 10k\n* iterations per shape:\n*\n* shape stringify recursive hash + deep equal\n* flat 1.06ms 2.09ms\n* _hover 1.00ms 2.16ms\n* responsive 1.23ms 2.15ms\n* realistic 2.32ms 5.84ms\n* nested 3 deep 1.22ms 2.35ms\n*\n* So a nested `css()` call costing several times a flat one is not a defect here.\n* It is the floor for a value-keyed memo in JS, and the way to avoid it is to not\n* make the call — see the build-time fold in `@bamboocss/vite`.\n*\n* Both regimes key on *values*, never on object identity: mutating a style object\n* between calls changes its hash, so the next call misses and recomputes rather\n* than serving a stale class. Keying nested arguments on the identity of the inner\n* objects would skip serialization entirely, but it cannot detect a mutation, and\n* \"same object, different contents\" is exactly what a style object built per render\n* looks like.\n*\n* Both caches are bounded. An unbounded memo is a leak in any long-lived process\n* (SSR), where the set of distinct style objects grows without limit.\n*/\n/**\n* Distinct hashes held per memoized function before the cache rotates.\n*\n* This bounds *buckets*, not entries: a bucket keeps up to `MAX_BUCKET` colliding\n* argument lists, so the ceiling is `MAX_ENTRIES * MAX_BUCKET` live entries, and\n* twice that across both generations, since the previous one is retained until the\n* next rotation. Collisions are rare in practice, so the realistic figure is close\n* to `MAX_ENTRIES` — but the worst case is what matters when sizing a long-lived\n* process, so state it plainly.\n*\n* Rotation beats evicting the oldest key: single-key eviction is worst-case for a\n* working set that cycles, because it drops exactly the entry about to be needed.\n* Measured on a cycling set of 20k styles, one-at-a-time eviction cost ~719ns/op\n* against ~189ns unbounded, while rotation holds ~274ns. On realistic skewed\n* access rotation is at or below the unbounded cost.\n*/\nconst MAX_ENTRIES = 1e3;\n/** Entries kept per hash bucket, to bound the cost of a collision scan. */\nconst MAX_BUCKET = 8;\n/**\n* DJB2 over the arguments' own keys and primitive values.\n* Returns `null` for anything nested, which routes the call to the string key.\n*/\nconst flatHashOrNull = (args) => {\n let h = 5381;\n for (let a = 0; a < args.length; a++) {\n const obj = args[a];\n if (obj === null || typeof obj !== \"object\") {\n const t = typeof obj;\n if (t === \"string\") for (let i = 0; i < obj.length; i++) h = h * 33 ^ obj.charCodeAt(i);\n else if (t === \"number\") h = h * 33 ^ (obj | 0);\n else if (t === \"boolean\") h = h * 33 ^ (obj ? 991 : 997);\n else h = h * 33 ^ 3;\n continue;\n }\n if (Array.isArray(obj)) h = h * 33 ^ 7;\n else {\n const proto = Object.getPrototypeOf(obj);\n if (proto !== Object.prototype && proto !== null) return null;\n }\n for (const k in obj) {\n const v = obj[k];\n const tv = typeof v;\n if (v !== null && tv === \"object\") return null;\n for (let i = 0; i < k.length; i++) h = h * 33 ^ k.charCodeAt(i);\n if (tv === \"string\") for (let i = 0; i < v.length; i++) h = h * 33 ^ v.charCodeAt(i);\n else if (tv === \"number\") h = h * 33 ^ (v | 0);\n else if (tv === \"boolean\") h = h * 33 ^ (v ? 991 : 997);\n else h = h * 33 ^ 2;\n }\n }\n return h >>> 0;\n};\n/**\n* Value snapshot of the arguments, taken once at insert.\n*\n* The cache must not hold the caller's objects: a style object can capture a much\n* larger graph, and keeping it alive until the cache rotates changes GC behaviour\n* for code that never asked to be cached. Only the flat path reaches here, so a\n* shallow copy contains primitives only and retains nothing.\n*\n* Comparing against a copy also removes the last way a mutation could be missed.\n* Were the caller's own object stored, `oa === ob` would short-circuit the value\n* comparison, and a mutation that happened to preserve the hash would return the\n* stale entry. Against a copy that check can only ever be true for equal\n* primitives.\n*/\nconst snapshotArgs = (args) => {\n const values = [];\n const counts = [];\n for (let i = 0; i < args.length; i++) {\n const o = args[i];\n if (o !== null && typeof o === \"object\") {\n const copy = Array.isArray(o) ? [] : {};\n let n = 0;\n for (const k in o) {\n copy[k] = o[k];\n n++;\n }\n values.push(copy);\n counts.push(n);\n } else {\n values.push(o);\n counts.push(0);\n }\n }\n return {\n values,\n counts\n };\n};\n/**\n* Exact match, so a `flatHashOrNull` collision is resolved rather than trusted.\n* `bCounts` is the cached side's key count; comparing against it avoids the\n* `Object.keys()` allocation this would otherwise make on every cache hit.\n*/\nconst flatArgsEqual = (a, b, bCounts) => {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n const oa = a[i];\n const ob = b[i];\n if (oa === ob) continue;\n if (oa === null || ob === null || typeof oa !== \"object\" || typeof ob !== \"object\") return false;\n if (Array.isArray(oa) !== Array.isArray(ob)) return false;\n let n = 0;\n for (const k in oa) {\n if (oa[k] !== ob[k]) return false;\n n++;\n }\n if (n !== bCounts[i]) return false;\n }\n return true;\n};\nconst memo = (fn) => {\n let buckets = /* @__PURE__ */ new Map();\n let priorBuckets = /* @__PURE__ */ new Map();\n let strings = /* @__PURE__ */ new Map();\n let priorStrings = /* @__PURE__ */ new Map();\n /**\n * One scalar argument, keyed directly.\n *\n * This is the shape of the hottest callers — `isCssProperty(prop)` runs per prop\n * per render — and a plain map lookup beats hashing, bucket scanning and\n * snapshotting for it. Distinct types stay distinct keys, so `1` and `'1'` do not\n * share an entry.\n */\n let scalars = /* @__PURE__ */ new Map();\n let priorScalars = /* @__PURE__ */ new Map();\n const scan = (bucket, args) => {\n if (bucket) for (let i = 0; i < bucket.length; i++) {\n const entry = bucket[i];\n if (flatArgsEqual(args, entry.values, entry.counts)) return entry;\n }\n };\n const get = (...args) => {\n if (args.length === 1) {\n const only = args[0];\n if (only === null || typeof only !== \"object\") {\n if (scalars.has(only)) return scalars.get(only);\n if (priorScalars.has(only)) {\n const promoted = priorScalars.get(only);\n scalars.set(only, promoted);\n return promoted;\n }\n const out = fn(only);\n scalars.set(only, out);\n if (scalars.size > MAX_ENTRIES) {\n priorScalars = scalars;\n scalars = /* @__PURE__ */ new Map();\n }\n return out;\n }\n }\n const hash = flatHashOrNull(args);\n if (hash !== null) {\n let bucket = buckets.get(hash);\n const hit = scan(bucket, args);\n if (hit) return hit.out;\n const priorHit = scan(priorBuckets.get(hash), args);\n if (priorHit) {\n if (!bucket) {\n bucket = [];\n buckets.set(hash, bucket);\n }\n bucket.push(priorHit);\n if (bucket.length > MAX_BUCKET) bucket.shift();\n return priorHit.out;\n }\n const snap = snapshotArgs(args);\n const out = fn(...args);\n if (!bucket) {\n bucket = [];\n buckets.set(hash, bucket);\n }\n bucket.push({\n values: snap.values,\n counts: snap.counts,\n out\n });\n if (bucket.length > MAX_BUCKET) bucket.shift();\n if (buckets.size > MAX_ENTRIES) {\n priorBuckets = buckets;\n buckets = /* @__PURE__ */ new Map();\n }\n return out;\n }\n const key = JSON.stringify(args);\n if (strings.has(key)) return strings.get(key);\n if (priorStrings.has(key)) {\n const promoted = priorStrings.get(key);\n strings.set(key, promoted);\n return promoted;\n }\n const out = fn(...args);\n strings.set(key, out);\n if (strings.size > MAX_ENTRIES) {\n priorStrings = strings;\n strings = /* @__PURE__ */ new Map();\n }\n return out;\n };\n return get;\n};\n//#endregion\n//#region src/merge-props.ts\nconst MERGE_OMIT = new Set([\n \"__proto__\",\n \"constructor\",\n \"prototype\"\n]);\nfunction mergeProps(...sources) {\n return sources.reduce((prev, obj) => {\n if (!obj) return prev;\n Object.keys(obj).forEach((key) => {\n if (MERGE_OMIT.has(key)) return;\n const prevValue = prev[key];\n const value = obj[key];\n if (isObject(prevValue) && isObject(value)) prev[key] = mergeProps(prevValue, value);\n else prev[key] = value;\n });\n return prev;\n }, {});\n}\n//#endregion\n//#region src/walk-object.ts\nconst isNotNullish = (element) => element != null;\nfunction walkObject(target, predicate, options = {}) {\n const { stop, getKey } = options;\n function inner(value, path = []) {\n if (isObjectOrArray(value)) {\n const result = {};\n for (const [prop, child] of Object.entries(value)) {\n const key = getKey?.(prop, child) ?? prop;\n const childPath = [...path, key];\n if (stop?.(value, childPath)) return predicate(value, path);\n const next = inner(child, childPath);\n if (isNotNullish(next)) result[key] = next;\n }\n return result;\n }\n return predicate(value, path);\n }\n return inner(target);\n}\nfunction mapObject(obj, fn) {\n if (Array.isArray(obj)) return obj.map((value) => fn(value));\n if (!isObject(obj)) return fn(obj);\n return walkObject(obj, (value) => fn(value));\n}\n//#endregion\n//#region src/normalize-style-object.ts\nfunction toResponsiveObject(values, breakpoints) {\n return values.reduce((acc, current, index) => {\n const key = breakpoints[index];\n if (current != null) acc[key] = current;\n return acc;\n }, {});\n}\n/**\n* Whether walking the object would only rebuild it.\n*\n* Normalizing does three things: it renames a shorthand to its longhand, expands a responsive\n* array into a breakpoint object, and drops nullish leaves. A flat object of plain values\n* written in longhand needs none of them, and that is most of what `css()` is handed — but it\n* still paid for a full rebuild plus a path array per key.\n*\n* Every clause has to be exact, since a false positive returns an object the walk would have\n* changed. Nullish is one of them: a leaf the walk removes must not survive, or a later merge\n* would see it override the value beneath it. The array check is another, and it is on the\n* container as well as the values — `stop` is handed the container, so an array arriving at\n* the top level becomes a breakpoint object rather than being walked into.\n*\n* `for...in` reads inherited keys the walk ignores, which is safe in the only direction it can\n* be wrong — an extra key can send this to the slow path, never past it.\n*\n* It does read every value, as `compactStyles` and the argument memo already do, so an\n* accessor prop is read once more than before. Style props are values by the time they get\n* here and reading one has no effect, but it is the reason this cannot be reordered to read\n* lazily.\n*/\nfunction needsNoNormalizing(styles, resolveShorthand) {\n if (Array.isArray(styles)) return false;\n for (const key in styles) {\n const value = styles[key];\n if (value == null || typeof value === \"object\") return false;\n if (resolveShorthand !== void 0 && resolveShorthand(key) !== key) return false;\n }\n return true;\n}\n/**\n* The result may be the argument itself rather than a fresh object, so callers have to treat\n* it as read-only. Every one of them does today: merging accumulates into its own object and\n* the two `raw()` helpers clone at the boundary.\n*/\nfunction normalizeStyleObject(styles, context, shorthand = true) {\n const { utility, conditions } = context;\n const { hasShorthand, resolveShorthand } = utility;\n if (needsNoNormalizing(styles, shorthand && hasShorthand ? resolveShorthand : void 0)) return styles;\n return walkObject(styles, (value) => {\n return Array.isArray(value) ? toResponsiveObject(value, conditions.breakpoints.keys) : value;\n }, {\n stop: (value) => Array.isArray(value),\n getKey: shorthand ? (prop) => hasShorthand ? resolveShorthand(prop) : prop : void 0\n });\n}\n//#endregion\n//#region src/classname.ts\nconst fallbackCondition = {\n shift: (v) => v,\n finalize: (v) => v,\n breakpoints: { keys: [] }\n};\nconst ENTRY_SEP = \"]___[\";\nconst COND_SEP = \"<___>\";\nfunction createCss(context) {\n const { utility, hash, grouped, conditions: conds = fallbackCondition } = context;\n const { prefix } = utility;\n const formatClassName = prefix ? (str) => str ? `${prefix}-${str}` : prefix : (str) => str || \"\";\n const hashFn = (conditions, className) => {\n if (hash) {\n const baseArray = [...conds.finalize(conditions), className];\n return formatClassName(utility.toHash(baseArray, toHash));\n }\n const finalized = conds.finalize(conditions);\n if (finalized.length === 0) return formatClassName(className);\n return [...finalized, formatClassName(className)].join(\":\");\n };\n if (grouped) return memo(({ base, ...styles } = {}) => {\n const normalizedObject = normalizeStyleObject(Object.assign(styles, base), context);\n const hashes = [];\n walkObject(normalizedObject, (value, paths) => {\n if (value == null) return;\n const [prop, ...allConditions] = conds.shift(paths);\n const conditions = filterBaseConditions(allConditions);\n const parts = [`${prop}${ENTRY_SEP}value:${value}`];\n if (conditions.length) parts.push(`cond:${conditions.join(COND_SEP)}`);\n hashes.push(parts.join(ENTRY_SEP));\n });\n if (hashes.length === 0) return \"\";\n hashes.sort();\n const groupId = hashes.join(\"|\");\n return formatClassName(utility.toHash([\"grouped\", groupId], toHash));\n });\n return memo(({ base, ...styles } = {}) => {\n const normalizedObject = normalizeStyleObject(Object.assign(styles, base), context);\n const classNames = /* @__PURE__ */ new Set();\n walkObject(normalizedObject, (value, paths) => {\n if (value == null) return;\n const important = isImportant(value);\n const [prop, ...allConditions] = conds.shift(paths);\n let className = hashFn(filterBaseConditions(allConditions), utility.transform(prop, withoutImportant(sanitize(value))).className);\n if (important) className = `${className}!`;\n classNames.add(className);\n });\n return Array.from(classNames).join(\" \");\n });\n}\n/**\n* Whether a style object carries anything `compact` would have kept.\n*\n* The question `compactStyles` asks is only ever \"is this empty once undefined values are\n* dropped\", but it used to answer it by building the compacted object and then a key array\n* for it, then throwing both away. `Object.keys` enumerates exactly what `compact`'s\n* `Object.entries` did — own, enumerable, string-keyed — so this is the same predicate\n* without the two allocations, and it stops at the first value that settles it.\n*/\nfunction hasDefinedValue(style) {\n const keys = Object.keys(style);\n for (let i = 0; i < keys.length; i++) if (style[keys[i]] !== void 0) return true;\n return false;\n}\nfunction compactStyles(...styles) {\n return styles.flat().filter((style) => isObject(style) && hasDefinedValue(style));\n}\nfunction createMergeCss(context) {\n function resolve(styles) {\n const allStyles = compactStyles(...styles);\n if (allStyles.length === 1) return allStyles;\n return allStyles.map((style) => normalizeStyleObject(style, context));\n }\n function mergeCss(...styles) {\n return mergeProps(...resolve(styles));\n }\n function assignCss(...styles) {\n return Object.assign({}, ...resolve(styles));\n }\n return {\n mergeCss: memo(mergeCss),\n assignCss\n };\n}\n//#endregion\n//#region src/clone-styles.ts\nconst OMIT = new Set([\n \"__proto__\",\n \"constructor\",\n \"prototype\"\n]);\n/**\n* Independent copy of a style object, nested condition blocks included.\n*\n* Merged style objects are cached, so anything handed to user code has to be\n* copied first: a caller mutating what it received would otherwise change what\n* every later caller reads back. `css.raw()` and `cva.raw()` are those boundaries.\n*\n* Kept separate from `mergeProps` deliberately. Merging is on the hot path — it\n* runs on every `css()` cache miss and on every render of a pattern component\n* under `jsxStyleProps: 'minimal'` — and copying there charges every caller for a\n* guarantee only the two `raw()` helpers need. Measured on a realistic style\n* object (5 base properties, 4 condition blocks) that was roughly twice the cost\n* of merging alone.\n*/\nfunction cloneStyles(styles) {\n if (Array.isArray(styles)) return styles.map((value) => cloneStyles(value));\n if (!isObject(styles)) return styles;\n const out = {};\n for (const key of Object.keys(styles)) {\n if (OMIT.has(key)) continue;\n out[key] = cloneStyles(styles[key]);\n }\n return out;\n}\n//#endregion\n//#region src/compact.ts\nfunction compact(value) {\n return Object.fromEntries(Object.entries(value ?? {}).filter(([_, value]) => value !== void 0));\n}\n//#endregion\n//#region src/leaf-class.ts\n/**\n* The class a single dynamic style leaf resolves to, given the prefix its property and\n* condition path produce.\n*\n* ## Why this can exist at all\n*\n* `css()` builds a class from the value alone — `utility.transform` is string\n* construction over a static map, and nothing consults which rules were actually emitted.\n* So `css({ color: tone })` already returns `c_<tone>` for a value the extractor never\n* saw, with no CSS behind it. Reproducing that string here cannot be less correct than\n* the call it replaces; it just skips the object literal, the merge and the memo.\n*\n* ## Why it is not a template literal\n*\n* Three shapes do not reduce to `prefix + value`, and all three return `undefined` so the\n* caller runs `css()` instead:\n*\n* - An array is expanded to a responsive object by `normalizeStyleObject`, so it produces\n* one class per breakpoint rather than one class.\n* - An object is a condition block, walked into for the same reason.\n* - `null` and `undefined` are skipped by the walk entirely, which is an empty string\n* rather than a class — that one is answered here, since it needs no `css()` call.\n*\n* ## Why the character scan\n*\n* The remaining work — collapsing whitespace, stripping `!important`, turning spaces into\n* underscores — is three regexes, and paying them per call makes this *slower* than a\n* memo hit. Almost no token value contains whitespace or `!`, so one scan for the\n* characters that make any of it necessary sends the common value straight to a\n* concatenation. A false positive only costs the slow path, so the scan errs wide.\n*/\nfunction leafClass(prefix, value) {\n if (value == null) return \"\";\n const type = typeof value;\n if (type === \"number\" || type === \"boolean\") return `${prefix}${value}`;\n if (type !== \"string\") return void 0;\n const str = value;\n for (let index = 0; index < str.length; index++) {\n const code = str.charCodeAt(index);\n if (code <= 33 || code === 160 || code === 5760 || code >= 8192) return slowLeaf(prefix, str);\n }\n return `${prefix}${str}`;\n}\n/** The full pipeline `createCss` runs, for a value that needs it. */\nfunction slowLeaf(prefix, value) {\n const important = isImportant(value);\n const className = `${prefix}${withoutSpace(withoutImportant(sanitize(value)))}`;\n return important ? `${className}!` : className;\n}\n//#endregion\n//#region src/hypenate-property.ts\nconst wordRegex = /([A-Z])/g;\nconst msRegex = /^ms-/;\nconst hypenateProperty = memo((property) => {\n if (property.startsWith(\"--\")) return property;\n return property.replace(wordRegex, \"-$1\").replace(msRegex, \"-ms-\").toLowerCase();\n});\n//#endregion\n//#region src/is-css-function.ts\nconst fnRegExp = new RegExp(`^(${[\n \"min\",\n \"max\",\n \"clamp\",\n \"calc\"\n].join(\"|\")})\\\\(.*\\\\)`);\nconst isCssFunction = (v) => typeof v === \"string\" && fnRegExp.test(v);\n//#endregion\n//#region src/is-css-unit.ts\nconst lengthUnitsPattern = `(?:${\"cm,mm,Q,in,pc,pt,px,em,ex,ch,rem,lh,rlh,vw,vh,vmin,vmax,vb,vi,svw,svh,lvw,lvh,dvw,dvh,cqw,cqh,cqi,cqb,cqmin,cqmax,%\".split(\",\").join(\"|\")})`;\nconst lengthRegExp = new RegExp(`^[+-]?[0-9]*.?[0-9]+(?:[eE][+-]?[0-9]+)?${lengthUnitsPattern}$`);\nconst isCssUnit = (v) => typeof v === \"string\" && lengthRegExp.test(v);\n//#endregion\n//#region src/is-css-var.ts\nconst isCssVar = (v) => typeof v === \"string\" && /^var\\(--.+\\)$/.test(v);\n//#endregion\n//#region src/pattern-fns.ts\nconst patternFns = {\n map: mapObject,\n isCssFunction,\n isCssVar,\n isCssUnit\n};\nconst getPatternStyles = (pattern, styles) => {\n if (!pattern?.defaultValues) return styles;\n const defaults = typeof pattern.defaultValues === \"function\" ? pattern.defaultValues(styles) : pattern.defaultValues;\n return Object.assign({}, defaults, compact(styles));\n};\n//#endregion\n//#region src/slot.ts\nconst getSlotRecipes = (recipe = {}) => {\n const init = (slot) => ({\n className: [recipe.className, slot].filter(Boolean).join(\"__\"),\n base: recipe.base?.[slot] ?? {},\n variants: {},\n defaultVariants: recipe.defaultVariants ?? {},\n compoundVariants: recipe.compoundVariants ? getSlotCompoundVariant(recipe.compoundVariants, slot) : []\n });\n const recipeParts = (recipe.slots ?? []).map((slot) => [slot, init(slot)]);\n for (const [variantsKey, variantsSpec] of Object.entries(recipe.variants ?? {})) for (const [variantKey, variantSpec] of Object.entries(variantsSpec)) recipeParts.forEach(([slot, slotRecipe]) => {\n slotRecipe.variants[variantsKey] ??= {};\n slotRecipe.variants[variantsKey][variantKey] = variantSpec[slot] ?? {};\n });\n return Object.fromEntries(recipeParts);\n};\nconst getSlotCompoundVariant = (compoundVariants, slotName) => compoundVariants.filter((compoundVariant) => compoundVariant.css[slotName]).map((compoundVariant) => ({\n ...compoundVariant,\n css: compoundVariant.css[slotName]\n}));\n//#endregion\n//#region src/split-props.ts\n/**\n* Deal a props object into one bucket per key group, plus a final bucket for the rest.\n* A key goes to the first group that claims it.\n*\n* ## Why the descriptor is read per key rather than in bulk\n*\n* This used to call `Object.getOwnPropertyDescriptors` for the whole object and\n* `defineProperty` for every key it moved. Copying plain values instead is 2.4–2.9x faster\n* on the shapes that allow it, but it is only correct where props are data — and they are\n* not always. Solid compiles props to accessors, so reading one eagerly runs whatever it\n* wraps: splitting a component's props would construct its children before the surrounding\n* provider exists.\n*\n* So the descriptor is fetched per key, and the value path is taken only when it changes\n* nothing observable. An accessor keeps its laziness, a non-enumerable key keeps its\n* invisibility, and `__proto__` is defined rather than assigned so it stays an own\n* property instead of reaching the prototype setter.\n*\n* The one thing the value path drops is `writable`/`configurable`, so a bucket key taken\n* from frozen props is writable where it used to be frozen. Nothing here relies on that,\n* and preserving it would mean `defineProperty` on the common path — the cost this exists\n* to avoid. Keys that take the descriptor path keep theirs, so a bucket can be\n* inconsistent in that one respect.\n*\n* Key order within a bucket is preserved exactly. It is not cosmetic: `cva` merges\n* variant props in iteration order, and the parser reads the rest bucket as the style\n* props it encodes, so order reaches the emitted CSS.\n*/\nfunction splitProps(props, ...keys) {\n const allKeys = Object.getOwnPropertyNames(props);\n const own = new Set(allKeys);\n const taken = /* @__PURE__ */ new Set();\n const split = (group) => {\n const clone = {};\n for (let i = 0; i < group.length; i++) {\n const key = group[i];\n if (taken.has(key) || !own.has(key)) continue;\n const descriptor = Object.getOwnPropertyDescriptor(props, key);\n if (!descriptor) continue;\n if (\"get\" in descriptor || \"set\" in descriptor || !descriptor.enumerable || key === \"__proto__\") Object.defineProperty(clone, key, descriptor);\n else clone[key] = descriptor.value;\n taken.add(key);\n }\n return clone;\n };\n /**\n * The predicate is called with the key alone.\n *\n * Handing it to `filter` passes `(key, index, allKeys)`. A one-parameter predicate cannot\n * see the extras, but a memoized one reads its whole argument list — and the predicates\n * that arrive here are memoized, `isCssProperty` among them. So the memo hashed the entire\n * key array once per prop, and keyed its cache on it: two elements with different prop sets\n * shared no entry even for the same prop name.\n *\n * Worth ~9.7x on that path, and nothing at all on a plain predicate — which is why the\n * bench below it needs a memoized case to see this at all.\n *\n * A loop rather than `filter((k) => key(k))` because the wrapper allocates a closure per\n * group. The two measure the same to within noise; the loop just does not need one.\n */\n const matching = (predicate) => {\n const group = [];\n for (let i = 0; i < allKeys.length; i++) {\n const key = allKeys[i];\n if (predicate(key)) group.push(key);\n }\n return group;\n };\n return keys.map((key) => split(Array.isArray(key) ? key : matching(key))).concat(split(allKeys));\n}\n//#endregion\n//#region src/uniq.ts\nconst uniq = (...items) => {\n const set = items.reduce((acc, currItems) => {\n if (currItems) currItems.forEach((item) => acc.add(item));\n return acc;\n }, /* @__PURE__ */ new Set([]));\n return Array.from(set);\n};\n//#endregion\nexport { cloneStyles, compact, createCss, createMergeCss, filterBaseConditions, getPatternStyles, getSlotCompoundVariant, getSlotRecipes, hypenateProperty, isBaseCondition, isObject, leafClass, mapObject, memo, mergeProps, patternFns, splitProps, toHash, uniq, walkObject, withoutSpace };\n";
|
|
646
|
+
var content$10 = "//#region src/assert.ts\nfunction isObject(value) {\n return typeof value === \"object\" && value != null && !Array.isArray(value);\n}\nconst isObjectOrArray = (obj) => typeof obj === \"object\" && obj !== null;\n//#endregion\n//#region src/condition.ts\nconst isBaseCondition = (v) => v === \"base\";\nfunction filterBaseConditions(c) {\n return c.slice().filter((v) => !isBaseCondition(v));\n}\n//#endregion\n//#region src/hash.ts\nfunction toChar(code) {\n return String.fromCharCode(code + (code > 25 ? 39 : 97));\n}\nfunction toName(code) {\n let name = \"\";\n let x;\n for (x = Math.abs(code); x > 52; x = x / 52 | 0) name = toChar(x % 52) + name;\n return toChar(x % 52) + name;\n}\nfunction toPhash(h, x) {\n let i = x.length;\n while (i) h = h * 33 ^ x.charCodeAt(--i);\n return h;\n}\nfunction toHash(value) {\n return toName(toPhash(5381, value) >>> 0);\n}\n//#endregion\n//#region src/important.ts\nconst importantRegex = /\\s*!(important)?/i;\nconst whitespaceRegex = /\\s/;\n/**\n* Collapse every run of whitespace to a single space, which is what the class name is\n* built from. Exported because `leafClass` has to reproduce this exact pipeline, and a\n* second copy of it would be free to drift from the one `createCss` runs.\n*/\nfunction sanitize(value) {\n if (typeof value !== \"string\") return value;\n const collapsed = whitespaceRegex.test(value) ? value.replaceAll(/[\\n\\s]+/g, \" \") : value;\n return collapsed.includes(\"\\0\") ? collapsed.replaceAll(\"\\0\", \"\") : collapsed;\n}\nfunction isImportant(value) {\n if (typeof value !== \"string\") return false;\n return value.includes(\"!\") && importantRegex.test(value);\n}\nfunction withoutImportant(value) {\n if (typeof value !== \"string\") return value;\n if (!value.includes(\"!\")) return value.trim();\n return value.replace(importantRegex, \"\").trim();\n}\nfunction withoutSpace(str) {\n if (typeof str !== \"string\") return str;\n return str.includes(\" \") ? str.replaceAll(\" \", \"_\") : str;\n}\n//#endregion\n//#region src/memo.ts\n/**\n* Bounded argument memo used by the generated runtime (`css`, patterns, `cva`, recipes).\n*\n* Two regimes, picked per call:\n*\n* - Arguments that are flat (objects of primitives) take a cheap structural hash\n* and are confirmed with an exact comparison, so a hash collision can never\n* serve the wrong result. This is the shape `css({ ... })` has.\n* - Anything nested falls back to `JSON.stringify`, which V8 does faster than a\n* JS walk.\n*\n* The second point is the counter-intuitive one, and it has been measured rather\n* than assumed. Extending the structural hash to recurse — so nested styles could\n* take the fast path too — is *slower*, because it trades one native serialization\n* for two JS walks (hash, then the deep equality that confirms it). Over 10k\n* iterations per shape:\n*\n* shape stringify recursive hash + deep equal\n* flat 1.06ms 2.09ms\n* _hover 1.00ms 2.16ms\n* responsive 1.23ms 2.15ms\n* realistic 2.32ms 5.84ms\n* nested 3 deep 1.22ms 2.35ms\n*\n* So a nested `css()` call costing several times a flat one is not a defect here.\n* It is the floor for a value-keyed memo in JS, and the way to avoid it is to not\n* make the call — see the build-time fold in `@bamboocss/vite`.\n*\n* Both regimes key on *values*, never on object identity: mutating a style object\n* between calls changes its hash, so the next call misses and recomputes rather\n* than serving a stale class. Keying nested arguments on the identity of the inner\n* objects would skip serialization entirely, but it cannot detect a mutation, and\n* \"same object, different contents\" is exactly what a style object built per render\n* looks like.\n*\n* Both caches are bounded. An unbounded memo is a leak in any long-lived process\n* (SSR), where the set of distinct style objects grows without limit.\n*/\n/**\n* Distinct hashes held per memoized function before the cache rotates.\n*\n* This bounds *buckets*, not entries: a bucket keeps up to `MAX_BUCKET` colliding\n* argument lists, so the ceiling is `MAX_ENTRIES * MAX_BUCKET` live entries, and\n* twice that across both generations, since the previous one is retained until the\n* next rotation. Collisions are rare in practice, so the realistic figure is close\n* to `MAX_ENTRIES` — but the worst case is what matters when sizing a long-lived\n* process, so state it plainly.\n*\n* Rotation beats evicting the oldest key: single-key eviction is worst-case for a\n* working set that cycles, because it drops exactly the entry about to be needed.\n* Measured on a cycling set of 20k styles, one-at-a-time eviction cost ~719ns/op\n* against ~189ns unbounded, while rotation holds ~274ns. On realistic skewed\n* access rotation is at or below the unbounded cost.\n*/\nconst MAX_ENTRIES = 1e3;\n/** Entries kept per hash bucket, to bound the cost of a collision scan. */\nconst MAX_BUCKET = 8;\n/**\n* DJB2 over the arguments' own keys and primitive values.\n* Returns `null` for anything nested, which routes the call to the string key.\n*/\nconst flatHashOrNull = (args) => {\n let h = 5381;\n for (let a = 0; a < args.length; a++) {\n const obj = args[a];\n if (obj === null || typeof obj !== \"object\") {\n const t = typeof obj;\n if (t === \"string\") for (let i = 0; i < obj.length; i++) h = h * 33 ^ obj.charCodeAt(i);\n else if (t === \"number\") h = h * 33 ^ (obj | 0);\n else if (t === \"boolean\") h = h * 33 ^ (obj ? 991 : 997);\n else h = h * 33 ^ 3;\n continue;\n }\n if (Array.isArray(obj)) h = h * 33 ^ 7;\n else {\n const proto = Object.getPrototypeOf(obj);\n if (proto !== Object.prototype && proto !== null) return null;\n }\n for (const k in obj) {\n const v = obj[k];\n const tv = typeof v;\n if (v !== null && tv === \"object\") return null;\n for (let i = 0; i < k.length; i++) h = h * 33 ^ k.charCodeAt(i);\n if (tv === \"string\") for (let i = 0; i < v.length; i++) h = h * 33 ^ v.charCodeAt(i);\n else if (tv === \"number\") h = h * 33 ^ (v | 0);\n else if (tv === \"boolean\") h = h * 33 ^ (v ? 991 : 997);\n else h = h * 33 ^ 2;\n }\n }\n return h >>> 0;\n};\n/**\n* Value snapshot of the arguments, taken once at insert.\n*\n* The cache must not hold the caller's objects: a style object can capture a much\n* larger graph, and keeping it alive until the cache rotates changes GC behaviour\n* for code that never asked to be cached. Only the flat path reaches here, so a\n* shallow copy contains primitives only and retains nothing.\n*\n* Comparing against a copy also removes the last way a mutation could be missed.\n* Were the caller's own object stored, `oa === ob` would short-circuit the value\n* comparison, and a mutation that happened to preserve the hash would return the\n* stale entry. Against a copy that check can only ever be true for equal\n* primitives.\n*/\nconst snapshotArgs = (args) => {\n const values = [];\n const counts = [];\n for (let i = 0; i < args.length; i++) {\n const o = args[i];\n if (o !== null && typeof o === \"object\") {\n const copy = Array.isArray(o) ? [] : {};\n let n = 0;\n for (const k in o) {\n copy[k] = o[k];\n n++;\n }\n values.push(copy);\n counts.push(n);\n } else {\n values.push(o);\n counts.push(0);\n }\n }\n return {\n values,\n counts\n };\n};\n/**\n* Exact match, so a `flatHashOrNull` collision is resolved rather than trusted.\n* `bCounts` is the cached side's key count; comparing against it avoids the\n* `Object.keys()` allocation this would otherwise make on every cache hit.\n*/\nconst flatArgsEqual = (a, b, bCounts) => {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n const oa = a[i];\n const ob = b[i];\n if (oa === ob) continue;\n if (oa === null || ob === null || typeof oa !== \"object\" || typeof ob !== \"object\") return false;\n if (Array.isArray(oa) !== Array.isArray(ob)) return false;\n let n = 0;\n for (const k in oa) {\n if (oa[k] !== ob[k]) return false;\n n++;\n }\n if (n !== bCounts[i]) return false;\n }\n return true;\n};\nconst memo = (fn) => {\n let buckets = /* @__PURE__ */ new Map();\n let priorBuckets = /* @__PURE__ */ new Map();\n let strings = /* @__PURE__ */ new Map();\n let priorStrings = /* @__PURE__ */ new Map();\n /**\n * One scalar argument, keyed directly.\n *\n * This is the shape of the hottest callers — `isCssProperty(prop)` runs per prop\n * per render — and a plain map lookup beats hashing, bucket scanning and\n * snapshotting for it. Distinct types stay distinct keys, so `1` and `'1'` do not\n * share an entry.\n */\n let scalars = /* @__PURE__ */ new Map();\n let priorScalars = /* @__PURE__ */ new Map();\n const scan = (bucket, args) => {\n if (bucket) for (let i = 0; i < bucket.length; i++) {\n const entry = bucket[i];\n if (flatArgsEqual(args, entry.values, entry.counts)) return entry;\n }\n };\n const get = (...args) => {\n if (args.length === 1) {\n const only = args[0];\n if (only === null || typeof only !== \"object\") {\n if (scalars.has(only)) return scalars.get(only);\n if (priorScalars.has(only)) {\n const promoted = priorScalars.get(only);\n scalars.set(only, promoted);\n return promoted;\n }\n const out = fn(only);\n scalars.set(only, out);\n if (scalars.size > MAX_ENTRIES) {\n priorScalars = scalars;\n scalars = /* @__PURE__ */ new Map();\n }\n return out;\n }\n }\n const hash = flatHashOrNull(args);\n if (hash !== null) {\n let bucket = buckets.get(hash);\n const hit = scan(bucket, args);\n if (hit) return hit.out;\n const priorHit = scan(priorBuckets.get(hash), args);\n if (priorHit) {\n if (!bucket) {\n bucket = [];\n buckets.set(hash, bucket);\n }\n bucket.push(priorHit);\n if (bucket.length > MAX_BUCKET) bucket.shift();\n return priorHit.out;\n }\n const snap = snapshotArgs(args);\n const out = fn(...args);\n if (!bucket) {\n bucket = [];\n buckets.set(hash, bucket);\n }\n bucket.push({\n values: snap.values,\n counts: snap.counts,\n out\n });\n if (bucket.length > MAX_BUCKET) bucket.shift();\n if (buckets.size > MAX_ENTRIES) {\n priorBuckets = buckets;\n buckets = /* @__PURE__ */ new Map();\n }\n return out;\n }\n const key = JSON.stringify(args);\n if (strings.has(key)) return strings.get(key);\n if (priorStrings.has(key)) {\n const promoted = priorStrings.get(key);\n strings.set(key, promoted);\n return promoted;\n }\n const out = fn(...args);\n strings.set(key, out);\n if (strings.size > MAX_ENTRIES) {\n priorStrings = strings;\n strings = /* @__PURE__ */ new Map();\n }\n return out;\n };\n return get;\n};\n//#endregion\n//#region src/merge-props.ts\nconst MERGE_OMIT = new Set([\n \"__proto__\",\n \"constructor\",\n \"prototype\"\n]);\nfunction mergeProps(...sources) {\n return sources.reduce((prev, obj) => {\n if (!obj) return prev;\n Object.keys(obj).forEach((key) => {\n if (MERGE_OMIT.has(key)) return;\n const prevValue = prev[key];\n const value = obj[key];\n if (isObject(prevValue) && isObject(value)) prev[key] = mergeProps(prevValue, value);\n else prev[key] = value;\n });\n return prev;\n }, {});\n}\n//#endregion\n//#region src/walk-object.ts\nconst isNotNullish = (element) => element != null;\nfunction walkObject(target, predicate, options = {}) {\n const { stop, getKey } = options;\n function inner(value, path = []) {\n if (isObjectOrArray(value)) {\n const result = {};\n for (const [prop, child] of Object.entries(value)) {\n const key = getKey?.(prop, child) ?? prop;\n const childPath = [...path, key];\n if (stop?.(value, childPath)) return predicate(value, path);\n const next = inner(child, childPath);\n if (isNotNullish(next)) result[key] = next;\n }\n return result;\n }\n return predicate(value, path);\n }\n return inner(target);\n}\nfunction mapObject(obj, fn) {\n if (Array.isArray(obj)) return obj.map((value) => fn(value));\n if (!isObject(obj)) return fn(obj);\n return walkObject(obj, (value) => fn(value));\n}\n//#endregion\n//#region src/normalize-style-object.ts\nfunction toResponsiveObject(values, breakpoints) {\n return values.reduce((acc, current, index) => {\n const key = breakpoints[index];\n if (current != null) acc[key] = current;\n return acc;\n }, {});\n}\n/**\n* Whether walking the object would only rebuild it.\n*\n* Normalizing does three things: it renames a shorthand to its longhand, expands a responsive\n* array into a breakpoint object, and drops nullish leaves. A flat object of plain values\n* written in longhand needs none of them, and that is most of what `css()` is handed — but it\n* still paid for a full rebuild plus a path array per key.\n*\n* Every clause has to be exact, since a false positive returns an object the walk would have\n* changed. Nullish is one of them: a leaf the walk removes must not survive, or a later merge\n* would see it override the value beneath it. The array check is another, and it is on the\n* container as well as the values — `stop` is handed the container, so an array arriving at\n* the top level becomes a breakpoint object rather than being walked into.\n*\n* `for...in` reads inherited keys the walk ignores, which is safe in the only direction it can\n* be wrong — an extra key can send this to the slow path, never past it.\n*\n* It does read every value, as `compactStyles` and the argument memo already do, so an\n* accessor prop is read once more than before. Style props are values by the time they get\n* here and reading one has no effect, but it is the reason this cannot be reordered to read\n* lazily.\n*/\nfunction needsNoNormalizing(styles, resolveShorthand) {\n if (Array.isArray(styles)) return false;\n for (const key in styles) {\n const value = styles[key];\n if (value == null || typeof value === \"object\") return false;\n if (resolveShorthand !== void 0 && resolveShorthand(key) !== key) return false;\n }\n return true;\n}\n/**\n* The result may be the argument itself rather than a fresh object, so callers have to treat\n* it as read-only. Every one of them does today: merging accumulates into its own object and\n* the two `raw()` helpers clone at the boundary.\n*/\nfunction normalizeStyleObject(styles, context, shorthand = true) {\n const { utility, conditions } = context;\n const { hasShorthand, resolveShorthand } = utility;\n if (needsNoNormalizing(styles, shorthand && hasShorthand ? resolveShorthand : void 0)) return styles;\n return walkObject(styles, (value) => {\n return Array.isArray(value) ? toResponsiveObject(value, conditions.breakpoints.keys) : value;\n }, {\n stop: (value) => Array.isArray(value),\n getKey: shorthand ? (prop) => hasShorthand ? resolveShorthand(prop) : prop : void 0\n });\n}\n//#endregion\n//#region src/classname.ts\nconst fallbackCondition = {\n shift: (v) => v,\n finalize: (v) => v,\n breakpoints: { keys: [] }\n};\nconst ENTRY_SEP = \"]___[\";\nconst COND_SEP = \"<___>\";\nfunction createCss(context) {\n const { utility, hash, grouped, conditions: conds = fallbackCondition } = context;\n const { prefix } = utility;\n const formatClassName = prefix ? (str) => str ? `${prefix}-${str}` : prefix : (str) => str || \"\";\n const hashFn = (conditions, className) => {\n if (hash) {\n const baseArray = [...conds.finalize(conditions), className];\n return formatClassName(utility.toHash(baseArray, toHash));\n }\n const finalized = conds.finalize(conditions);\n if (finalized.length === 0) return formatClassName(className);\n return [...finalized, formatClassName(className)].join(\":\");\n };\n if (grouped) return memo(({ base, ...styles } = {}) => {\n const normalizedObject = normalizeStyleObject(Object.assign(styles, base), context);\n const hashes = [];\n walkObject(normalizedObject, (value, paths) => {\n if (value == null) return;\n const [prop, ...allConditions] = conds.shift(paths);\n const conditions = filterBaseConditions(allConditions);\n const parts = [`${prop}${ENTRY_SEP}value:${value}`];\n if (conditions.length) parts.push(`cond:${conditions.join(COND_SEP)}`);\n hashes.push(parts.join(ENTRY_SEP));\n });\n if (hashes.length === 0) return \"\";\n hashes.sort();\n const groupId = hashes.join(\"|\");\n return formatClassName(utility.toHash([\"grouped\", groupId], toHash));\n });\n return memo(({ base, ...styles } = {}) => {\n const normalizedObject = normalizeStyleObject(Object.assign(styles, base), context);\n const classNames = /* @__PURE__ */ new Set();\n walkObject(normalizedObject, (value, paths) => {\n if (value == null) return;\n const important = isImportant(value);\n const [prop, ...allConditions] = conds.shift(paths);\n let className = hashFn(filterBaseConditions(allConditions), utility.transform(prop, withoutImportant(sanitize(value))).className);\n if (important) className = `${className}!`;\n classNames.add(className);\n });\n return Array.from(classNames).join(\" \");\n });\n}\n/**\n* Whether a style object carries anything `compact` would have kept.\n*\n* The question `compactStyles` asks is only ever \"is this empty once undefined values are\n* dropped\", but it used to answer it by building the compacted object and then a key array\n* for it, then throwing both away. `Object.keys` enumerates exactly what `compact`'s\n* `Object.entries` did — own, enumerable, string-keyed — so this is the same predicate\n* without the two allocations, and it stops at the first value that settles it.\n*/\nfunction hasDefinedValue(style) {\n const keys = Object.keys(style);\n for (let i = 0; i < keys.length; i++) if (style[keys[i]] !== void 0) return true;\n return false;\n}\nfunction compactStyles(...styles) {\n return styles.flat().filter((style) => isObject(style) && hasDefinedValue(style));\n}\nfunction createMergeCss(context) {\n function resolve(styles) {\n const allStyles = compactStyles(...styles);\n if (allStyles.length === 1) return allStyles;\n return allStyles.map((style) => normalizeStyleObject(style, context));\n }\n function mergeCss(...styles) {\n return mergeProps(...resolve(styles));\n }\n function assignCss(...styles) {\n return Object.assign({}, ...resolve(styles));\n }\n return {\n mergeCss: memo(mergeCss),\n assignCss\n };\n}\n//#endregion\n//#region src/clone-styles.ts\nconst OMIT = new Set([\n \"__proto__\",\n \"constructor\",\n \"prototype\"\n]);\n/**\n* Independent copy of a style object, nested condition blocks included.\n*\n* Merged style objects are cached, so anything handed to user code has to be\n* copied first: a caller mutating what it received would otherwise change what\n* every later caller reads back. `css.raw()` and `cva.raw()` are those boundaries.\n*\n* Kept separate from `mergeProps` deliberately. Merging is on the hot path — it\n* runs on every `css()` cache miss and on every render of a pattern component\n* under `jsxStyleProps: 'minimal'` — and copying there charges every caller for a\n* guarantee only the two `raw()` helpers need. Measured on a realistic style\n* object (5 base properties, 4 condition blocks) that was roughly twice the cost\n* of merging alone.\n*/\nfunction cloneStyles(styles) {\n if (Array.isArray(styles)) return styles.map((value) => cloneStyles(value));\n if (!isObject(styles)) return styles;\n const out = {};\n for (const key of Object.keys(styles)) {\n if (OMIT.has(key)) continue;\n out[key] = cloneStyles(styles[key]);\n }\n return out;\n}\n//#endregion\n//#region src/compact.ts\nfunction compact(value) {\n return Object.fromEntries(Object.entries(value ?? {}).filter(([_, value]) => value !== void 0));\n}\n//#endregion\n//#region src/leaf-class.ts\n/**\n* The class a single dynamic style leaf resolves to, given the prefix its property and\n* condition path produce.\n*\n* ## Why this can exist at all\n*\n* `css()` builds a class from the value alone — `utility.transform` is string\n* construction over a static map, and nothing consults which rules were actually emitted.\n* So `css({ color: tone })` already returns `c_<tone>` for a value the extractor never\n* saw, with no CSS behind it. Reproducing that string here cannot be less correct than\n* the call it replaces; it just skips the object literal, the merge and the memo.\n*\n* ## Why it is not a template literal\n*\n* Three shapes do not reduce to `prefix + value`, and all three return `undefined` so the\n* caller runs `css()` instead:\n*\n* - An array is expanded to a responsive object by `normalizeStyleObject`, so it produces\n* one class per breakpoint rather than one class.\n* - An object is a condition block, walked into for the same reason.\n* - `null` and `undefined` are skipped by the walk entirely, which is an empty string\n* rather than a class — that one is answered here, since it needs no `css()` call.\n*\n* ## Why the character scan\n*\n* The remaining work — collapsing whitespace, stripping `!important`, turning spaces into\n* underscores — is three regexes, and paying them per call makes this *slower* than a\n* memo hit. Almost no token value contains whitespace or `!`, so one scan for the\n* characters that make any of it necessary sends the common value straight to a\n* concatenation. A false positive only costs the slow path, so the scan errs wide.\n*/\nfunction leafClass(prefix, value) {\n if (value == null) return \"\";\n const type = typeof value;\n if (type === \"number\" || type === \"boolean\") return `${prefix}${value}`;\n if (type !== \"string\") return void 0;\n const str = value;\n for (let index = 0; index < str.length; index++) {\n const code = str.charCodeAt(index);\n if (code <= 33 || code === 160 || code === 5760 || code >= 8192) return slowLeaf(prefix, str);\n }\n return `${prefix}${str}`;\n}\n/** The full pipeline `createCss` runs, for a value that needs it. */\nfunction slowLeaf(prefix, value) {\n const important = isImportant(value);\n const className = `${prefix}${withoutSpace(withoutImportant(sanitize(value)))}`;\n return important ? `${className}!` : className;\n}\n//#endregion\n//#region src/hypenate-property.ts\nconst wordRegex = /([A-Z])/g;\nconst msRegex = /^ms-/;\nconst hypenateProperty = memo((property) => {\n if (property.startsWith(\"--\")) return property;\n return property.replace(wordRegex, \"-$1\").replace(msRegex, \"-ms-\").toLowerCase();\n});\n//#endregion\n//#region src/is-css-function.ts\nconst fnRegExp = new RegExp(`^(${[\n \"min\",\n \"max\",\n \"clamp\",\n \"calc\"\n].join(\"|\")})\\\\(.*\\\\)`);\nconst isCssFunction = (v) => typeof v === \"string\" && fnRegExp.test(v);\n//#endregion\n//#region src/is-css-unit.ts\nconst lengthUnitsPattern = `(?:${\"cm,mm,Q,in,pc,pt,px,em,ex,ch,rem,lh,rlh,vw,vh,vmin,vmax,vb,vi,svw,svh,lvw,lvh,dvw,dvh,cqw,cqh,cqi,cqb,cqmin,cqmax,%\".split(\",\").join(\"|\")})`;\nconst lengthRegExp = new RegExp(`^[+-]?[0-9]*.?[0-9]+(?:[eE][+-]?[0-9]+)?${lengthUnitsPattern}$`);\nconst isCssUnit = (v) => typeof v === \"string\" && lengthRegExp.test(v);\n//#endregion\n//#region src/is-css-var.ts\nconst isCssVar = (v) => typeof v === \"string\" && /^var\\(--.+\\)$/.test(v);\n//#endregion\n//#region src/pattern-fns.ts\nconst patternFns = {\n map: mapObject,\n isCssFunction,\n isCssVar,\n isCssUnit\n};\nconst getPatternStyles = (pattern, styles) => {\n if (!pattern?.defaultValues) return styles;\n const defaults = typeof pattern.defaultValues === \"function\" ? pattern.defaultValues(styles) : pattern.defaultValues;\n return Object.assign({}, defaults, compact(styles));\n};\n//#endregion\n//#region src/slot.ts\nconst getSlotRecipes = (recipe = {}) => {\n const init = (slot) => ({\n className: [recipe.className, slot].filter(Boolean).join(\"__\"),\n base: recipe.base?.[slot] ?? {},\n variants: {},\n defaultVariants: recipe.defaultVariants ?? {},\n compoundVariants: recipe.compoundVariants ? getSlotCompoundVariant(recipe.compoundVariants, slot) : []\n });\n const recipeParts = (recipe.slots ?? []).map((slot) => [slot, init(slot)]);\n for (const [variantsKey, variantsSpec] of Object.entries(recipe.variants ?? {})) for (const [variantKey, variantSpec] of Object.entries(variantsSpec)) recipeParts.forEach(([slot, slotRecipe]) => {\n slotRecipe.variants[variantsKey] ??= {};\n slotRecipe.variants[variantsKey][variantKey] = variantSpec[slot] ?? {};\n });\n return Object.fromEntries(recipeParts);\n};\nconst getSlotCompoundVariant = (compoundVariants, slotName) => compoundVariants.filter((compoundVariant) => compoundVariant.css[slotName]).map((compoundVariant) => ({\n ...compoundVariant,\n css: compoundVariant.css[slotName]\n}));\n//#endregion\n//#region src/split-props.ts\n/**\n* Deal a props object into one bucket per key group, plus a final bucket for the rest.\n* A key goes to the first group that claims it.\n*\n* ## Why the descriptor is read per key rather than in bulk\n*\n* This used to call `Object.getOwnPropertyDescriptors` for the whole object and\n* `defineProperty` for every key it moved. Copying plain values instead is 2.4–2.9x faster\n* on the shapes that allow it, but it is only correct where props are data — and they are\n* not always. Solid compiles props to accessors, so reading one eagerly runs whatever it\n* wraps: splitting a component's props would construct its children before the surrounding\n* provider exists.\n*\n* So the descriptor is fetched per key, and the value path is taken only when it changes\n* nothing observable. An accessor keeps its laziness, a non-enumerable key keeps its\n* invisibility, and `__proto__` is defined rather than assigned so it stays an own\n* property instead of reaching the prototype setter.\n*\n* The one thing the value path drops is `writable`/`configurable`, so a bucket key taken\n* from frozen props is writable where it used to be frozen. Nothing here relies on that,\n* and preserving it would mean `defineProperty` on the common path — the cost this exists\n* to avoid. Keys that take the descriptor path keep theirs, so a bucket can be\n* inconsistent in that one respect.\n*\n* Key order within a bucket is preserved exactly. It is not cosmetic: `cva` merges\n* variant props in iteration order, and the parser reads the rest bucket as the style\n* props it encodes, so order reaches the emitted CSS.\n*/\nfunction splitProps(props, ...keys) {\n const allKeys = Object.getOwnPropertyNames(props);\n const own = new Set(allKeys);\n const taken = /* @__PURE__ */ new Set();\n const split = (group) => {\n const clone = {};\n for (let i = 0; i < group.length; i++) {\n const key = group[i];\n if (taken.has(key) || !own.has(key)) continue;\n const descriptor = Object.getOwnPropertyDescriptor(props, key);\n if (!descriptor) continue;\n if (\"get\" in descriptor || \"set\" in descriptor || !descriptor.enumerable || key === \"__proto__\") Object.defineProperty(clone, key, descriptor);\n else clone[key] = descriptor.value;\n taken.add(key);\n }\n return clone;\n };\n /**\n * The predicate is called with the key alone.\n *\n * Handing it to `filter` passes `(key, index, allKeys)`. A one-parameter predicate cannot\n * see the extras, but a memoized one reads its whole argument list — and the predicates\n * that arrive here are memoized, `isCssProperty` among them. So the memo hashed the entire\n * key array once per prop, and keyed its cache on it: two elements with different prop sets\n * shared no entry even for the same prop name.\n *\n * Worth ~9.7x on that path, and nothing at all on a plain predicate — which is why the\n * bench below it needs a memoized case to see this at all.\n *\n * A loop rather than `filter((k) => key(k))` because the wrapper allocates a closure per\n * group. The two measure the same to within noise; the loop just does not need one.\n */\n const matching = (predicate) => {\n const group = [];\n for (let i = 0; i < allKeys.length; i++) {\n const key = allKeys[i];\n if (predicate(key)) group.push(key);\n }\n return group;\n };\n return keys.map((key) => split(Array.isArray(key) ? key : matching(key))).concat(split(allKeys));\n}\n//#endregion\n//#region src/uniq.ts\nconst uniq = (...items) => {\n const set = items.reduce((acc, currItems) => {\n if (currItems) currItems.forEach((item) => acc.add(item));\n return acc;\n }, /* @__PURE__ */ new Set([]));\n return Array.from(set);\n};\n//#endregion\n//#region src/view-transition.ts\n/**\n* The slots a `viewTransition()` bag accepts, in the order they are emitted.\n*\n* Anything else in the options object is ignored — including by the hash, so adding a\n* comment key or spreading an unrelated object does not fork the class name.\n*/\nconst viewTransitionSlots = [\n \"group\",\n \"imagePair\",\n \"old\",\n \"new\"\n];\n/**\n* Serialize a value with object keys sorted, so two style objects that differ only in\n* authoring order hash to the same class.\n*\n* Values JSON has no encoding for (`undefined`, functions, symbols) serialize as `null`\n* rather than being dropped. Nothing reaches here holding one — `filterSlots` removes\n* nullish slots, and the extractor drops nullish leaves — so this is only a floor: it\n* keeps a hole in an array from shifting the elements after it.\n*\n* Not exported. The name promises a general-purpose stable serializer, and this is not\n* one; `viewTransitionClassName` is the whole reason it exists.\n*/\nfunction stableStringify(value) {\n if (value === null) return \"null\";\n const type = typeof value;\n if (type === \"boolean\") return value ? \"true\" : \"false\";\n if (type === \"number\") return Number.isFinite(value) ? String(value) : \"null\";\n if (type === \"string\") return JSON.stringify(value);\n if (Array.isArray(value)) {\n let out = \"[\";\n for (let index = 0; index < value.length; index++) {\n if (index) out += \",\";\n out += stableStringify(value[index]);\n }\n return out + \"]\";\n }\n if (type === \"object\") {\n const keys = Object.keys(value).sort();\n let out = \"{\";\n for (let index = 0; index < keys.length; index++) {\n if (index) out += \",\";\n const key = keys[index];\n out += JSON.stringify(key) + \":\" + stableStringify(value[key]);\n }\n return out + \"}\";\n }\n return \"null\";\n}\n/**\n* Keep the four known slots, dropping any that is nullish.\n*\n* Both halves of the contract have to agree on what an empty slot is, and only one of\n* them gets a choice. The extractor evaluates the source, and a nullish property is gone\n* from what it hands over — `{ new: undefined }` and `{}` reach the build identically.\n* So absent, `undefined` and `null` collapse here too, and `new: enabled ? {…} : null`\n* hashes to the bag it actually styles.\n*/\nfunction filterSlots(options) {\n const filtered = {};\n if (!options || typeof options !== \"object\") return filtered;\n for (const slot of viewTransitionSlots) {\n const value = options[slot];\n if (value != null) filtered[slot] = value;\n }\n return filtered;\n}\n/**\n* The class a `viewTransition()` call resolves to.\n*\n* This is the whole contract between the build and the runtime: the extractor hashes the\n* options it found in the source, the generated `viewTransition()` hashes the options it\n* is called with, and the CSS only reaches the element if the two agree. They agree by\n* construction — both call this function — but only over what the extractor can see. A\n* value it cannot resolve statically is absent from its side and present on the runtime's,\n* which is why a bag has to be a static object literal to be styled at all.\n*\n* The class is used twice, as the `view-transition-class` value and as the argument to\n* `::view-transition-*(.cls)`, so the prefix applies to both.\n*/\nfunction viewTransitionClassName(options, prefix = \"\") {\n const base = \"vt_\" + toHash(stableStringify(filterSlots(options)));\n return prefix ? prefix + \"-\" + base : base;\n}\n//#endregion\nexport { cloneStyles, compact, createCss, createMergeCss, filterBaseConditions, getPatternStyles, getSlotCompoundVariant, getSlotRecipes, hypenateProperty, isBaseCondition, isObject, leafClass, mapObject, memo, mergeProps, patternFns, splitProps, toHash, uniq, viewTransitionClassName, walkObject, withoutSpace };\n";
|
|
430
647
|
//#endregion
|
|
431
648
|
//#region src/artifacts/generated/normalize-html.mjs.json
|
|
432
649
|
var content$9 = "//#region src/normalize-html.ts\nconst htmlProps = [\n \"htmlSize\",\n \"htmlTranslate\",\n \"htmlWidth\",\n \"htmlHeight\"\n];\nfunction convert(key) {\n return htmlProps.includes(key) ? key.replace(\"html\", \"\").toLowerCase() : key;\n}\nfunction normalizeHTMLProps(props) {\n return Object.fromEntries(Object.entries(props).map(([key, value]) => [convert(key), value]));\n}\nnormalizeHTMLProps.keys = htmlProps;\n//#endregion\nexport { normalizeHTMLProps };\n";
|
|
@@ -4513,7 +4730,7 @@ var content$2 = "import type { Pseudos } from './csstype'\n\ntype AriaAttributes
|
|
|
4513
4730
|
var content$1 = "interface ConditionOptions {\n /**\n * The conditions to generate for the rule.\n * @example ['hover', 'focus']\n */\n conditions?: string[]\n /**\n * Whether to generate responsive styles for the rule.\n */\n responsive?: boolean\n}\n\nexport interface CssRule extends ConditionOptions {\n /**\n * The css properties to generate utilities for.\n * @example ['margin', 'padding']\n */\n properties: {\n [property: string]: Array<string | number>\n }\n}\n\ninterface RecipeRuleVariants {\n [variant: string]: boolean | string[]\n}\n\nexport type RecipeRuleObject = RecipeRuleVariants & ConditionOptions\nexport type RecipeRule = '*' | RecipeRuleObject\n\nexport type PatternRule = '*' | CssRule\n\nexport interface StaticCssOptions {\n /**\n * The css utility classes to generate.\n */\n css?: CssRule[]\n /**\n * The css recipes to generate.\n */\n recipes?:\n | '*'\n | {\n [recipe: string]: RecipeRule[]\n }\n /**\n * The css patterns to generate.\n */\n patterns?: {\n [pattern: string]: PatternRule[]\n }\n /**\n * The CSS themes to generate\n */\n themes?: string[]\n}\n";
|
|
4514
4731
|
//#endregion
|
|
4515
4732
|
//#region src/artifacts/generated/system-types.d.ts.json
|
|
4516
|
-
var content = "import type { ConditionalValue, Nested } from './conditions'\nimport type { AtRule, Globals, PropertiesFallback } from './csstype'\nimport type { SystemProperties, CssVarProperties } from './style-props'\n\ntype String = string & {}\ntype Number = number & {}\n\nexport type Pretty<T> = { [K in keyof T]: T[K] } & {}\n\nexport type DistributiveOmit<T, K extends keyof any> = T extends unknown ? Omit<T, K> : never\n\nexport type DistributiveUnion<T, U> = {\n [K in keyof T]: K extends keyof U ? U[K] | T[K] : T[K]\n} & DistributiveOmit<U, keyof T>\n\nexport type Assign<T, U> = {\n [K in keyof T]: K extends keyof U ? U[K] : T[K]\n} & U\n\n/* -----------------------------------------------------------------------------\n * Native css properties\n * -----------------------------------------------------------------------------*/\n\ntype CornerShapeValue = 'round' | 'square' | 'bevel' | 'scoop' | 'notch' | 'squircle' | `superellipse(${number})`\n\nexport interface ModernCssProperties {\n /**\n * Controls whether the entire element should be draggable instead of its contents.\n */\n WebkitUserDrag?: Globals | 'auto' | 'element' | 'none'\n\n /**\n * Specifies whether an element can be used to drag the entire app window (Electron).\n */\n WebkitAppRegion?: Globals | 'drag' | 'no-drag'\n\n /**\n * Sets the horizontal spacing between table borders.\n */\n WebkitBorderHorizontalSpacing?: Globals | String | Number\n\n /**\n * Sets the vertical spacing between table borders.\n */\n WebkitBorderVerticalSpacing?: Globals | String | Number\n\n /**\n * Controls the display of text content for security purposes (e.g., password fields).\n */\n WebkitTextSecurity?: Globals | 'none' | 'circle' | 'disc' | 'square'\n\n /**\n * Specifies the shape of a box's corners within the area defined by the border-radius property.\n * @experimental\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/corner-shape\n */\n cornerShape?:\n | Globals\n | CornerShapeValue\n | `${CornerShapeValue} ${CornerShapeValue}`\n | `${CornerShapeValue} ${CornerShapeValue} ${CornerShapeValue}`\n | `${CornerShapeValue} ${CornerShapeValue} ${CornerShapeValue} ${CornerShapeValue}`\n | String\n}\n\nexport type CssProperty = keyof PropertiesFallback\n\nexport interface CssProperties extends PropertiesFallback<String | Number>, CssVarProperties, ModernCssProperties {}\n\nexport interface CssKeyframes {\n [name: string]: {\n [time: string]: CssProperties\n }\n}\n\n/* -----------------------------------------------------------------------------\n * Conditional css properties\n * -----------------------------------------------------------------------------*/\n\ninterface GenericProperties {\n [key: string]: ConditionalValue<String | Number | boolean>\n}\n\n/* -----------------------------------------------------------------------------\n * Native css props\n * -----------------------------------------------------------------------------*/\n\nexport type NestedCssProperties = Nested<CssProperties>\n\nexport type SystemStyleObject = Omit<Nested<SystemProperties & CssVarProperties>, 'base'>\n\nexport interface GlobalStyleObject {\n [selector: string]: SystemStyleObject\n}\nexport interface ExtendableGlobalStyleObject {\n [selector: string]: SystemStyleObject | undefined\n extend?: GlobalStyleObject | undefined\n}\n\n/* -----------------------------------------------------------------------------\n * Composition (text styles, layer styles)\n * -----------------------------------------------------------------------------*/\n\ntype FilterStyleObject<P extends string> = {\n [K in P]?: K extends keyof SystemStyleObject ? SystemStyleObject[K] : unknown\n}\n\nexport type CompositionStyleObject<Property extends string> = Nested<FilterStyleObject<Property> & CssVarProperties>\n\n/* -----------------------------------------------------------------------------\n * Font face\n * -----------------------------------------------------------------------------*/\n\nexport type GlobalFontfaceRule = Omit<AtRule.FontFaceFallback, 'src'> & Required<Pick<AtRule.FontFaceFallback, 'src'>>\n\nexport type FontfaceRule = Omit<GlobalFontfaceRule, 'fontFamily'>\n\nexport interface GlobalFontface {\n [name: string]: FontfaceRule | FontfaceRule[]\n}\n\nexport interface ExtendableGlobalFontface {\n [name: string]: FontfaceRule | FontfaceRule[] | GlobalFontface | undefined\n extend?: GlobalFontface | undefined\n}\n\n/* -----------------------------------------------------------------------------\n * Jsx style props\n * -----------------------------------------------------------------------------*/\ninterface WithCss {\n css?: SystemStyleObject | SystemStyleObject[]\n}\n\nexport type JsxStyleProps = SystemStyleObject & WithCss\n\nexport interface PatchedHTMLProps {\n htmlWidth?: string | number\n htmlHeight?: string | number\n htmlTranslate?: 'yes' | 'no' | undefined\n htmlContent?: string\n}\n\nexport type OmittedHTMLProps = 'color' | 'translate' | 'transition' | 'width' | 'height' | 'content'\n\ntype WithHTMLProps<T> = DistributiveOmit<T, OmittedHTMLProps> & PatchedHTMLProps\n\nexport type JsxHTMLProps<T extends Record<string, any>, P extends Record<string, any> = {}> = Assign<\n WithHTMLProps<T>,\n P\n>\n";
|
|
4733
|
+
var content = "import type { ConditionalValue, Nested } from './conditions'\nimport type { AtRule, Globals, PropertiesFallback } from './csstype'\nimport type { SystemProperties, CssVarProperties } from './style-props'\n\ntype String = string & {}\ntype Number = number & {}\n\nexport type Pretty<T> = { [K in keyof T]: T[K] } & {}\n\nexport type DistributiveOmit<T, K extends keyof any> = T extends unknown ? Omit<T, K> : never\n\nexport type DistributiveUnion<T, U> = {\n [K in keyof T]: K extends keyof U ? U[K] | T[K] : T[K]\n} & DistributiveOmit<U, keyof T>\n\nexport type Assign<T, U> = {\n [K in keyof T]: K extends keyof U ? U[K] : T[K]\n} & U\n\n/* -----------------------------------------------------------------------------\n * Native css properties\n * -----------------------------------------------------------------------------*/\n\ntype CornerShapeValue = 'round' | 'square' | 'bevel' | 'scoop' | 'notch' | 'squircle' | `superellipse(${number})`\n\nexport interface ModernCssProperties {\n /**\n * Controls whether the entire element should be draggable instead of its contents.\n */\n WebkitUserDrag?: Globals | 'auto' | 'element' | 'none'\n\n /**\n * Specifies whether an element can be used to drag the entire app window (Electron).\n */\n WebkitAppRegion?: Globals | 'drag' | 'no-drag'\n\n /**\n * Sets the horizontal spacing between table borders.\n */\n WebkitBorderHorizontalSpacing?: Globals | String | Number\n\n /**\n * Sets the vertical spacing between table borders.\n */\n WebkitBorderVerticalSpacing?: Globals | String | Number\n\n /**\n * Controls the display of text content for security purposes (e.g., password fields).\n */\n WebkitTextSecurity?: Globals | 'none' | 'circle' | 'disc' | 'square'\n\n /**\n * Specifies the shape of a box's corners within the area defined by the border-radius property.\n * @experimental\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/corner-shape\n */\n cornerShape?:\n | Globals\n | CornerShapeValue\n | `${CornerShapeValue} ${CornerShapeValue}`\n | `${CornerShapeValue} ${CornerShapeValue} ${CornerShapeValue}`\n | `${CornerShapeValue} ${CornerShapeValue} ${CornerShapeValue} ${CornerShapeValue}`\n | String\n}\n\nexport type CssProperty = keyof PropertiesFallback\n\nexport interface CssProperties extends PropertiesFallback<String | Number>, CssVarProperties, ModernCssProperties {}\n\nexport interface CssKeyframes {\n [name: string]: {\n [time: string]: CssProperties\n }\n}\n\n/* -----------------------------------------------------------------------------\n * Conditional css properties\n * -----------------------------------------------------------------------------*/\n\ninterface GenericProperties {\n [key: string]: ConditionalValue<String | Number | boolean>\n}\n\n/* -----------------------------------------------------------------------------\n * Native css props\n * -----------------------------------------------------------------------------*/\n\nexport type NestedCssProperties = Nested<CssProperties>\n\nexport type SystemStyleObject = Omit<Nested<SystemProperties & CssVarProperties>, 'base'>\n\n/**\n * The four `::view-transition-*` pseudo-elements a `viewTransition()` bag can style.\n *\n * `imagePair` is camelCase here and emitted as `::view-transition-image-pair`, matching\n * how every other property in a style object is authored.\n */\nexport interface ViewTransitionStyleObject {\n group?: SystemStyleObject\n imagePair?: SystemStyleObject\n old?: SystemStyleObject\n new?: SystemStyleObject\n}\n\nexport type ViewTransitionFn = (options: ViewTransitionStyleObject) => string\n\nexport interface GlobalStyleObject {\n [selector: string]: SystemStyleObject\n}\nexport interface ExtendableGlobalStyleObject {\n [selector: string]: SystemStyleObject | undefined\n extend?: GlobalStyleObject | undefined\n}\n\n/* -----------------------------------------------------------------------------\n * Composition (text styles, layer styles)\n * -----------------------------------------------------------------------------*/\n\ntype FilterStyleObject<P extends string> = {\n [K in P]?: K extends keyof SystemStyleObject ? SystemStyleObject[K] : unknown\n}\n\nexport type CompositionStyleObject<Property extends string> = Nested<FilterStyleObject<Property> & CssVarProperties>\n\n/* -----------------------------------------------------------------------------\n * Font face\n * -----------------------------------------------------------------------------*/\n\nexport type GlobalFontfaceRule = Omit<AtRule.FontFaceFallback, 'src'> & Required<Pick<AtRule.FontFaceFallback, 'src'>>\n\nexport type FontfaceRule = Omit<GlobalFontfaceRule, 'fontFamily'>\n\nexport interface GlobalFontface {\n [name: string]: FontfaceRule | FontfaceRule[]\n}\n\nexport interface ExtendableGlobalFontface {\n [name: string]: FontfaceRule | FontfaceRule[] | GlobalFontface | undefined\n extend?: GlobalFontface | undefined\n}\n\n/* -----------------------------------------------------------------------------\n * Jsx style props\n * -----------------------------------------------------------------------------*/\ninterface WithCss {\n css?: SystemStyleObject | SystemStyleObject[]\n}\n\nexport type JsxStyleProps = SystemStyleObject & WithCss\n\nexport interface PatchedHTMLProps {\n htmlWidth?: string | number\n htmlHeight?: string | number\n htmlTranslate?: 'yes' | 'no' | undefined\n htmlContent?: string\n}\n\nexport type OmittedHTMLProps = 'color' | 'translate' | 'transition' | 'width' | 'height' | 'content'\n\ntype WithHTMLProps<T> = DistributiveOmit<T, OmittedHTMLProps> & PatchedHTMLProps\n\nexport type JsxHTMLProps<T extends Record<string, any>, P extends Record<string, any> = {}> = Assign<\n WithHTMLProps<T>,\n P\n>\n";
|
|
4517
4734
|
//#endregion
|
|
4518
4735
|
//#region src/artifacts/types/generated.ts
|
|
4519
4736
|
function getGeneratedTypes(ctx) {
|
|
@@ -4596,11 +4813,21 @@ function generatePropTypes(ctx) {
|
|
|
4596
4813
|
type Important = ImportantMark | WhitespaceImportant
|
|
4597
4814
|
type WithImportant<T> = [T] extends [string] ? \`\${T}\${Important}\` & { __important?: true } : never
|
|
4598
4815
|
|
|
4816
|
+
/**
|
|
4817
|
+
* A list of candidate values, most-preferred first, emitted as repeated declarations so the
|
|
4818
|
+
* browser keeps the last one it understands.
|
|
4819
|
+
*
|
|
4820
|
+
* @example
|
|
4821
|
+
* css({ height: 'fallback(100dvh, 100vh)' })
|
|
4822
|
+
*/
|
|
4823
|
+
type FallbackValue = \`fallback(\${string})\`
|
|
4824
|
+
|
|
4599
4825
|
/**
|
|
4600
4826
|
* Only relevant when using \`strictTokens\` or \`strictPropertyValues\` in your config.
|
|
4601
4827
|
* - Allows you to use an escape hatch (e.g. \`[123px]\`) to use any string as a value.
|
|
4602
4828
|
* - Allows you to use a color opacity modifier (e.g. \`red/300\`) with known color values.
|
|
4603
4829
|
* - Allows you to use an important mark (e.g. \`!\` or \`!important\`) in the value.
|
|
4830
|
+
* - Allows you to use a fallback list (e.g. \`fallback(100dvh, 100vh)\`).
|
|
4604
4831
|
*
|
|
4605
4832
|
* This is useful when you want to use a value that is not defined in the config or want to opt-out of the defaults.
|
|
4606
4833
|
*
|
|
@@ -4612,7 +4839,13 @@ function generatePropTypes(ctx) {
|
|
|
4612
4839
|
* @see https://bamboocss.com/docs/concepts/writing-styles#stricttokens
|
|
4613
4840
|
* @see https://bamboocss.com/docs/concepts/writing-styles#strictpropertyvalues
|
|
4614
4841
|
*/
|
|
4615
|
-
export type WithEscapeHatch<T> =
|
|
4842
|
+
export type WithEscapeHatch<T> =
|
|
4843
|
+
| T
|
|
4844
|
+
| \`[\${string}]\`
|
|
4845
|
+
| FallbackValue
|
|
4846
|
+
| WithImportant<FallbackValue>
|
|
4847
|
+
| WithColorOpacityModifier<T>
|
|
4848
|
+
| WithImportant<T>
|
|
4616
4849
|
|
|
4617
4850
|
/**
|
|
4618
4851
|
* Will restrict the value of properties that have predefined values to those values only.
|
|
@@ -5220,7 +5453,7 @@ function setupSva(ctx) {
|
|
|
5220
5453
|
};
|
|
5221
5454
|
}
|
|
5222
5455
|
function setupCx(ctx) {
|
|
5223
|
-
const code = generateCx();
|
|
5456
|
+
const code = generateCx(ctx);
|
|
5224
5457
|
return {
|
|
5225
5458
|
id: "cx",
|
|
5226
5459
|
dir: ctx.paths.css,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bamboocss/generator",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"description": "The css generator for css bamboo",
|
|
5
5
|
"homepage": "https://bamboocss.com",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,12 +38,12 @@
|
|
|
38
38
|
"pluralize": "8.0.0",
|
|
39
39
|
"postcss": "8.5.14",
|
|
40
40
|
"ts-pattern": "5.9.0",
|
|
41
|
-
"@bamboocss/core": "1.
|
|
42
|
-
"@bamboocss/is-valid-prop": "^1.
|
|
43
|
-
"@bamboocss/logger": "1.
|
|
44
|
-
"@bamboocss/shared": "1.
|
|
45
|
-
"@bamboocss/token-dictionary": "1.
|
|
46
|
-
"@bamboocss/types": "1.
|
|
41
|
+
"@bamboocss/core": "1.15.0",
|
|
42
|
+
"@bamboocss/is-valid-prop": "^1.15.0",
|
|
43
|
+
"@bamboocss/logger": "1.15.0",
|
|
44
|
+
"@bamboocss/shared": "1.15.0",
|
|
45
|
+
"@bamboocss/token-dictionary": "1.15.0",
|
|
46
|
+
"@bamboocss/types": "1.15.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@types/pluralize": "0.0.33"
|