@bamboocss/generator 1.46.3 → 1.48.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 +39 -284
- package/dist/index.d.cts +6 -14
- package/dist/index.d.mts +6 -14
- package/dist/index.mjs +39 -284
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -42,46 +42,7 @@ function formatConditionJsDoc(raw) {
|
|
|
42
42
|
}
|
|
43
43
|
function generateConditions(ctx) {
|
|
44
44
|
const keys = Object.keys(ctx.conditions.values).concat("base");
|
|
45
|
-
return {
|
|
46
|
-
js: outdent.default`
|
|
47
|
-
${ctx.file.import("withoutSpace", "../helpers")}
|
|
48
|
-
|
|
49
|
-
const conditionsStr = "${keys.join(",")}"
|
|
50
|
-
const conditions = new Set(conditionsStr.split(','))
|
|
51
|
-
|
|
52
|
-
const conditionRegex = /^@|&|&$/
|
|
53
|
-
|
|
54
|
-
export function isCondition(value){
|
|
55
|
-
return conditions.has(value) || conditionRegex.test(value)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const underscoreRegex = /^_/
|
|
59
|
-
const conditionsSelectorRegex = /&|@/
|
|
60
|
-
|
|
61
|
-
export function finalizeConditions(paths){
|
|
62
|
-
return paths.map((path) => {
|
|
63
|
-
if (conditions.has(path)){
|
|
64
|
-
return path.replace(underscoreRegex, '')
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (conditionsSelectorRegex.test(path)){
|
|
68
|
-
return \`[\${withoutSpace(path.trim())}]\`
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return path
|
|
72
|
-
})}
|
|
73
|
-
|
|
74
|
-
export function sortConditions(paths){
|
|
75
|
-
return paths.sort((a, b) => {
|
|
76
|
-
const aa = isCondition(a)
|
|
77
|
-
const bb = isCondition(b)
|
|
78
|
-
if (aa && !bb) return 1
|
|
79
|
-
if (!aa && bb) return -1
|
|
80
|
-
return 0
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
`,
|
|
84
|
-
dts: outdent.default`
|
|
45
|
+
return { dts: outdent.default`
|
|
85
46
|
${ctx.file.importType("AnySelector, Selectors", "./selectors")}
|
|
86
47
|
|
|
87
48
|
export interface Conditions {
|
|
@@ -103,14 +64,11 @@ function generateConditions(ctx) {
|
|
|
103
64
|
[K in keyof Conditions]?: Nested<P>
|
|
104
65
|
}
|
|
105
66
|
|
|
106
|
-
`
|
|
107
|
-
};
|
|
67
|
+
` };
|
|
108
68
|
}
|
|
109
69
|
//#endregion
|
|
110
70
|
//#region src/artifacts/js/css-fn.ts
|
|
111
71
|
function generateCssFn(ctx) {
|
|
112
|
-
const { utility, hash, prefix } = ctx;
|
|
113
|
-
const { separator } = utility;
|
|
114
72
|
return {
|
|
115
73
|
dts: outdent.outdent`
|
|
116
74
|
${ctx.file.importType("SystemStyleObject, ViewTransitionFn", "../types/index")}
|
|
@@ -161,39 +119,12 @@ function generateCssFn(ctx) {
|
|
|
161
119
|
|
|
162
120
|
`,
|
|
163
121
|
js: outdent.outdent`
|
|
164
|
-
${ctx.file.import("cloneStyles,
|
|
165
|
-
${ctx.file.import("
|
|
166
|
-
${ctx.file.import("classNameByProp", "./utilities")}
|
|
167
|
-
${ctx.file.import("mergeCss, mergeCssUncached, resolveShorthand", "./merge-css")}
|
|
168
|
-
|
|
169
|
-
const context = {
|
|
170
|
-
${hash.className ? "hash: true," : ""}
|
|
171
|
-
conditions: {
|
|
172
|
-
shift: sortConditions,
|
|
173
|
-
finalize: finalizeConditions,
|
|
174
|
-
},
|
|
175
|
-
utility: {
|
|
176
|
-
${prefix.className ? "prefix: " + JSON.stringify(prefix.className) + "," : ""}
|
|
177
|
-
transform: ${utility.hasShorthand ? `(prop, value) => {
|
|
178
|
-
const key = resolveShorthand(prop)
|
|
179
|
-
const propKey = classNameByProp.get(key) || hypenateProperty(key)
|
|
180
|
-
return { className: \`$\{propKey}${separator}$\{withoutSpace(value)}\` }
|
|
181
|
-
}` : `(key, value) => ({ className: \`$\{classNameByProp.get(key) || hypenateProperty(key)}${separator}$\{withoutSpace(value)}\` })`},
|
|
182
|
-
${utility.hasShorthand ? "hasShorthand: true," : ""}
|
|
183
|
-
toHash: ${utility.toHash},
|
|
184
|
-
resolveShorthand: ${utility.hasShorthand ? "resolveShorthand" : "prop => prop"},
|
|
185
|
-
}
|
|
186
|
-
}
|
|
122
|
+
${ctx.file.import("cloneStyles, uncompiledStyle", "../helpers")}
|
|
123
|
+
${ctx.file.import("mergeCss", "./merge-css")}
|
|
187
124
|
|
|
188
|
-
const
|
|
189
|
-
// \`
|
|
190
|
-
//
|
|
191
|
-
// have not been seen — so a second cache keyed on the same arguments, or on the merge
|
|
192
|
-
// derived from them, can only miss too, after paying for the lookup.
|
|
193
|
-
export const css = /* @__PURE__ */ memo((...styles) => cssFn(mergeCssUncached(...styles)))
|
|
194
|
-
// The cached merge here, since \`raw\` is called straight from user code with no memo
|
|
195
|
-
// above it. The merged result is cached and shared, so a caller mutating a nested
|
|
196
|
-
// condition object would otherwise poison it for everyone after them.
|
|
125
|
+
export const css = (..._styles) => uncompiledStyle('css')
|
|
126
|
+
// \`raw\` is a style object, not a class string. The compiler leaves it; \`css(css.raw(...))\`
|
|
127
|
+
// is what has to fold.
|
|
197
128
|
css.raw = (...styles) => cloneStyles(mergeCss(...styles))
|
|
198
129
|
|
|
199
130
|
// Sugar for the string form, so the feature has an import to discover, a signature to
|
|
@@ -201,10 +132,7 @@ function generateCssFn(ctx) {
|
|
|
201
132
|
// value reaching \`css()\` is the same literal either way.
|
|
202
133
|
export const fallback = (...values) => \`fallback($\{values.join(', ')})\`
|
|
203
134
|
|
|
204
|
-
|
|
205
|
-
// from the same options, hashed by this same function. A call the extractor never saw
|
|
206
|
-
// still returns a class, exactly as \`css()\` does for a value it never saw.
|
|
207
|
-
export const viewTransition = (options) => viewTransitionClassName(options, ${JSON.stringify(prefix.className ?? "")})
|
|
135
|
+
export const viewTransition = (_options) => uncompiledStyle('viewTransition')
|
|
208
136
|
|
|
209
137
|
`
|
|
210
138
|
};
|
|
@@ -282,35 +210,13 @@ function generateMergeCssFn(ctx) {
|
|
|
282
210
|
};
|
|
283
211
|
}
|
|
284
212
|
//#endregion
|
|
285
|
-
//#region src/artifacts/js/utilities-table.ts
|
|
286
|
-
/**
|
|
287
|
-
* The property→className map, and nothing else.
|
|
288
|
-
*
|
|
289
|
-
* Only `css()` names a class, so only `css()` reads this. The shorthand half of what used to
|
|
290
|
-
* be one table now lives in `merge-css`, which is what `cva` reaches — see that file for why
|
|
291
|
-
* the two were separated and what it costs.
|
|
292
|
-
*/
|
|
293
|
-
function generateUtilitiesTable(ctx) {
|
|
294
|
-
const { utility } = ctx;
|
|
295
|
-
return { js: outdent.outdent`
|
|
296
|
-
// Encoded as \`prop:className\`.
|
|
297
|
-
const utilities = "${utility.entries().map(([prop, className]) => `${prop}:${className}`).join(",")}"
|
|
298
|
-
|
|
299
|
-
export const classNameByProp = new Map()
|
|
300
|
-
utilities.split(',').forEach((entry) => {
|
|
301
|
-
const [prop, className] = entry.split(':')
|
|
302
|
-
classNameByProp.set(prop, className)
|
|
303
|
-
})
|
|
304
|
-
` };
|
|
305
|
-
}
|
|
306
|
-
//#endregion
|
|
307
213
|
//#region src/artifacts/js/cva.ts
|
|
308
214
|
function generateCvaFn(ctx) {
|
|
309
215
|
const { utility, hash, prefix } = ctx;
|
|
310
216
|
const withPrefix = prefix.className ? `(className) => className ? ${JSON.stringify(prefix.className)} + '-' + className : ${JSON.stringify(prefix.className)}` : `(className) => className`;
|
|
311
217
|
return {
|
|
312
218
|
js: outdent.outdent`
|
|
313
|
-
${ctx.file.import("cloneStyles, compact,
|
|
219
|
+
${ctx.file.import("cloneStyles, compact, getRecipeIdentity, mergeProps, memo, splitProps, toHash, uniq, uncompiledStyle", "../helpers")}
|
|
314
220
|
${ctx.file.import("mergeCss", "./merge-css")}
|
|
315
221
|
${ctx.file.import("cx", "./cx")}
|
|
316
222
|
|
|
@@ -380,8 +286,8 @@ function generateCvaFn(ctx) {
|
|
|
380
286
|
// Compound variants are absent on purpose. Their rule selects on the variant classes
|
|
381
287
|
// already in this list — \`.btn--size_sm.btn--tone_a\` — so it applies without anything
|
|
382
288
|
// being added here, and adding a class for it would name a rule that does not exist.
|
|
383
|
-
function cvaFn(
|
|
384
|
-
return
|
|
289
|
+
function cvaFn(_props) {
|
|
290
|
+
return uncompiledStyle('cva')
|
|
385
291
|
}
|
|
386
292
|
|
|
387
293
|
const variantKeys = Object.keys(variants)
|
|
@@ -620,7 +526,7 @@ function generateCx(ctx) {
|
|
|
620
526
|
}
|
|
621
527
|
//#endregion
|
|
622
528
|
//#region src/artifacts/generated/helpers.mjs.json
|
|
623
|
-
var content$7 = "//#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/error.ts\nvar BambooError = class extends Error {\n code;\n hint;\n constructor(code, message, opts) {\n super(message, { cause: opts?.cause });\n this.code = `ERR_BAMBOO_${code}`;\n this.hint = opts?.hint;\n }\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)) return null;\n const proto = Object.getPrototypeOf(obj);\n if (proto !== Object.prototype && proto !== null) return null;\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\n/**\n* Whether walking the object would only rebuild it.\n*\n* Normalizing does two things: it renames a shorthand to its longhand and drops nullish\n* leaves. A flat object of plain values written in longhand needs neither, and that is most\n* of what `css()` is handed — but it 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 has to reach the walk to be rejected rather than being returned as it came.\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*\n* An array is not a style value. It used to be read as one value per breakpoint, which meant\n* a font stack written the way CSS writes one — `['Inter', 'sans-serif']` — silently became\n* `Inter` at base and `sans-serif` at `sm`. The type no longer admits an array, so reaching\n* this throw takes a cast or untyped javascript; it says which property, since the walk knows\n* the path and the caller usually does not.\n*/\nfunction normalizeStyleObject(styles, context, shorthand = true) {\n const { utility } = context;\n const { hasShorthand, resolveShorthand } = utility;\n if (needsNoNormalizing(styles, shorthand && hasShorthand ? resolveShorthand : void 0)) return styles;\n return walkObject(styles, (value, path) => {\n if (Array.isArray(value)) throw new BambooError(\"INVALID_STYLE_VALUE\", `An array is not a style value${path.length ? `: \"${path.join(\".\")}\"` : \"\"}.`, { hint: \"Write a responsive value as a condition object, e.g. { base: \\\"medium\\\", lg: \\\"bold\\\" }.\" });\n return 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};\n/**\n* Name a style object, without caching the answer.\n*\n* For callers already sitting behind a memo keyed on the same call. `css` is the one that\n* matters:\n*\n* css = memo((...styles) => cssFn(mergeCssUncached(...styles)))\n*\n* reaches `cssFn` only when its own cache missed, and the merged object it passes is a\n* deterministic function of those same arguments — so a second cache on it cannot hit.\n* Measured over 25k calls it served zero hits across every workload, including working sets\n* larger than `MAX_ENTRIES`, where both caches rotate in lockstep rather than one rescuing\n* the other. The same applies wherever `createCss` is called *inside* the memoized function,\n* as the generated recipe runtime does: a fresh cache built per call is used once.\n*\n* Use `createCss` instead when there is no such memo above — the vite fold reaches it\n* directly, once per folded call site, and the merge is many-to-one there, so it hits.\n*/\nfunction createCssUncached(context) {\n const { utility, hash, 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 /** The class for one declaration. */\n const atomicName = (prop, value, conditions) => {\n const important = isImportant(value);\n const className = hashFn(conditions, utility.transform(prop, withoutImportant(sanitize(value))).className);\n return important ? `${className}!` : className;\n };\n return ({ 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 [prop, ...allConditions] = conds.shift(paths);\n classNames.add(atomicName(prop, value, filterBaseConditions(allConditions)));\n });\n return Array.from(classNames).join(\" \");\n };\n}\n/**\n* `createCssUncached`, cached.\n*\n* For callers that reach it directly and repeatedly with no memo of their own — the vite\n* fold builds one per build and shares it across every module. There the cache earns its\n* keep twice over: call sites repeat across a codebase, and the merge feeding it is\n* many-to-one, so `css({a}, {b})` and `css({a, b})` land on the same entry. Measured 2-35%\n* hits across the projects in this repo, and dropping it cost +187% on the fold.\n*/\nfunction createCss(context) {\n return memo(createCssUncached(context));\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}\n/**\n* An array is not a style argument.\n*\n* `css([a, b])` used to mean `css(a, b)`, flattened here one level. Two spellings of one\n* call is the redundancy; the array one also cost a `flat()` allocation on every merge to\n* serve a shape almost nothing wrote, and read as a responsive array everywhere that had\n* not been taught to flatten it first.\n*\n* It throws rather than being filtered out as a non-object, which is what dropping the\n* `flat()` alone would have done — silently returning no class at all.\n*/\nfunction compactStyles(...styles) {\n return styles.filter((style) => {\n if (Array.isArray(style)) throw new BambooError(\"INVALID_STYLE_ARGUMENT\", \"An array is not a style argument.\", { hint: \"Spread it instead, e.g. css(...styles) rather than css(styles).\" });\n return isObject(style) && hasDefinedValue(style);\n });\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 return {\n mergeCss: memo(mergeCss),\n mergeCssUncached: mergeCss\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 not a style value at all, and `normalizeStyleObject` throws saying so. That\n* diagnostic names the property, which this cannot, so the array has to reach it.\n* - An object is a condition block, walked into rather than named.\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/recipe-identity.ts\n/**\n* The fields that decide what CSS a recipe produces. Anything else is metadata.\n*\n* `slots` and `scopeRoots` count. They do not change a declaration, but they change the\n* *shape* of what is emitted — which slots exist, and whether a slot's variants become\n* `@scope` rules or a class of its own. Two `sva`s differing only in `scopeRoots` hashed to\n* one name, and since an inline recipe is registered once, whichever was extracted first\n* decided the emission for both; the other's runtime then asked for classes no rule\n* existed under. \"Same styles, different DOM topology\" is exactly what `scopeRoots` is for,\n* so it is the collision most likely to happen.\n*/\nconst STYLE_FIELDS = [\n \"base\",\n \"variants\",\n \"compoundVariants\",\n \"defaultVariants\",\n \"slots\",\n \"scopeRoots\"\n];\n/**\n* A serialization that depends on the config's *content* and not on how it was written.\n*\n* Object keys are sorted, so reordering two variants in the source does not rename every\n* class the recipe emits. Arrays keep their order, because `compoundVariants` is precedence\n* ordered and two orderings are two different recipes.\n*\n* A function serializes to `null` rather than to its source. `JSON.stringify` already drops\n* them, and stringifying instead would key the name on whether the bundle was minified —\n* the same build-dependent divergence that `cx` was changed to avoid. Nothing that survives\n* static extraction is a function, so there is no real config this loses information about.\n*/\n/**\n* Runs of whitespace inside a declaration value, collapsed to one space.\n*\n* The build never sees the value as written. `maybe-box-node` reads every string literal\n* through `trimWhitespace`, so `'calc(100vh - 16px)'` is `'calc(100vh - 16px)'` by the time\n* a recipe config reaches the encoder — the two produce identical CSS, and the stylesheet\n* emits one rule for both.\n*\n* The browser holds the config as authored. Without this, the two sides hashed different\n* objects and derived different names, so the element asked for a class the stylesheet did\n* not carry and rendered with *none* of the recipe's styles. Silent, and invisible to a\n* dead-rule check: the extra name leaves no unused rule behind, because the collapsed config\n* is byte-identical to one that was already emitted.\n*\n* The regex is `trimWhitespace`'s, deliberately. A second spelling of \"the same value\" is a\n* second thing to keep in agreement, which is the defect this is fixing.\n*/\nconst collapseWhitespace = (value) => value.replaceAll(/\\s+/g, \" \");\nconst stable = (value) => {\n if (typeof value === \"string\") return JSON.stringify(collapseWhitespace(value));\n if (value === null || typeof value !== \"object\") return JSON.stringify(value) ?? \"null\";\n if (Array.isArray(value)) return `[${value.map(stable).join(\",\")}]`;\n const source = value;\n return `{${Object.keys(source).sort().filter((key) => source[key] !== void 0 && source[key] !== null).map((key) => `${JSON.stringify(key)}:${stable(source[key])}`).join(\",\")}}`;\n};\n/**\n* The name an inline `cva`/`sva` emits its classes under — `button--size_sm`, where this\n* returns the `button`.\n*\n* A config recipe gets its name from the key it is declared under. An inline one has no\n* such key, and the two places that need the name never meet: the build derives it while\n* emitting the stylesheet, the runtime derives it again in the browser. So it has to come\n* from something both of them see, which leaves the config object itself.\n*\n* Deriving it from the *binding* — `const button = cva(...)` — was the obvious alternative\n* and does not work. Only the build can see that binding; handing it to the runtime means\n* rewriting the call, and then a pipeline without that transform names classes differently\n* from one with it. An optional `className` gets the same readable output with none of\n* that, because it travels inside the config to both sides.\n*\n* `className` is the field a config recipe already names itself with, and it means the same\n* thing here — the prefix every class the recipe emits is built from. An inline recipe that\n* declares one is indistinguishable in the stylesheet from a recipe declared in config.\n*/\nconst getRecipeIdentity = (config, prefix = \"cva\") => {\n const declared = config?.className;\n if (typeof declared === \"string\" && declared) return declared;\n const styles = {};\n for (const field of STYLE_FIELDS) {\n const value = config?.[field];\n if (value !== void 0) styles[field] = value;\n }\n return `${prefix}_${toHash(stable(styles))}`;\n};\n/**\n* The classes a recipe puts on an element: its own, plus one per selected variant.\n*\n* Lives here rather than in the generated `cva` because the build has to be able to check\n* it. `checkNamingAgreement` derives class names both ways and compares them, and it can\n* only do that against the code the browser actually runs — a second implementation written\n* to match would agree with itself and prove nothing.\n*\n* Compound variants are absent by design. Their rule selects on the variant classes already\n* in this list, so it applies without a class of its own.\n*/\nconst getRecipeClassNames = (name, variants, selection, separator = \"_\", format = (className) => className) => {\n let result = format(name);\n for (const variant of Object.keys(variants ?? {})) {\n const value = selection[variant];\n if (value == null) continue;\n const declared = variants?.[variant];\n if (!declared || !Object.hasOwn(declared, value) || declared[value] == null) continue;\n result += ` ${format(`${name}--${variant}${separator}${withoutSpace(value)}`)}`;\n }\n return result;\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 createPatternFns = (token) => ({\n map: mapObject,\n isCssFunction,\n isCssVar,\n isCssUnit,\n token\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* Move one key into a bucket, keeping whatever about it is observable.\n*\n* Shared by both paths below so there is one implementation of the descriptor rules rather\n* than two to keep in step. The rules themselves are documented on `splitProps`.\n*/\nconst copyKey = (props, target, key) => {\n const descriptor = Object.getOwnPropertyDescriptor(props, key);\n if (!descriptor) return false;\n if (\"get\" in descriptor || \"set\" in descriptor || !descriptor.enumerable || key === \"__proto__\") Object.defineProperty(target, key, descriptor);\n else target[key] = descriptor.value;\n return true;\n};\n/**\n* One array group, which is what every call site in this project passes — a recipe's\n* `variantKeys`.\n*\n* The general path below is built for several groups that may be predicates, and pays for\n* that shape on every call: a closure per group, a `map` and a `concat` to assemble the\n* result, and a branch per group to tell an array from a predicate. None of it is reachable\n* with one array group.\n*\n* What it does *not* skip is the part that looks skippable. `own` stays, because membership\n* has to be answered from `ownKeys` rather than by asking the object: on a proxy — which is\n* what Solid's `mergeProps` hands over — every question is a trap, and a recipe naming eight\n* variants would otherwise fire eight traps to learn what one `ownKeys` already said. And\n* the two passes stay separate, because the group bucket is in *group* order while the rest\n* bucket is in *props* order, and that ordering reaches the emitted CSS.\n*/\nconst splitOneGroup = (props, allKeys, group) => {\n const own = new Set(allKeys);\n const taken = /* @__PURE__ */ new Set();\n const picked = {};\n for (let i = 0; i < group.length; i++) {\n const key = group[i];\n if (taken.has(key) || !own.has(key)) continue;\n if (copyKey(props, picked, key)) taken.add(key);\n }\n const rest = {};\n for (let i = 0; i < allKeys.length; i++) {\n const key = allKeys[i];\n if (taken.has(key)) continue;\n copyKey(props, rest, key);\n }\n return [picked, rest];\n};\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 if (keys.length === 1 && Array.isArray(keys[0])) return splitOneGroup(props, allKeys, keys[0]);\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 if (copyKey(props, clone, key)) 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, createCssUncached, createMergeCss, createPatternFns, filterBaseConditions, getPatternStyles, getRecipeClassNames, getRecipeIdentity, getSlotCompoundVariant, getSlotRecipes, hypenateProperty, isBaseCondition, isObject, leafClass, mapObject, memo, mergeProps, splitProps, toHash, uniq, viewTransitionClassName, walkObject, withoutSpace };\n";
|
|
529
|
+
var content$7 = "//#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/error.ts\nvar BambooError = class extends Error {\n code;\n hint;\n constructor(code, message, opts) {\n super(message, { cause: opts?.cause });\n this.code = `ERR_BAMBOO_${code}`;\n this.hint = opts?.hint;\n }\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/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)) return null;\n const proto = Object.getPrototypeOf(obj);\n if (proto !== Object.prototype && proto !== null) return null;\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\n/**\n* Whether walking the object would only rebuild it.\n*\n* Normalizing does two things: it renames a shorthand to its longhand and drops nullish\n* leaves. A flat object of plain values written in longhand needs neither, and that is most\n* of what `css()` is handed — but it 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 has to reach the walk to be rejected rather than being returned as it came.\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*\n* An array is not a style value. It used to be read as one value per breakpoint, which meant\n* a font stack written the way CSS writes one — `['Inter', 'sans-serif']` — silently became\n* `Inter` at base and `sans-serif` at `sm`. The type no longer admits an array, so reaching\n* this throw takes a cast or untyped javascript; it says which property, since the walk knows\n* the path and the caller usually does not.\n*/\nfunction normalizeStyleObject(styles, context, shorthand = true) {\n const { utility } = context;\n const { hasShorthand, resolveShorthand } = utility;\n if (needsNoNormalizing(styles, shorthand && hasShorthand ? resolveShorthand : void 0)) return styles;\n return walkObject(styles, (value, path) => {\n if (Array.isArray(value)) throw new BambooError(\"INVALID_STYLE_VALUE\", `An array is not a style value${path.length ? `: \"${path.join(\".\")}\"` : \"\"}.`, { hint: \"Write a responsive value as a condition object, e.g. { base: \\\"medium\\\", lg: \\\"bold\\\" }.\" });\n return 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\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}\n/**\n* An array is not a style argument.\n*\n* `css([a, b])` used to mean `css(a, b)`, flattened here one level. Two spellings of one\n* call is the redundancy; the array one also cost a `flat()` allocation on every merge to\n* serve a shape almost nothing wrote, and read as a responsive array everywhere that had\n* not been taught to flatten it first.\n*\n* It throws rather than being filtered out as a non-object, which is what dropping the\n* `flat()` alone would have done — silently returning no class at all.\n*/\nfunction compactStyles(...styles) {\n return styles.filter((style) => {\n if (Array.isArray(style)) throw new BambooError(\"INVALID_STYLE_ARGUMENT\", \"An array is not a style argument.\", { hint: \"Spread it instead, e.g. css(...styles) rather than css(styles).\" });\n return isObject(style) && hasDefinedValue(style);\n });\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 return {\n mergeCss: memo(mergeCss),\n mergeCssUncached: mergeCss\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/recipe-identity.ts\n/**\n* The fields that decide what CSS a recipe produces. Anything else is metadata.\n*\n* `slots` and `scopeRoots` count. They do not change a declaration, but they change the\n* *shape* of what is emitted — which slots exist, and whether a slot's variants become\n* `@scope` rules or a class of its own. Two `sva`s differing only in `scopeRoots` hashed to\n* one name, and since an inline recipe is registered once, whichever was extracted first\n* decided the emission for both; the other's runtime then asked for classes no rule\n* existed under. \"Same styles, different DOM topology\" is exactly what `scopeRoots` is for,\n* so it is the collision most likely to happen.\n*/\nconst STYLE_FIELDS = [\n \"base\",\n \"variants\",\n \"compoundVariants\",\n \"defaultVariants\",\n \"slots\",\n \"scopeRoots\"\n];\n/**\n* A serialization that depends on the config's *content* and not on how it was written.\n*\n* Object keys are sorted, so reordering two variants in the source does not rename every\n* class the recipe emits. Arrays keep their order, because `compoundVariants` is precedence\n* ordered and two orderings are two different recipes.\n*\n* A function serializes to `null` rather than to its source. `JSON.stringify` already drops\n* them, and stringifying instead would key the name on whether the bundle was minified —\n* the same build-dependent divergence that `cx` was changed to avoid. Nothing that survives\n* static extraction is a function, so there is no real config this loses information about.\n*/\n/**\n* Runs of whitespace inside a declaration value, collapsed to one space.\n*\n* The build never sees the value as written. `maybe-box-node` reads every string literal\n* through `trimWhitespace`, so `'calc(100vh - 16px)'` is `'calc(100vh - 16px)'` by the time\n* a recipe config reaches the encoder — the two produce identical CSS, and the stylesheet\n* emits one rule for both.\n*\n* The browser holds the config as authored. Without this, the two sides hashed different\n* objects and derived different names, so the element asked for a class the stylesheet did\n* not carry and rendered with *none* of the recipe's styles. Silent, and invisible to a\n* dead-rule check: the extra name leaves no unused rule behind, because the collapsed config\n* is byte-identical to one that was already emitted.\n*\n* The regex is `trimWhitespace`'s, deliberately. A second spelling of \"the same value\" is a\n* second thing to keep in agreement, which is the defect this is fixing.\n*/\nconst collapseWhitespace = (value) => value.replaceAll(/\\s+/g, \" \");\nconst stable = (value) => {\n if (typeof value === \"string\") return JSON.stringify(collapseWhitespace(value));\n if (value === null || typeof value !== \"object\") return JSON.stringify(value) ?? \"null\";\n if (Array.isArray(value)) return `[${value.map(stable).join(\",\")}]`;\n const source = value;\n return `{${Object.keys(source).sort().filter((key) => source[key] !== void 0 && source[key] !== null).map((key) => `${JSON.stringify(key)}:${stable(source[key])}`).join(\",\")}}`;\n};\n/**\n* The name an inline `cva`/`sva` emits its classes under — `button--size_sm`, where this\n* returns the `button`.\n*\n* A config recipe gets its name from the key it is declared under. An inline one has no\n* such key, and the two places that need the name never meet: the build derives it while\n* emitting the stylesheet, the runtime derives it again in the browser. So it has to come\n* from something both of them see, which leaves the config object itself.\n*\n* Deriving it from the *binding* — `const button = cva(...)` — was the obvious alternative\n* and does not work. Only the build can see that binding; handing it to the runtime means\n* rewriting the call, and then a pipeline without that transform names classes differently\n* from one with it. An optional `className` gets the same readable output with none of\n* that, because it travels inside the config to both sides.\n*\n* `className` is the field a config recipe already names itself with, and it means the same\n* thing here — the prefix every class the recipe emits is built from. An inline recipe that\n* declares one is indistinguishable in the stylesheet from a recipe declared in config.\n*/\nconst getRecipeIdentity = (config, prefix = \"cva\") => {\n const declared = config?.className;\n if (typeof declared === \"string\" && declared) return declared;\n const styles = {};\n for (const field of STYLE_FIELDS) {\n const value = config?.[field];\n if (value !== void 0) styles[field] = value;\n }\n return `${prefix}_${toHash(stable(styles))}`;\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 createPatternFns = (token) => ({\n map: mapObject,\n isCssFunction,\n isCssVar,\n isCssUnit,\n token\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* Move one key into a bucket, keeping whatever about it is observable.\n*\n* Shared by both paths below so there is one implementation of the descriptor rules rather\n* than two to keep in step. The rules themselves are documented on `splitProps`.\n*/\nconst copyKey = (props, target, key) => {\n const descriptor = Object.getOwnPropertyDescriptor(props, key);\n if (!descriptor) return false;\n if (\"get\" in descriptor || \"set\" in descriptor || !descriptor.enumerable || key === \"__proto__\") Object.defineProperty(target, key, descriptor);\n else target[key] = descriptor.value;\n return true;\n};\n/**\n* One array group, which is what every call site in this project passes — a recipe's\n* `variantKeys`.\n*\n* The general path below is built for several groups that may be predicates, and pays for\n* that shape on every call: a closure per group, a `map` and a `concat` to assemble the\n* result, and a branch per group to tell an array from a predicate. None of it is reachable\n* with one array group.\n*\n* What it does *not* skip is the part that looks skippable. `own` stays, because membership\n* has to be answered from `ownKeys` rather than by asking the object: on a proxy — which is\n* what Solid's `mergeProps` hands over — every question is a trap, and a recipe naming eight\n* variants would otherwise fire eight traps to learn what one `ownKeys` already said. And\n* the two passes stay separate, because the group bucket is in *group* order while the rest\n* bucket is in *props* order, and that ordering reaches the emitted CSS.\n*/\nconst splitOneGroup = (props, allKeys, group) => {\n const own = new Set(allKeys);\n const taken = /* @__PURE__ */ new Set();\n const picked = {};\n for (let i = 0; i < group.length; i++) {\n const key = group[i];\n if (taken.has(key) || !own.has(key)) continue;\n if (copyKey(props, picked, key)) taken.add(key);\n }\n const rest = {};\n for (let i = 0; i < allKeys.length; i++) {\n const key = allKeys[i];\n if (taken.has(key)) continue;\n copyKey(props, rest, key);\n }\n return [picked, rest];\n};\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 if (keys.length === 1 && Array.isArray(keys[0])) return splitOneGroup(props, allKeys, keys[0]);\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 if (copyKey(props, clone, key)) 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, createMergeCss, createPatternFns, getPatternStyles, getRecipeIdentity, getSlotCompoundVariant, getSlotRecipes, memo, mergeProps, splitProps, toHash, uniq };\n";
|
|
624
530
|
//#endregion
|
|
625
531
|
//#region src/artifacts/js/helpers.ts
|
|
626
532
|
function generateHelpers() {
|
|
@@ -634,6 +540,18 @@ function generateHelpers() {
|
|
|
634
540
|
export function __objRest(source, exclude) {
|
|
635
541
|
return Object.fromEntries(Object.entries(source).filter(([key]) => !exclude.includes(key)))
|
|
636
542
|
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Style-producing calls compile away. Hitting this means \`@bamboocss/vite\` did not fold
|
|
546
|
+
* the call — missing plugin, a file outside \`include\`, or a shape the compiler rejects.
|
|
547
|
+
*/
|
|
548
|
+
export function uncompiledStyle(name) {
|
|
549
|
+
throw new Error(
|
|
550
|
+
'bamboocss: ' +
|
|
551
|
+
name +
|
|
552
|
+
'() was not compiled. Add \`bamboocss()\` from \`@bamboocss/vite\` and import \`virtual:bamboo.css\`. See https://bamboocss.com/docs/installation/vite',
|
|
553
|
+
)
|
|
554
|
+
}
|
|
637
555
|
` };
|
|
638
556
|
}
|
|
639
557
|
//#endregion
|
|
@@ -752,7 +670,7 @@ function generatePattern(ctx, filters) {
|
|
|
752
670
|
transform,
|
|
753
671
|
defaultValues
|
|
754
672
|
})) ?? "";
|
|
755
|
-
const helperImports = ["getPatternStyles, createPatternFns
|
|
673
|
+
const helperImports = ["getPatternStyles, createPatternFns"];
|
|
756
674
|
if (patternConfigFn.includes("__spreadValues")) helperImports.push("__spreadValues");
|
|
757
675
|
if (patternConfigFn.includes("__objRest")) helperImports.push("__objRest");
|
|
758
676
|
return {
|
|
@@ -785,8 +703,7 @@ function generatePattern(ctx, filters) {
|
|
|
785
703
|
|
|
786
704
|
`,
|
|
787
705
|
js: outdent.outdent`
|
|
788
|
-
${ctx.file.import(helperImports.join(", "), "../helpers")}
|
|
789
|
-
${ctx.file.import("css", "../css/index")}
|
|
706
|
+
${ctx.file.import([...helperImports, "uncompiledStyle"].join(", "), "../helpers")}
|
|
790
707
|
${ctx.file.import("token", "../tokens/index")}
|
|
791
708
|
|
|
792
709
|
/**
|
|
@@ -806,7 +723,7 @@ function generatePattern(ctx, filters) {
|
|
|
806
723
|
return ${baseName}Config.transform(_styles, patternHelpers)
|
|
807
724
|
}
|
|
808
725
|
|
|
809
|
-
export const ${baseName} =
|
|
726
|
+
export const ${baseName} = (styles) => uncompiledStyle(${JSON.stringify(baseName)})
|
|
810
727
|
${baseName}.raw = ${styleFnName}
|
|
811
728
|
`
|
|
812
729
|
};
|
|
@@ -859,55 +776,12 @@ function generateCreateRecipe(ctx) {
|
|
|
859
776
|
name: "create-recipe",
|
|
860
777
|
dts: "",
|
|
861
778
|
js: outdent.outdent`
|
|
862
|
-
${ctx.file.import("
|
|
863
|
-
${ctx.file.import("assertCompoundVariant, getCompoundVariantCss", "../css/cva")}
|
|
864
|
-
${ctx.file.import("cx", "../css/cx")}
|
|
865
|
-
${ctx.file.import("compact, createCssUncached, getRecipeClassNames, splitProps, toHash, uniq, withoutSpace", "../helpers")}
|
|
779
|
+
${ctx.file.import(hash.className ? "compact, splitProps, toHash, uniq, uncompiledStyle" : "compact, splitProps, uniq, uncompiledStyle", "../helpers")}
|
|
866
780
|
|
|
867
|
-
/**
|
|
868
|
-
* What \`createCss\` does to a class name: prefix it, and hash it when \`hash.className\`
|
|
869
|
-
* is set.
|
|
870
|
-
*
|
|
871
|
-
* A slot that takes variants gets this for free, because its classes come from
|
|
872
|
-
* \`createCss\`. A *scoped* slot's class never goes through it — it is a constant — so it
|
|
873
|
-
* has to be formatted here or the runtime hands back a raw name while the stylesheet
|
|
874
|
-
* emits the rule under a hashed one, and the slot renders unstyled.
|
|
875
|
-
*/
|
|
876
781
|
const withPrefix = ${prefix.className ? `(className) => className ? ${JSON.stringify(prefix.className)} + '-' + className : ${JSON.stringify(prefix.className)}` : `(className) => className`}
|
|
877
782
|
export const formatRecipeClass = ${hash.className ? `(className) => withPrefix((${utility.toHash})([className], toHash))` : `withPrefix`}
|
|
878
783
|
|
|
879
|
-
export const createRecipe = (name, defaultVariants,
|
|
880
|
-
/**
|
|
881
|
-
* \`variantMap\` as \`getRecipeClassNames\` wants it — value *keys* rather than a list.
|
|
882
|
-
*
|
|
883
|
-
* Built once per recipe at module init. The lookup needs \`Object.hasOwn\`, and an array
|
|
884
|
-
* answers that for its indices rather than its contents, so a list cannot be passed
|
|
885
|
-
* straight through.
|
|
886
|
-
*/
|
|
887
|
-
const variantValues = variantMap
|
|
888
|
-
? Object.fromEntries(
|
|
889
|
-
Object.entries(variantMap).map(([variant, values]) => [
|
|
890
|
-
variant,
|
|
891
|
-
Object.fromEntries(values.map((value) => [value, true])),
|
|
892
|
-
]),
|
|
893
|
-
)
|
|
894
|
-
: undefined
|
|
895
|
-
|
|
896
|
-
/**
|
|
897
|
-
* Whether every selected value is a plain scalar, and so nameable without \`createCss\`.
|
|
898
|
-
*
|
|
899
|
-
* \`typeof value === 'object'\` covers a conditional value like \`{ base: 'sm', md: 'lg' }\`,
|
|
900
|
-
* whose classes carry condition prefixes only \`createCss\` can build. It also catches
|
|
901
|
-
* \`null\`, which \`compact\` keeps — that goes to the path below and comes out as it did
|
|
902
|
-
* before, since \`createCss\` names no class for a null value either.
|
|
903
|
-
*/
|
|
904
|
-
const isScalarSelection = (declared) => {
|
|
905
|
-
for (const key in declared) {
|
|
906
|
-
if (typeof declared[key] === 'object') return false
|
|
907
|
-
}
|
|
908
|
-
return true
|
|
909
|
-
}
|
|
910
|
-
|
|
784
|
+
export const createRecipe = (name, defaultVariants, _compoundVariants, _variantMap) => {
|
|
911
785
|
const getVariantProps = (variants) => {
|
|
912
786
|
return {
|
|
913
787
|
[name]: '__ignore__',
|
|
@@ -916,82 +790,12 @@ function generateCreateRecipe(ctx) {
|
|
|
916
790
|
};
|
|
917
791
|
};
|
|
918
792
|
|
|
919
|
-
const recipeFn = (
|
|
920
|
-
const declaredProps = getVariantProps(variants)
|
|
921
|
-
|
|
922
|
-
// A scalar selection names its classes by lookup: the recipe's own class plus one per
|
|
923
|
-
// selected variant, which is all \`createCss\` was deriving here. Measured at 4.1x the
|
|
924
|
-
// \`createCss\` path on a three-variant recipe. The gain is on a \`memo\` miss — the first
|
|
925
|
-
// call for each variant combination — since a hit never reaches this at all.
|
|
926
|
-
//
|
|
927
|
-
// Compound variants stay absent, as they are on the path below: their rule selects on the
|
|
928
|
-
// variant classes already named, so it applies without one of its own.
|
|
929
|
-
if (variantValues && isScalarSelection(declaredProps)) {
|
|
930
|
-
return getRecipeClassNames(name, variantValues, declaredProps, '${utility.separator}', formatRecipeClass)
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
const transform = (prop, value) => {
|
|
934
|
-
assertCompoundVariant(name, compoundVariants, variants, prop)
|
|
935
|
-
|
|
936
|
-
if (value === '__ignore__') {
|
|
937
|
-
return { className: name }
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
value = withoutSpace(value)
|
|
941
|
-
return { className: \`\${name}--\${prop}${utility.separator}\${value}\` }
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
// Uncached: this runs *inside* \`recipeFn\`, which is itself memoized, so the cache a
|
|
945
|
-
// cached \`createCss\` would build here is constructed fresh per call and used once.
|
|
946
|
-
const recipeCss = createCssUncached({
|
|
947
|
-
${hash.className ? "hash: true," : ""}
|
|
948
|
-
conditions: {
|
|
949
|
-
shift: sortConditions,
|
|
950
|
-
finalize: finalizeConditions,
|
|
951
|
-
},
|
|
952
|
-
utility: {
|
|
953
|
-
${prefix.className ? "prefix: " + JSON.stringify(prefix.className) + "," : ""}
|
|
954
|
-
toHash: ${utility.toHash},
|
|
955
|
-
transform,
|
|
956
|
-
}
|
|
957
|
-
})
|
|
958
|
-
|
|
959
|
-
// Only what the config declares names a class.
|
|
960
|
-
//
|
|
961
|
-
// Without this the transform named one for *any* prop it was handed — the build emits
|
|
962
|
-
// rules only for declared values, so the element carried a class nothing backed. It also
|
|
963
|
-
// disagreed with \`cva\`, which skips an undeclared value, leaving the two recipe kinds
|
|
964
|
-
// with different class strings for the same call.
|
|
965
|
-
//
|
|
966
|
-
// Filtered here rather than in \`getVariantProps\`, which is public and is what compound
|
|
967
|
-
// variants are matched against.
|
|
968
|
-
const declared = declaredProps
|
|
969
|
-
const recipeStyles = variantMap
|
|
970
|
-
? Object.fromEntries(
|
|
971
|
-
Object.entries(declared).filter(([prop, value]) => {
|
|
972
|
-
if (prop === name) return true
|
|
973
|
-
// A conditional or responsive value is an object of leaves, and the leaves are
|
|
974
|
-
// what name classes: createCss walks them and calls transform per condition.
|
|
975
|
-
// Only a scalar can be judged here.
|
|
976
|
-
if (value === null || typeof value === 'object') return true
|
|
977
|
-
return Object.hasOwn(variantMap, prop) && variantMap[prop].includes(String(value))
|
|
978
|
-
}),
|
|
979
|
-
)
|
|
980
|
-
: declared
|
|
981
|
-
|
|
982
|
-
// No class for the compound variants. Their rule selects on the variant classes
|
|
983
|
-
// \`recipeCss\` just named — \`.btn--size_sm.btn--tone_a\` — so it applies on its own,
|
|
984
|
-
// and it is in the same layer as the rest of the recipe rather than atomically in
|
|
985
|
-
// \`utilities\` above it.
|
|
986
|
-
return recipeCss(recipeStyles)
|
|
987
|
-
}
|
|
793
|
+
const recipeFn = (_variants) => uncompiledStyle(name)
|
|
988
794
|
|
|
989
795
|
return {
|
|
990
796
|
recipeFn,
|
|
991
797
|
getVariantProps,
|
|
992
|
-
__getCompoundVariantCss__: (
|
|
993
|
-
return getCompoundVariantCss(compoundVariants, getVariantProps(variants));
|
|
994
|
-
},
|
|
798
|
+
__getCompoundVariantCss__: (_variants) => uncompiledStyle(name),
|
|
995
799
|
}
|
|
996
800
|
}
|
|
997
801
|
|
|
@@ -999,7 +803,7 @@ function generateCreateRecipe(ctx) {
|
|
|
999
803
|
if (recipeA && !recipeB) return recipeA
|
|
1000
804
|
if (!recipeA && recipeB) return recipeB
|
|
1001
805
|
|
|
1002
|
-
const recipeFn = (...
|
|
806
|
+
const recipeFn = (..._args) => uncompiledStyle(recipeA.__name__ || 'recipe')
|
|
1003
807
|
const variantKeys = uniq(Object.keys(recipeA.variantMap), Object.keys(recipeB.variantMap))
|
|
1004
808
|
const variantMap = variantKeys.reduce((acc, key) => {
|
|
1005
809
|
acc[key] = uniq(recipeA.variantMap[key], recipeB.variantMap[key])
|
|
@@ -1195,7 +999,7 @@ function generateRecipes(ctx, filters) {
|
|
|
1195
999
|
function generateSvaFn(ctx) {
|
|
1196
1000
|
return {
|
|
1197
1001
|
js: outdent.outdent`
|
|
1198
|
-
${ctx.file.import("compact, getRecipeIdentity, getSlotRecipes, memo, splitProps", "../helpers")}
|
|
1002
|
+
${ctx.file.import("compact, getRecipeIdentity, getSlotRecipes, memo, splitProps, uncompiledStyle", "../helpers")}
|
|
1199
1003
|
${ctx.file.import("cva, formatRecipeClass", "./cva")}
|
|
1200
1004
|
${ctx.file.import("cx", "./cx")}
|
|
1201
1005
|
|
|
@@ -1237,12 +1041,8 @@ function generateSvaFn(ctx) {
|
|
|
1237
1041
|
// atomic and nothing else carried the name to target it in the DOM. The slot's cva is
|
|
1238
1042
|
// now named \`name__slot\` and returns that as its base class, so joining it again
|
|
1239
1043
|
// would just repeat it.
|
|
1240
|
-
function svaFn(
|
|
1241
|
-
|
|
1242
|
-
slot,
|
|
1243
|
-
anchors.length && !anchors.includes(slot) ? formatRecipeClass(cvaFn.config.className) : cvaFn(props),
|
|
1244
|
-
])
|
|
1245
|
-
return Object.fromEntries(result)
|
|
1044
|
+
function svaFn(_props) {
|
|
1045
|
+
return uncompiledStyle('sva')
|
|
1246
1046
|
}
|
|
1247
1047
|
|
|
1248
1048
|
function raw(props) {
|
|
@@ -3105,18 +2905,8 @@ function setupGeneratedSystemTypes(ctx) {
|
|
|
3105
2905
|
}
|
|
3106
2906
|
function setupCss(ctx) {
|
|
3107
2907
|
const code = generateCssFn(ctx);
|
|
3108
|
-
const conditions = generateConditions(ctx);
|
|
3109
2908
|
const mergeCss = generateMergeCssFn(ctx);
|
|
3110
|
-
const utilities = generateUtilitiesTable(ctx);
|
|
3111
2909
|
const files = [
|
|
3112
|
-
{
|
|
3113
|
-
file: ctx.file.ext("conditions"),
|
|
3114
|
-
code: conditions.js
|
|
3115
|
-
},
|
|
3116
|
-
{
|
|
3117
|
-
file: ctx.file.ext("utilities"),
|
|
3118
|
-
code: utilities.js
|
|
3119
|
-
},
|
|
3120
2910
|
{
|
|
3121
2911
|
file: ctx.file.ext("merge-css"),
|
|
3122
2912
|
code: mergeCss.js
|
|
@@ -3574,9 +3364,9 @@ function generateResetCss(ctx, sheet) {
|
|
|
3574
3364
|
}
|
|
3575
3365
|
//#endregion
|
|
3576
3366
|
//#region src/artifacts/css/static-css.ts
|
|
3577
|
-
const generateStaticCss = (ctx, sheet) => {
|
|
3367
|
+
const generateStaticCss = (ctx, sheet, options) => {
|
|
3578
3368
|
const { config, staticCss } = ctx;
|
|
3579
|
-
const engine = staticCss.process(ctx.config.staticCss ?? {}, sheet);
|
|
3369
|
+
const engine = staticCss.process(ctx.config.staticCss ?? {}, sheet, options);
|
|
3580
3370
|
if (!sheet) {
|
|
3581
3371
|
const { minify } = config;
|
|
3582
3372
|
let css = engine.sheet.toCss({ minify });
|
|
@@ -3935,10 +3725,10 @@ var Generator = class extends _bamboocss_core.Context {
|
|
|
3935
3725
|
appendLayerParams = (sheet) => {
|
|
3936
3726
|
sheet.layers.root.prepend(sheet.layers.params);
|
|
3937
3727
|
};
|
|
3938
|
-
appendBaselineCss = (sheet) => {
|
|
3728
|
+
appendBaselineCss = (sheet, { atomizeRecipes = false } = {}) => {
|
|
3939
3729
|
if (this.config.preflight) this.appendCssOfType("preflight", sheet);
|
|
3940
3730
|
if (!this.tokens.isEmpty) this.appendCssOfType("tokens", sheet);
|
|
3941
|
-
this
|
|
3731
|
+
generateStaticCss(this, sheet, { atomizeRecipes });
|
|
3942
3732
|
this.appendCssOfType("global", sheet);
|
|
3943
3733
|
if (this.config.theme?.keyframes) this.appendCssOfType("keyframes", sheet);
|
|
3944
3734
|
};
|
|
@@ -4260,28 +4050,11 @@ var Generator = class extends _bamboocss_core.Context {
|
|
|
4260
4050
|
return sheet.getLayerCss(layer);
|
|
4261
4051
|
};
|
|
4262
4052
|
/**
|
|
4263
|
-
* Get CSS for a specific recipe
|
|
4264
|
-
*/
|
|
4265
|
-
getRecipeCss = (recipeName) => {
|
|
4266
|
-
const sheet = this.createSheet();
|
|
4267
|
-
const decoder = this.decoder.collect(this.encoder);
|
|
4268
|
-
sheet.processDecoderForRecipe(decoder, recipeName);
|
|
4269
|
-
return sheet.getLayerCss("recipes");
|
|
4270
|
-
};
|
|
4271
|
-
/**
|
|
4272
|
-
* Get all recipe names from the decoder
|
|
4273
|
-
*/
|
|
4274
|
-
getRecipeNames = () => {
|
|
4275
|
-
const decoder = this.decoder.collect(this.encoder);
|
|
4276
|
-
return Array.from(decoder.recipes.keys());
|
|
4277
|
-
};
|
|
4278
|
-
/**
|
|
4279
4053
|
* Get all split CSS artifacts for the stylesheet
|
|
4280
4054
|
* Used when --splitting flag is enabled
|
|
4281
4055
|
*/
|
|
4282
4056
|
getSplitCssArtifacts = (sheet) => {
|
|
4283
4057
|
const layerNames = this.config.layers;
|
|
4284
|
-
const decoder = this.decoder.collect(this.encoder);
|
|
4285
4058
|
const layers = [
|
|
4286
4059
|
{
|
|
4287
4060
|
name: "reset",
|
|
@@ -4309,19 +4082,6 @@ var Generator = class extends _bamboocss_core.Context {
|
|
|
4309
4082
|
file: l.file,
|
|
4310
4083
|
code: l.css
|
|
4311
4084
|
}));
|
|
4312
|
-
const recipes = [];
|
|
4313
|
-
for (const recipeName of this.recipes.keys) {
|
|
4314
|
-
const recipeSheet = this.createSheet();
|
|
4315
|
-
recipeSheet.processDecoderForRecipe(decoder, recipeName);
|
|
4316
|
-
const code = recipeSheet.getLayerCss("recipes");
|
|
4317
|
-
if (code.trim()) recipes.push({
|
|
4318
|
-
type: "recipe",
|
|
4319
|
-
name: recipeName,
|
|
4320
|
-
file: `${(0, _bamboocss_shared.dashCase)(recipeName)}.css`,
|
|
4321
|
-
code,
|
|
4322
|
-
dir: "recipes"
|
|
4323
|
-
});
|
|
4324
|
-
}
|
|
4325
4085
|
const themes = [];
|
|
4326
4086
|
if (this.config.theme?.variants) for (const themeName of Object.keys(this.config.theme?.variants)) {
|
|
4327
4087
|
const css = getThemeCss(this, themeName);
|
|
@@ -4333,21 +4093,16 @@ var Generator = class extends _bamboocss_core.Context {
|
|
|
4333
4093
|
dir: "themes"
|
|
4334
4094
|
});
|
|
4335
4095
|
}
|
|
4336
|
-
const recipesIndex = recipes.map((r) => `@import './recipes/${r.file}';`).join("\n");
|
|
4337
4096
|
const imports = [`@layer ${[
|
|
4338
4097
|
layerNames.reset,
|
|
4339
4098
|
layerNames.base,
|
|
4340
4099
|
layerNames.tokens,
|
|
4341
|
-
layerNames.recipes,
|
|
4342
4100
|
layerNames.utilities
|
|
4343
4101
|
].join(", ")};`, ""];
|
|
4344
4102
|
for (const layer of layers) imports.push(`@import './styles/${layer.file}';`);
|
|
4345
|
-
if (recipes.length) imports.push(`@import './styles/recipes.css';`);
|
|
4346
4103
|
return {
|
|
4347
4104
|
layers,
|
|
4348
|
-
recipes,
|
|
4349
4105
|
themes,
|
|
4350
|
-
recipesIndex,
|
|
4351
4106
|
index: imports.join("\n")
|
|
4352
4107
|
};
|
|
4353
4108
|
};
|