@bamboocss/generator 1.12.3 → 1.13.2
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/LICENSE.md +1 -0
- package/dist/index.cjs +255 -32
- package/dist/index.d.cts +61 -0
- package/dist/index.d.mts +61 -0
- package/dist/index.mjs +257 -34
- package/package.json +9 -9
package/dist/index.d.mts
CHANGED
|
@@ -29,6 +29,67 @@ declare class Generator extends Context {
|
|
|
29
29
|
appendLayerParams: (sheet: Stylesheet) => void;
|
|
30
30
|
appendBaselineCss: (sheet: Stylesheet) => void;
|
|
31
31
|
appendParserCss: (sheet: Stylesheet) => void;
|
|
32
|
+
/**
|
|
33
|
+
* Drop token css variables nothing can reach. Call this only once the sheet holds the
|
|
34
|
+
* whole stylesheet — a baseline-only sheet has no utilities to reference anything, so
|
|
35
|
+
* every token would look unused.
|
|
36
|
+
*
|
|
37
|
+
* `keep` carries references this cannot see for itself; see `collectTokenReferences`.
|
|
38
|
+
*/
|
|
39
|
+
pruneTokens: (sheet: Stylesheet, keep?: Set<string>) => {
|
|
40
|
+
removed: number;
|
|
41
|
+
kept: number;
|
|
42
|
+
} | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Drop `@keyframes` nothing can reach. Same completeness requirement as
|
|
45
|
+
* `pruneTokens`: the sheet has to hold the whole stylesheet, or every keyframe looks
|
|
46
|
+
* unused for want of a utility to reference it.
|
|
47
|
+
*
|
|
48
|
+
* `keep` carries names this cannot see for itself; see `collectKeyframeReferences`.
|
|
49
|
+
*/
|
|
50
|
+
pruneKeyframes: (sheet: Stylesheet, keep?: Set<string>) => {
|
|
51
|
+
removed: number;
|
|
52
|
+
kept: number;
|
|
53
|
+
} | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Keyframes the themes name.
|
|
56
|
+
*
|
|
57
|
+
* A theme is emitted as its own artifact and injected at runtime, so its css is not in
|
|
58
|
+
* the sheet being pruned. A theme that points an animation token at a different
|
|
59
|
+
* keyframe than the base does — `--animations-enter: fade-in` in the base and
|
|
60
|
+
* `slide-up` under `dark` — would otherwise have that keyframe removed, because
|
|
61
|
+
* nothing in the pruned sheet ever names it.
|
|
62
|
+
*/
|
|
63
|
+
private getThemeKeyframeNames;
|
|
64
|
+
/**
|
|
65
|
+
* Every custom property the token system declares. Used as the allow-list of what may
|
|
66
|
+
* be removed, so custom properties from `globalCss` are never touched.
|
|
67
|
+
*/
|
|
68
|
+
private getTokenVarNames;
|
|
69
|
+
/**
|
|
70
|
+
* Everything the themes refer to.
|
|
71
|
+
*
|
|
72
|
+
* A theme is emitted as its own artifact and injected at runtime, so its css is not in
|
|
73
|
+
* the sheet being pruned and nothing there points at what it needs. A theme that maps a
|
|
74
|
+
* token onto a base colour would otherwise be left referring to a declaration that has
|
|
75
|
+
* been removed.
|
|
76
|
+
*/
|
|
77
|
+
private getThemeTokenVars;
|
|
78
|
+
/**
|
|
79
|
+
* Tokens whose javascript value is a `var()` reference rather than a literal.
|
|
80
|
+
* `token('colors.text')` hands those to the caller as a reference, so the declaration
|
|
81
|
+
* has to survive whether or not the generated css mentions it. Ordinary tokens resolve
|
|
82
|
+
* to a literal in javascript and need no such exemption.
|
|
83
|
+
*
|
|
84
|
+
* The two cases mirror `generateTokenJs`, which is what decides the value javascript
|
|
85
|
+
* actually receives:
|
|
86
|
+
*
|
|
87
|
+
* - A virtual token, or one carrying a condition, is handed its own `varRef`.
|
|
88
|
+
* - A negative token is handed `calc(var(--x) * -1)`, so it is a reference too — but to
|
|
89
|
+
* the *positive* token's declaration. Its own var is never declared, so the name has
|
|
90
|
+
* to come out of the value.
|
|
91
|
+
*/
|
|
92
|
+
private getAlwaysKeptTokenVars;
|
|
32
93
|
getParserCss: (decoder: StyleDecoder) => string;
|
|
33
94
|
getCss: (stylesheet?: Stylesheet) => string;
|
|
34
95
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { Context, Recipes, expandNestedCss, extractParentSelectors, extractTrailingPseudos, stringify } from "@bamboocss/core";
|
|
2
|
-
import {
|
|
1
|
+
import { Context, Recipes, expandNestedCss, extractParentSelectors, extractTrailingPseudos, pruneKeyframes, pruneTokenVars, stringify } from "@bamboocss/core";
|
|
2
|
+
import { logger } from "@bamboocss/logger";
|
|
3
|
+
import { BambooError, capitalize, compact, cssVarRefs, dashCase, isBoolean, isObject, mapEntries, unionType, uniq, walkObject } from "@bamboocss/shared";
|
|
3
4
|
import { match } from "ts-pattern";
|
|
4
5
|
import outdent$1, { outdent } from "outdent";
|
|
5
6
|
import { stringify as stringify$1 } from "javascript-stringify";
|
|
6
7
|
import { allCssProperties } from "@bamboocss/is-valid-prop";
|
|
7
8
|
import pluralize from "pluralize";
|
|
8
|
-
import { logger } from "@bamboocss/logger";
|
|
9
9
|
import postcss, { CssSyntaxError } from "postcss";
|
|
10
10
|
//#region src/artifacts/js/conditions.ts
|
|
11
11
|
function formatConditionJsDoc(raw) {
|
|
@@ -138,9 +138,15 @@ function generateCssFn(ctx) {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
export declare const css: CssFunction;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Internal. Emitted for the source transform, which rewrites a single dynamic style
|
|
144
|
+
* leaf into a call to this. Not part of the authoring API.
|
|
145
|
+
*/
|
|
146
|
+
export declare const cssLeaf: (prefix: string, prop: string, value: unknown) => string;
|
|
141
147
|
`,
|
|
142
148
|
js: outdent`
|
|
143
|
-
${ctx.file.import("createCss, createMergeCss, hypenateProperty, withoutSpace", "../helpers")}
|
|
149
|
+
${ctx.file.import("cloneStyles, createCss, createMergeCss, hypenateProperty, leafClass, memo, withoutSpace", "../helpers")}
|
|
144
150
|
${ctx.file.import("sortConditions, finalizeConditions", "./conditions")}
|
|
145
151
|
|
|
146
152
|
const utilities = "${utility.entries().map(([prop, className]) => {
|
|
@@ -191,8 +197,19 @@ function generateCssFn(ctx) {
|
|
|
191
197
|
}
|
|
192
198
|
|
|
193
199
|
const cssFn = createCss(context)
|
|
194
|
-
export const css = (...styles) => cssFn(mergeCss(...styles))
|
|
195
|
-
|
|
200
|
+
export const css = /* @__PURE__ */ memo((...styles) => cssFn(mergeCss(...styles)))
|
|
201
|
+
// The merged result is cached and shared, so a caller mutating a nested
|
|
202
|
+
// condition object would otherwise poison it for everyone after them.
|
|
203
|
+
css.raw = (...styles) => cloneStyles(mergeCss(...styles))
|
|
204
|
+
|
|
205
|
+
// Emitted for the source transform, which rewrites a single dynamic style leaf into a
|
|
206
|
+
// call to this rather than leaving a \`css()\` behind. \`prefix\` is the class up to the
|
|
207
|
+
// value, resolved at build time; \`prop\` is only used for the shapes \`leafClass\`
|
|
208
|
+
// declines, which have to run the real thing.
|
|
209
|
+
export const cssLeaf = (prefix, prop, value) => {
|
|
210
|
+
const className = leafClass(prefix, value)
|
|
211
|
+
return className === undefined ? css({ [prop]: value }) : className
|
|
212
|
+
}
|
|
196
213
|
|
|
197
214
|
export const { mergeCss, assignCss } = createMergeCss(context)
|
|
198
215
|
`
|
|
@@ -227,7 +244,7 @@ function generateStringLiteralCssFn(ctx) {
|
|
|
227
244
|
export declare const css: CssFunction;
|
|
228
245
|
`,
|
|
229
246
|
js: outdent`
|
|
230
|
-
${ctx.file.import("astish, createCss, isObject, mergeProps, withoutSpace", "../helpers")}
|
|
247
|
+
${ctx.file.import("astish, cloneStyles, createCss, isObject, mergeProps, withoutSpace", "../helpers")}
|
|
231
248
|
${ctx.file.import("finalizeConditions, sortConditions", "./conditions")}
|
|
232
249
|
|
|
233
250
|
function transform(prop, value) {
|
|
@@ -257,7 +274,9 @@ function generateStringLiteralCssFn(ctx) {
|
|
|
257
274
|
|
|
258
275
|
const fn = (style) => (isObject(style) ? style : astish(style[0]))
|
|
259
276
|
export const css = (...styles) => cssFn(mergeProps(...styles.filter(Boolean).map(fn)))
|
|
260
|
-
css.raw
|
|
277
|
+
// Same independence guarantee as the object-syntax css.raw(), so the public
|
|
278
|
+
// API behaves identically across both syntaxes.
|
|
279
|
+
css.raw = (...styles) => cloneStyles(mergeProps(...styles.filter(Boolean).map(fn)))
|
|
261
280
|
`
|
|
262
281
|
};
|
|
263
282
|
}
|
|
@@ -266,7 +285,7 @@ function generateStringLiteralCssFn(ctx) {
|
|
|
266
285
|
function generateCvaFn(ctx) {
|
|
267
286
|
return {
|
|
268
287
|
js: outdent`
|
|
269
|
-
${ctx.file.import("compact, mergeProps, memo, splitProps, uniq", "../helpers")}
|
|
288
|
+
${ctx.file.import("cloneStyles, compact, mergeProps, memo, splitProps, uniq", "../helpers")}
|
|
270
289
|
${ctx.file.import("css, mergeCss", "./css")}
|
|
271
290
|
|
|
272
291
|
const defaults = (conf) => ({
|
|
@@ -306,6 +325,15 @@ function generateCvaFn(ctx) {
|
|
|
306
325
|
})
|
|
307
326
|
}
|
|
308
327
|
|
|
328
|
+
// \`raw\` runs per element per render — the JSX factory calls it to build the styles it
|
|
329
|
+
// merges with style props — and \`resolve\` is not cheap: a \`mergeCss\` per active variant
|
|
330
|
+
// plus a scan of every compound variant. Memoizing it keys that work on the variant
|
|
331
|
+
// props rather than repeating it for every element that shares them.
|
|
332
|
+
//
|
|
333
|
+
// \`raw\` still clones what it returns. The memoized object is shared, so handing it to a
|
|
334
|
+
// caller that mutated it would poison every later call.
|
|
335
|
+
const resolveVariants = memo(resolve)
|
|
336
|
+
|
|
309
337
|
function cvaFn(props) {
|
|
310
338
|
return css(resolve(props))
|
|
311
339
|
}
|
|
@@ -322,7 +350,7 @@ function generateCvaFn(ctx) {
|
|
|
322
350
|
__cva__: true,
|
|
323
351
|
variantMap,
|
|
324
352
|
variantKeys,
|
|
325
|
-
raw:
|
|
353
|
+
raw: (...args) => cloneStyles(resolveVariants(...args)),
|
|
326
354
|
config,
|
|
327
355
|
merge,
|
|
328
356
|
splitVariantProps,
|
|
@@ -398,7 +426,7 @@ function generateCx() {
|
|
|
398
426
|
var content$11 = "//#region src/astish.ts\nconst newRule = /(?:([\\u0080-\\uFFFF\\w-%@]+) *:? *([^{;]+?);|([^;}{]*?) *{)|(}\\s*)/g;\nconst ruleClean = /\\/\\*[^]*?\\*\\/| +/g;\nconst ruleNewline = /\\n+/g;\nconst empty = \" \";\nconst astish = (val, tree = [{}]) => {\n if (!val) return tree[0];\n let block, left;\n while (block = newRule.exec(val.replace(ruleClean, \"\"))) if (block[4]) tree.shift();\n else if (block[3]) {\n left = block[3].replace(ruleNewline, empty).trim();\n if (!left.includes(\"&\") && !left.startsWith(\"@\")) left = \"& \" + left;\n tree.unshift(tree[0][left] = tree[0][left] || {});\n } else tree[0][block[1]] = block[2].replace(ruleNewline, empty).trim();\n return tree[0];\n};\n//#endregion\nexport { astish };\n";
|
|
399
427
|
//#endregion
|
|
400
428
|
//#region src/artifacts/generated/helpers.mjs.json
|
|
401
|
-
var content$10 = "//#region src/assert.ts\nfunction isObject(value) {\n return typeof value === \"object\" && value != null && !Array.isArray(value);\n}\nconst isObjectOrArray = (obj) => typeof obj === \"object\" && obj !== null;\n//#endregion\n//#region src/compact.ts\nfunction compact(value) {\n return Object.fromEntries(Object.entries(value ?? {}).filter(([_, value]) => value !== void 0));\n}\n//#endregion\n//#region src/condition.ts\nconst isBaseCondition = (v) => v === \"base\";\nfunction filterBaseConditions(c) {\n return c.slice().filter((v) => !isBaseCondition(v));\n}\n//#endregion\n//#region src/hash.ts\nfunction toChar(code) {\n return String.fromCharCode(code + (code > 25 ? 39 : 97));\n}\nfunction toName(code) {\n let name = \"\";\n let x;\n for (x = Math.abs(code); x > 52; x = x / 52 | 0) name = toChar(x % 52) + name;\n return toChar(x % 52) + name;\n}\nfunction toPhash(h, x) {\n let i = x.length;\n while (i) h = h * 33 ^ x.charCodeAt(--i);\n return h;\n}\nfunction toHash(value) {\n return toName(toPhash(5381, value) >>> 0);\n}\n//#endregion\n//#region src/important.ts\nconst importantRegex = /\\s*!(important)?/i;\nfunction isImportant(value) {\n return typeof value === \"string\" ? importantRegex.test(value) : false;\n}\nfunction withoutImportant(value) {\n return typeof value === \"string\" ? value.replace(importantRegex, \"\").trim() : value;\n}\nfunction withoutSpace(str) {\n return typeof str === \"string\" ? str.replaceAll(\" \", \"_\") : str;\n}\n//#endregion\n//#region src/memo.ts\nconst memo = (fn) => {\n const cache = /* @__PURE__ */ new Map();\n const get = (...args) => {\n const key = JSON.stringify(args);\n if (cache.has(key)) return cache.get(key);\n const result = fn(...args);\n cache.set(key, result);\n return result;\n };\n return get;\n};\n//#endregion\n//#region src/merge-props.ts\nconst MERGE_OMIT = new Set([\n \"__proto__\",\n \"constructor\",\n \"prototype\"\n]);\nfunction mergeProps(...sources) {\n return sources.reduce((prev, obj) => {\n if (!obj) return prev;\n Object.keys(obj).forEach((key) => {\n if (MERGE_OMIT.has(key)) return;\n const prevValue = prev[key];\n const value = obj[key];\n if (isObject(prevValue) && isObject(value)) prev[key] = mergeProps(prevValue, value);\n else prev[key] = value;\n });\n return prev;\n }, {});\n}\n//#endregion\n//#region src/walk-object.ts\nconst isNotNullish = (element) => element != null;\nfunction walkObject(target, predicate, options = {}) {\n const { stop, getKey } = options;\n function inner(value, path = []) {\n if (isObjectOrArray(value)) {\n const result = {};\n for (const [prop, child] of Object.entries(value)) {\n const key = getKey?.(prop, child) ?? prop;\n const childPath = [...path, key];\n if (stop?.(value, childPath)) return predicate(value, path);\n const next = inner(child, childPath);\n if (isNotNullish(next)) result[key] = next;\n }\n return result;\n }\n return predicate(value, path);\n }\n return inner(target);\n}\nfunction mapObject(obj, fn) {\n if (Array.isArray(obj)) return obj.map((value) => fn(value));\n if (!isObject(obj)) return fn(obj);\n return walkObject(obj, (value) => fn(value));\n}\n//#endregion\n//#region src/normalize-style-object.ts\nfunction toResponsiveObject(values, breakpoints) {\n return values.reduce((acc, current, index) => {\n const key = breakpoints[index];\n if (current != null) acc[key] = current;\n return acc;\n }, {});\n}\nfunction normalizeStyleObject(styles, context, shorthand = true) {\n const { utility, conditions } = context;\n const { hasShorthand, resolveShorthand } = utility;\n return walkObject(styles, (value) => {\n return Array.isArray(value) ? toResponsiveObject(value, conditions.breakpoints.keys) : value;\n }, {\n stop: (value) => Array.isArray(value),\n getKey: shorthand ? (prop) => hasShorthand ? resolveShorthand(prop) : prop : void 0\n });\n}\n//#endregion\n//#region src/classname.ts\nconst fallbackCondition = {\n shift: (v) => v,\n finalize: (v) => v,\n breakpoints: { keys: [] }\n};\nconst sanitize = (value) => typeof value === \"string\" ? value.replaceAll(/[\\n\\s]+/g, \" \") : value;\nconst ENTRY_SEP = \"]___[\";\nconst COND_SEP = \"<___>\";\nfunction createCss(context) {\n const { utility, hash, grouped, conditions: conds = fallbackCondition } = context;\n const formatClassName = (str) => [utility.prefix, str].filter(Boolean).join(\"-\");\n const hashFn = (conditions, className) => {\n let result;\n if (hash) {\n const baseArray = [...conds.finalize(conditions), className];\n result = formatClassName(utility.toHash(baseArray, toHash));\n } else result = [...conds.finalize(conditions), formatClassName(className)].join(\":\");\n return result;\n };\n if (grouped) return memo(({ base, ...styles } = {}) => {\n const normalizedObject = normalizeStyleObject(Object.assign(styles, base), context);\n const hashes = [];\n walkObject(normalizedObject, (value, paths) => {\n if (value == null) return;\n const [prop, ...allConditions] = conds.shift(paths);\n const conditions = filterBaseConditions(allConditions);\n const parts = [`${prop}${ENTRY_SEP}value:${value}`];\n if (conditions.length) parts.push(`cond:${conditions.join(COND_SEP)}`);\n hashes.push(parts.join(ENTRY_SEP));\n });\n if (hashes.length === 0) return \"\";\n hashes.sort();\n const groupId = hashes.join(\"|\");\n return formatClassName(utility.toHash([\"grouped\", groupId], toHash));\n });\n return memo(({ base, ...styles } = {}) => {\n const normalizedObject = normalizeStyleObject(Object.assign(styles, base), context);\n const classNames = /* @__PURE__ */ new Set();\n walkObject(normalizedObject, (value, paths) => {\n if (value == null) return;\n const important = isImportant(value);\n const [prop, ...allConditions] = conds.shift(paths);\n let className = hashFn(filterBaseConditions(allConditions), utility.transform(prop, withoutImportant(sanitize(value))).className);\n if (important) className = `${className}!`;\n classNames.add(className);\n });\n return Array.from(classNames).join(\" \");\n });\n}\nfunction compactStyles(...styles) {\n return styles.flat().filter((style) => isObject(style) && Object.keys(compact(style)).length > 0);\n}\nfunction createMergeCss(context) {\n function resolve(styles) {\n const allStyles = compactStyles(...styles);\n if (allStyles.length === 1) return allStyles;\n return allStyles.map((style) => normalizeStyleObject(style, context));\n }\n function mergeCss(...styles) {\n return mergeProps(...resolve(styles));\n }\n function assignCss(...styles) {\n return Object.assign({}, ...resolve(styles));\n }\n return {\n mergeCss: memo(mergeCss),\n assignCss\n };\n}\n//#endregion\n//#region src/hypenate-property.ts\nconst wordRegex = /([A-Z])/g;\nconst msRegex = /^ms-/;\nconst hypenateProperty = memo((property) => {\n if (property.startsWith(\"--\")) return property;\n return property.replace(wordRegex, \"-$1\").replace(msRegex, \"-ms-\").toLowerCase();\n});\n//#endregion\n//#region src/is-css-function.ts\nconst fnRegExp = new RegExp(`^(${[\n \"min\",\n \"max\",\n \"clamp\",\n \"calc\"\n].join(\"|\")})\\\\(.*\\\\)`);\nconst isCssFunction = (v) => typeof v === \"string\" && fnRegExp.test(v);\n//#endregion\n//#region src/is-css-unit.ts\nconst lengthUnitsPattern = `(?:${\"cm,mm,Q,in,pc,pt,px,em,ex,ch,rem,lh,rlh,vw,vh,vmin,vmax,vb,vi,svw,svh,lvw,lvh,dvw,dvh,cqw,cqh,cqi,cqb,cqmin,cqmax,%\".split(\",\").join(\"|\")})`;\nconst lengthRegExp = new RegExp(`^[+-]?[0-9]*.?[0-9]+(?:[eE][+-]?[0-9]+)?${lengthUnitsPattern}$`);\nconst isCssUnit = (v) => typeof v === \"string\" && lengthRegExp.test(v);\n//#endregion\n//#region src/is-css-var.ts\nconst isCssVar = (v) => typeof v === \"string\" && /^var\\(--.+\\)$/.test(v);\n//#endregion\n//#region src/pattern-fns.ts\nconst patternFns = {\n map: mapObject,\n isCssFunction,\n isCssVar,\n isCssUnit\n};\nconst getPatternStyles = (pattern, styles) => {\n if (!pattern?.defaultValues) return styles;\n const defaults = typeof pattern.defaultValues === \"function\" ? pattern.defaultValues(styles) : pattern.defaultValues;\n return Object.assign({}, defaults, compact(styles));\n};\n//#endregion\n//#region src/slot.ts\nconst getSlotRecipes = (recipe = {}) => {\n const init = (slot) => ({\n className: [recipe.className, slot].filter(Boolean).join(\"__\"),\n base: recipe.base?.[slot] ?? {},\n variants: {},\n defaultVariants: recipe.defaultVariants ?? {},\n compoundVariants: recipe.compoundVariants ? getSlotCompoundVariant(recipe.compoundVariants, slot) : []\n });\n const recipeParts = (recipe.slots ?? []).map((slot) => [slot, init(slot)]);\n for (const [variantsKey, variantsSpec] of Object.entries(recipe.variants ?? {})) for (const [variantKey, variantSpec] of Object.entries(variantsSpec)) recipeParts.forEach(([slot, slotRecipe]) => {\n slotRecipe.variants[variantsKey] ??= {};\n slotRecipe.variants[variantsKey][variantKey] = variantSpec[slot] ?? {};\n });\n return Object.fromEntries(recipeParts);\n};\nconst getSlotCompoundVariant = (compoundVariants, slotName) => compoundVariants.filter((compoundVariant) => compoundVariant.css[slotName]).map((compoundVariant) => ({\n ...compoundVariant,\n css: compoundVariant.css[slotName]\n}));\n//#endregion\n//#region src/split-props.ts\nfunction splitProps(props, ...keys) {\n const descriptors = Object.getOwnPropertyDescriptors(props);\n const dKeys = Object.keys(descriptors);\n const split = (k) => {\n const clone = {};\n for (let i = 0; i < k.length; i++) {\n const key = k[i];\n if (descriptors[key]) {\n Object.defineProperty(clone, key, descriptors[key]);\n delete descriptors[key];\n }\n }\n return clone;\n };\n const fn = (key) => split(Array.isArray(key) ? key : dKeys.filter(key));\n return keys.map(fn).concat(split(dKeys));\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 { compact, createCss, createMergeCss, filterBaseConditions, getPatternStyles, getSlotCompoundVariant, getSlotRecipes, hypenateProperty, isBaseCondition, isObject, mapObject, memo, mergeProps, patternFns, splitProps, toHash, uniq, walkObject, withoutSpace };\n";
|
|
429
|
+
var content$10 = "//#region src/assert.ts\nfunction isObject(value) {\n return typeof value === \"object\" && value != null && !Array.isArray(value);\n}\nconst isObjectOrArray = (obj) => typeof obj === \"object\" && obj !== null;\n//#endregion\n//#region src/condition.ts\nconst isBaseCondition = (v) => v === \"base\";\nfunction filterBaseConditions(c) {\n return c.slice().filter((v) => !isBaseCondition(v));\n}\n//#endregion\n//#region src/hash.ts\nfunction toChar(code) {\n return String.fromCharCode(code + (code > 25 ? 39 : 97));\n}\nfunction toName(code) {\n let name = \"\";\n let x;\n for (x = Math.abs(code); x > 52; x = x / 52 | 0) name = toChar(x % 52) + name;\n return toChar(x % 52) + name;\n}\nfunction toPhash(h, x) {\n let i = x.length;\n while (i) h = h * 33 ^ x.charCodeAt(--i);\n return h;\n}\nfunction toHash(value) {\n return toName(toPhash(5381, value) >>> 0);\n}\n//#endregion\n//#region src/important.ts\nconst importantRegex = /\\s*!(important)?/i;\nconst whitespaceRegex = /\\s/;\n/**\n* Collapse every run of whitespace to a single space, which is what the class name is\n* built from. Exported because `leafClass` has to reproduce this exact pipeline, and a\n* second copy of it would be free to drift from the one `createCss` runs.\n*/\nfunction sanitize(value) {\n if (typeof value !== \"string\") return value;\n return whitespaceRegex.test(value) ? value.replaceAll(/[\\n\\s]+/g, \" \") : value;\n}\nfunction isImportant(value) {\n if (typeof value !== \"string\") return false;\n return value.includes(\"!\") && importantRegex.test(value);\n}\nfunction withoutImportant(value) {\n if (typeof value !== \"string\") return value;\n if (!value.includes(\"!\")) return value.trim();\n return value.replace(importantRegex, \"\").trim();\n}\nfunction withoutSpace(str) {\n if (typeof str !== \"string\") return str;\n return str.includes(\" \") ? str.replaceAll(\" \", \"_\") : str;\n}\n//#endregion\n//#region src/memo.ts\n/**\n* Bounded argument memo used by the generated runtime (`css`, patterns, `cva`, recipes).\n*\n* Two regimes, picked per call:\n*\n* - Arguments that are flat (objects of primitives) take a cheap structural hash\n* and are confirmed with an exact comparison, so a hash collision can never\n* serve the wrong result. This is the shape `css({ ... })` has.\n* - Anything nested falls back to `JSON.stringify`, which V8 does faster than a\n* JS walk.\n*\n* The second point is the counter-intuitive one, and it has been measured rather\n* than assumed. Extending the structural hash to recurse — so nested styles could\n* take the fast path too — is *slower*, because it trades one native serialization\n* for two JS walks (hash, then the deep equality that confirms it). Over 10k\n* iterations per shape:\n*\n* shape stringify recursive hash + deep equal\n* flat 1.06ms 2.09ms\n* _hover 1.00ms 2.16ms\n* responsive 1.23ms 2.15ms\n* realistic 2.32ms 5.84ms\n* nested 3 deep 1.22ms 2.35ms\n*\n* So a nested `css()` call costing several times a flat one is not a defect here.\n* It is the floor for a value-keyed memo in JS, and the way to avoid it is to not\n* make the call — see the build-time fold in `@bamboocss/vite`.\n*\n* Both regimes key on *values*, never on object identity: mutating a style object\n* between calls changes its hash, so the next call misses and recomputes rather\n* than serving a stale class. Keying nested arguments on the identity of the inner\n* objects would skip serialization entirely, but it cannot detect a mutation, and\n* \"same object, different contents\" is exactly what a style object built per render\n* looks like.\n*\n* Both caches are bounded. An unbounded memo is a leak in any long-lived process\n* (SSR), where the set of distinct style objects grows without limit.\n*/\n/**\n* Distinct hashes held per memoized function before the cache rotates.\n*\n* This bounds *buckets*, not entries: a bucket keeps up to `MAX_BUCKET` colliding\n* argument lists, so the ceiling is `MAX_ENTRIES * MAX_BUCKET` live entries, and\n* twice that across both generations, since the previous one is retained until the\n* next rotation. Collisions are rare in practice, so the realistic figure is close\n* to `MAX_ENTRIES` — but the worst case is what matters when sizing a long-lived\n* process, so state it plainly.\n*\n* Rotation beats evicting the oldest key: single-key eviction is worst-case for a\n* working set that cycles, because it drops exactly the entry about to be needed.\n* Measured on a cycling set of 20k styles, one-at-a-time eviction cost ~719ns/op\n* against ~189ns unbounded, while rotation holds ~274ns. On realistic skewed\n* access rotation is at or below the unbounded cost.\n*/\nconst MAX_ENTRIES = 1e3;\n/** Entries kept per hash bucket, to bound the cost of a collision scan. */\nconst MAX_BUCKET = 8;\n/**\n* DJB2 over the arguments' own keys and primitive values.\n* Returns `null` for anything nested, which routes the call to the string key.\n*/\nconst flatHashOrNull = (args) => {\n let h = 5381;\n for (let a = 0; a < args.length; a++) {\n const obj = args[a];\n if (obj === null || typeof obj !== \"object\") {\n const t = typeof obj;\n if (t === \"string\") for (let i = 0; i < obj.length; i++) h = h * 33 ^ obj.charCodeAt(i);\n else if (t === \"number\") h = h * 33 ^ (obj | 0);\n else if (t === \"boolean\") h = h * 33 ^ (obj ? 991 : 997);\n else h = h * 33 ^ 3;\n continue;\n }\n if (Array.isArray(obj)) h = h * 33 ^ 7;\n else {\n const proto = Object.getPrototypeOf(obj);\n if (proto !== Object.prototype && proto !== null) return null;\n }\n for (const k in obj) {\n const v = obj[k];\n const tv = typeof v;\n if (v !== null && tv === \"object\") return null;\n for (let i = 0; i < k.length; i++) h = h * 33 ^ k.charCodeAt(i);\n if (tv === \"string\") for (let i = 0; i < v.length; i++) h = h * 33 ^ v.charCodeAt(i);\n else if (tv === \"number\") h = h * 33 ^ (v | 0);\n else if (tv === \"boolean\") h = h * 33 ^ (v ? 991 : 997);\n else h = h * 33 ^ 2;\n }\n }\n return h >>> 0;\n};\n/**\n* Value snapshot of the arguments, taken once at insert.\n*\n* The cache must not hold the caller's objects: a style object can capture a much\n* larger graph, and keeping it alive until the cache rotates changes GC behaviour\n* for code that never asked to be cached. Only the flat path reaches here, so a\n* shallow copy contains primitives only and retains nothing.\n*\n* Comparing against a copy also removes the last way a mutation could be missed.\n* Were the caller's own object stored, `oa === ob` would short-circuit the value\n* comparison, and a mutation that happened to preserve the hash would return the\n* stale entry. Against a copy that check can only ever be true for equal\n* primitives.\n*/\nconst snapshotArgs = (args) => {\n const values = [];\n const counts = [];\n for (let i = 0; i < args.length; i++) {\n const o = args[i];\n if (o !== null && typeof o === \"object\") {\n const copy = Array.isArray(o) ? [] : {};\n let n = 0;\n for (const k in o) {\n copy[k] = o[k];\n n++;\n }\n values.push(copy);\n counts.push(n);\n } else {\n values.push(o);\n counts.push(0);\n }\n }\n return {\n values,\n counts\n };\n};\n/**\n* Exact match, so a `flatHashOrNull` collision is resolved rather than trusted.\n* `bCounts` is the cached side's key count; comparing against it avoids the\n* `Object.keys()` allocation this would otherwise make on every cache hit.\n*/\nconst flatArgsEqual = (a, b, bCounts) => {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n const oa = a[i];\n const ob = b[i];\n if (oa === ob) continue;\n if (oa === null || ob === null || typeof oa !== \"object\" || typeof ob !== \"object\") return false;\n if (Array.isArray(oa) !== Array.isArray(ob)) return false;\n let n = 0;\n for (const k in oa) {\n if (oa[k] !== ob[k]) return false;\n n++;\n }\n if (n !== bCounts[i]) return false;\n }\n return true;\n};\nconst memo = (fn) => {\n let buckets = /* @__PURE__ */ new Map();\n let priorBuckets = /* @__PURE__ */ new Map();\n let strings = /* @__PURE__ */ new Map();\n let priorStrings = /* @__PURE__ */ new Map();\n /**\n * One scalar argument, keyed directly.\n *\n * This is the shape of the hottest callers — `isCssProperty(prop)` runs per prop\n * per render — and a plain map lookup beats hashing, bucket scanning and\n * snapshotting for it. Distinct types stay distinct keys, so `1` and `'1'` do not\n * share an entry.\n */\n let scalars = /* @__PURE__ */ new Map();\n let priorScalars = /* @__PURE__ */ new Map();\n const scan = (bucket, args) => {\n if (bucket) for (let i = 0; i < bucket.length; i++) {\n const entry = bucket[i];\n if (flatArgsEqual(args, entry.values, entry.counts)) return entry;\n }\n };\n const get = (...args) => {\n if (args.length === 1) {\n const only = args[0];\n if (only === null || typeof only !== \"object\") {\n if (scalars.has(only)) return scalars.get(only);\n if (priorScalars.has(only)) {\n const promoted = priorScalars.get(only);\n scalars.set(only, promoted);\n return promoted;\n }\n const out = fn(only);\n scalars.set(only, out);\n if (scalars.size > MAX_ENTRIES) {\n priorScalars = scalars;\n scalars = /* @__PURE__ */ new Map();\n }\n return out;\n }\n }\n const hash = flatHashOrNull(args);\n if (hash !== null) {\n let bucket = buckets.get(hash);\n const hit = scan(bucket, args);\n if (hit) return hit.out;\n const priorHit = scan(priorBuckets.get(hash), args);\n if (priorHit) {\n if (!bucket) {\n bucket = [];\n buckets.set(hash, bucket);\n }\n bucket.push(priorHit);\n if (bucket.length > MAX_BUCKET) bucket.shift();\n return priorHit.out;\n }\n const snap = snapshotArgs(args);\n const out = fn(...args);\n if (!bucket) {\n bucket = [];\n buckets.set(hash, bucket);\n }\n bucket.push({\n values: snap.values,\n counts: snap.counts,\n out\n });\n if (bucket.length > MAX_BUCKET) bucket.shift();\n if (buckets.size > MAX_ENTRIES) {\n priorBuckets = buckets;\n buckets = /* @__PURE__ */ new Map();\n }\n return out;\n }\n const key = JSON.stringify(args);\n if (strings.has(key)) return strings.get(key);\n if (priorStrings.has(key)) {\n const promoted = priorStrings.get(key);\n strings.set(key, promoted);\n return promoted;\n }\n const out = fn(...args);\n strings.set(key, out);\n if (strings.size > MAX_ENTRIES) {\n priorStrings = strings;\n strings = /* @__PURE__ */ new Map();\n }\n return out;\n };\n return get;\n};\n//#endregion\n//#region src/merge-props.ts\nconst MERGE_OMIT = new Set([\n \"__proto__\",\n \"constructor\",\n \"prototype\"\n]);\nfunction mergeProps(...sources) {\n return sources.reduce((prev, obj) => {\n if (!obj) return prev;\n Object.keys(obj).forEach((key) => {\n if (MERGE_OMIT.has(key)) return;\n const prevValue = prev[key];\n const value = obj[key];\n if (isObject(prevValue) && isObject(value)) prev[key] = mergeProps(prevValue, value);\n else prev[key] = value;\n });\n return prev;\n }, {});\n}\n//#endregion\n//#region src/walk-object.ts\nconst isNotNullish = (element) => element != null;\nfunction walkObject(target, predicate, options = {}) {\n const { stop, getKey } = options;\n function inner(value, path = []) {\n if (isObjectOrArray(value)) {\n const result = {};\n for (const [prop, child] of Object.entries(value)) {\n const key = getKey?.(prop, child) ?? prop;\n const childPath = [...path, key];\n if (stop?.(value, childPath)) return predicate(value, path);\n const next = inner(child, childPath);\n if (isNotNullish(next)) result[key] = next;\n }\n return result;\n }\n return predicate(value, path);\n }\n return inner(target);\n}\nfunction mapObject(obj, fn) {\n if (Array.isArray(obj)) return obj.map((value) => fn(value));\n if (!isObject(obj)) return fn(obj);\n return walkObject(obj, (value) => fn(value));\n}\n//#endregion\n//#region src/normalize-style-object.ts\nfunction toResponsiveObject(values, breakpoints) {\n return values.reduce((acc, current, index) => {\n const key = breakpoints[index];\n if (current != null) acc[key] = current;\n return acc;\n }, {});\n}\n/**\n* Whether walking the object would only rebuild it.\n*\n* Normalizing does three things: it renames a shorthand to its longhand, expands a responsive\n* array into a breakpoint object, and drops nullish leaves. A flat object of plain values\n* written in longhand needs none of them, and that is most of what `css()` is handed — but it\n* still paid for a full rebuild plus a path array per key.\n*\n* Every clause has to be exact, since a false positive returns an object the walk would have\n* changed. Nullish is one of them: a leaf the walk removes must not survive, or a later merge\n* would see it override the value beneath it. The array check is another, and it is on the\n* container as well as the values — `stop` is handed the container, so an array arriving at\n* the top level becomes a breakpoint object rather than being walked into.\n*\n* `for...in` reads inherited keys the walk ignores, which is safe in the only direction it can\n* be wrong — an extra key can send this to the slow path, never past it.\n*\n* It does read every value, as `compactStyles` and the argument memo already do, so an\n* accessor prop is read once more than before. Style props are values by the time they get\n* here and reading one has no effect, but it is the reason this cannot be reordered to read\n* lazily.\n*/\nfunction needsNoNormalizing(styles, resolveShorthand) {\n if (Array.isArray(styles)) return false;\n for (const key in styles) {\n const value = styles[key];\n if (value == null || typeof value === \"object\") return false;\n if (resolveShorthand !== void 0 && resolveShorthand(key) !== key) return false;\n }\n return true;\n}\n/**\n* The result may be the argument itself rather than a fresh object, so callers have to treat\n* it as read-only. Every one of them does today: merging accumulates into its own object and\n* the two `raw()` helpers clone at the boundary.\n*/\nfunction normalizeStyleObject(styles, context, shorthand = true) {\n const { utility, conditions } = context;\n const { hasShorthand, resolveShorthand } = utility;\n if (needsNoNormalizing(styles, shorthand && hasShorthand ? resolveShorthand : void 0)) return styles;\n return walkObject(styles, (value) => {\n return Array.isArray(value) ? toResponsiveObject(value, conditions.breakpoints.keys) : value;\n }, {\n stop: (value) => Array.isArray(value),\n getKey: shorthand ? (prop) => hasShorthand ? resolveShorthand(prop) : prop : void 0\n });\n}\n//#endregion\n//#region src/classname.ts\nconst fallbackCondition = {\n shift: (v) => v,\n finalize: (v) => v,\n breakpoints: { keys: [] }\n};\nconst ENTRY_SEP = \"]___[\";\nconst COND_SEP = \"<___>\";\nfunction createCss(context) {\n const { utility, hash, grouped, conditions: conds = fallbackCondition } = context;\n const { prefix } = utility;\n const formatClassName = prefix ? (str) => str ? `${prefix}-${str}` : prefix : (str) => str || \"\";\n const hashFn = (conditions, className) => {\n if (hash) {\n const baseArray = [...conds.finalize(conditions), className];\n return formatClassName(utility.toHash(baseArray, toHash));\n }\n const finalized = conds.finalize(conditions);\n if (finalized.length === 0) return formatClassName(className);\n return [...finalized, formatClassName(className)].join(\":\");\n };\n if (grouped) return memo(({ base, ...styles } = {}) => {\n const normalizedObject = normalizeStyleObject(Object.assign(styles, base), context);\n const hashes = [];\n walkObject(normalizedObject, (value, paths) => {\n if (value == null) return;\n const [prop, ...allConditions] = conds.shift(paths);\n const conditions = filterBaseConditions(allConditions);\n const parts = [`${prop}${ENTRY_SEP}value:${value}`];\n if (conditions.length) parts.push(`cond:${conditions.join(COND_SEP)}`);\n hashes.push(parts.join(ENTRY_SEP));\n });\n if (hashes.length === 0) return \"\";\n hashes.sort();\n const groupId = hashes.join(\"|\");\n return formatClassName(utility.toHash([\"grouped\", groupId], toHash));\n });\n return memo(({ base, ...styles } = {}) => {\n const normalizedObject = normalizeStyleObject(Object.assign(styles, base), context);\n const classNames = /* @__PURE__ */ new Set();\n walkObject(normalizedObject, (value, paths) => {\n if (value == null) return;\n const important = isImportant(value);\n const [prop, ...allConditions] = conds.shift(paths);\n let className = hashFn(filterBaseConditions(allConditions), utility.transform(prop, withoutImportant(sanitize(value))).className);\n if (important) className = `${className}!`;\n classNames.add(className);\n });\n return Array.from(classNames).join(\" \");\n });\n}\n/**\n* Whether a style object carries anything `compact` would have kept.\n*\n* The question `compactStyles` asks is only ever \"is this empty once undefined values are\n* dropped\", but it used to answer it by building the compacted object and then a key array\n* for it, then throwing both away. `Object.keys` enumerates exactly what `compact`'s\n* `Object.entries` did — own, enumerable, string-keyed — so this is the same predicate\n* without the two allocations, and it stops at the first value that settles it.\n*/\nfunction hasDefinedValue(style) {\n const keys = Object.keys(style);\n for (let i = 0; i < keys.length; i++) if (style[keys[i]] !== void 0) return true;\n return false;\n}\nfunction compactStyles(...styles) {\n return styles.flat().filter((style) => isObject(style) && hasDefinedValue(style));\n}\nfunction createMergeCss(context) {\n function resolve(styles) {\n const allStyles = compactStyles(...styles);\n if (allStyles.length === 1) return allStyles;\n return allStyles.map((style) => normalizeStyleObject(style, context));\n }\n function mergeCss(...styles) {\n return mergeProps(...resolve(styles));\n }\n function assignCss(...styles) {\n return Object.assign({}, ...resolve(styles));\n }\n return {\n mergeCss: memo(mergeCss),\n assignCss\n };\n}\n//#endregion\n//#region src/clone-styles.ts\nconst OMIT = new Set([\n \"__proto__\",\n \"constructor\",\n \"prototype\"\n]);\n/**\n* Independent copy of a style object, nested condition blocks included.\n*\n* Merged style objects are cached, so anything handed to user code has to be\n* copied first: a caller mutating what it received would otherwise change what\n* every later caller reads back. `css.raw()` and `cva.raw()` are those boundaries.\n*\n* Kept separate from `mergeProps` deliberately. Merging is on the hot path — it\n* runs on every `css()` cache miss and on every render of a pattern component\n* under `jsxStyleProps: 'minimal'` — and copying there charges every caller for a\n* guarantee only the two `raw()` helpers need. Measured on a realistic style\n* object (5 base properties, 4 condition blocks) that was roughly twice the cost\n* of merging alone.\n*/\nfunction cloneStyles(styles) {\n if (Array.isArray(styles)) return styles.map((value) => cloneStyles(value));\n if (!isObject(styles)) return styles;\n const out = {};\n for (const key of Object.keys(styles)) {\n if (OMIT.has(key)) continue;\n out[key] = cloneStyles(styles[key]);\n }\n return out;\n}\n//#endregion\n//#region src/compact.ts\nfunction compact(value) {\n return Object.fromEntries(Object.entries(value ?? {}).filter(([_, value]) => value !== void 0));\n}\n//#endregion\n//#region src/leaf-class.ts\n/**\n* The class a single dynamic style leaf resolves to, given the prefix its property and\n* condition path produce.\n*\n* ## Why this can exist at all\n*\n* `css()` builds a class from the value alone — `utility.transform` is string\n* construction over a static map, and nothing consults which rules were actually emitted.\n* So `css({ color: tone })` already returns `c_<tone>` for a value the extractor never\n* saw, with no CSS behind it. Reproducing that string here cannot be less correct than\n* the call it replaces; it just skips the object literal, the merge and the memo.\n*\n* ## Why it is not a template literal\n*\n* Three shapes do not reduce to `prefix + value`, and all three return `undefined` so the\n* caller runs `css()` instead:\n*\n* - An array is expanded to a responsive object by `normalizeStyleObject`, so it produces\n* one class per breakpoint rather than one class.\n* - An object is a condition block, walked into for the same reason.\n* - `null` and `undefined` are skipped by the walk entirely, which is an empty string\n* rather than a class — that one is answered here, since it needs no `css()` call.\n*\n* ## Why the character scan\n*\n* The remaining work — collapsing whitespace, stripping `!important`, turning spaces into\n* underscores — is three regexes, and paying them per call makes this *slower* than a\n* memo hit. Almost no token value contains whitespace or `!`, so one scan for the\n* characters that make any of it necessary sends the common value straight to a\n* concatenation. A false positive only costs the slow path, so the scan errs wide.\n*/\nfunction leafClass(prefix, value) {\n if (value == null) return \"\";\n const type = typeof value;\n if (type === \"number\" || type === \"boolean\") return `${prefix}${value}`;\n if (type !== \"string\") return void 0;\n const str = value;\n for (let index = 0; index < str.length; index++) {\n const code = str.charCodeAt(index);\n if (code <= 33 || code === 160 || code === 5760 || code >= 8192) return slowLeaf(prefix, str);\n }\n return `${prefix}${str}`;\n}\n/** The full pipeline `createCss` runs, for a value that needs it. */\nfunction slowLeaf(prefix, value) {\n const important = isImportant(value);\n const className = `${prefix}${withoutSpace(withoutImportant(sanitize(value)))}`;\n return important ? `${className}!` : className;\n}\n//#endregion\n//#region src/hypenate-property.ts\nconst wordRegex = /([A-Z])/g;\nconst msRegex = /^ms-/;\nconst hypenateProperty = memo((property) => {\n if (property.startsWith(\"--\")) return property;\n return property.replace(wordRegex, \"-$1\").replace(msRegex, \"-ms-\").toLowerCase();\n});\n//#endregion\n//#region src/is-css-function.ts\nconst fnRegExp = new RegExp(`^(${[\n \"min\",\n \"max\",\n \"clamp\",\n \"calc\"\n].join(\"|\")})\\\\(.*\\\\)`);\nconst isCssFunction = (v) => typeof v === \"string\" && fnRegExp.test(v);\n//#endregion\n//#region src/is-css-unit.ts\nconst lengthUnitsPattern = `(?:${\"cm,mm,Q,in,pc,pt,px,em,ex,ch,rem,lh,rlh,vw,vh,vmin,vmax,vb,vi,svw,svh,lvw,lvh,dvw,dvh,cqw,cqh,cqi,cqb,cqmin,cqmax,%\".split(\",\").join(\"|\")})`;\nconst lengthRegExp = new RegExp(`^[+-]?[0-9]*.?[0-9]+(?:[eE][+-]?[0-9]+)?${lengthUnitsPattern}$`);\nconst isCssUnit = (v) => typeof v === \"string\" && lengthRegExp.test(v);\n//#endregion\n//#region src/is-css-var.ts\nconst isCssVar = (v) => typeof v === \"string\" && /^var\\(--.+\\)$/.test(v);\n//#endregion\n//#region src/pattern-fns.ts\nconst patternFns = {\n map: mapObject,\n isCssFunction,\n isCssVar,\n isCssUnit\n};\nconst getPatternStyles = (pattern, styles) => {\n if (!pattern?.defaultValues) return styles;\n const defaults = typeof pattern.defaultValues === \"function\" ? pattern.defaultValues(styles) : pattern.defaultValues;\n return Object.assign({}, defaults, compact(styles));\n};\n//#endregion\n//#region src/slot.ts\nconst getSlotRecipes = (recipe = {}) => {\n const init = (slot) => ({\n className: [recipe.className, slot].filter(Boolean).join(\"__\"),\n base: recipe.base?.[slot] ?? {},\n variants: {},\n defaultVariants: recipe.defaultVariants ?? {},\n compoundVariants: recipe.compoundVariants ? getSlotCompoundVariant(recipe.compoundVariants, slot) : []\n });\n const recipeParts = (recipe.slots ?? []).map((slot) => [slot, init(slot)]);\n for (const [variantsKey, variantsSpec] of Object.entries(recipe.variants ?? {})) for (const [variantKey, variantSpec] of Object.entries(variantsSpec)) recipeParts.forEach(([slot, slotRecipe]) => {\n slotRecipe.variants[variantsKey] ??= {};\n slotRecipe.variants[variantsKey][variantKey] = variantSpec[slot] ?? {};\n });\n return Object.fromEntries(recipeParts);\n};\nconst getSlotCompoundVariant = (compoundVariants, slotName) => compoundVariants.filter((compoundVariant) => compoundVariant.css[slotName]).map((compoundVariant) => ({\n ...compoundVariant,\n css: compoundVariant.css[slotName]\n}));\n//#endregion\n//#region src/split-props.ts\n/**\n* Deal a props object into one bucket per key group, plus a final bucket for the rest.\n* A key goes to the first group that claims it.\n*\n* ## Why the descriptor is read per key rather than in bulk\n*\n* This used to call `Object.getOwnPropertyDescriptors` for the whole object and\n* `defineProperty` for every key it moved. Copying plain values instead is 2.4–2.9x faster\n* on the shapes that allow it, but it is only correct where props are data — and they are\n* not always. Solid compiles props to accessors, so reading one eagerly runs whatever it\n* wraps: splitting a component's props would construct its children before the surrounding\n* provider exists.\n*\n* So the descriptor is fetched per key, and the value path is taken only when it changes\n* nothing observable. An accessor keeps its laziness, a non-enumerable key keeps its\n* invisibility, and `__proto__` is defined rather than assigned so it stays an own\n* property instead of reaching the prototype setter.\n*\n* The one thing the value path drops is `writable`/`configurable`, so a bucket key taken\n* from frozen props is writable where it used to be frozen. Nothing here relies on that,\n* and preserving it would mean `defineProperty` on the common path — the cost this exists\n* to avoid. Keys that take the descriptor path keep theirs, so a bucket can be\n* inconsistent in that one respect.\n*\n* Key order within a bucket is preserved exactly. It is not cosmetic: `cva` merges\n* variant props in iteration order, and the parser reads the rest bucket as the style\n* props it encodes, so order reaches the emitted CSS.\n*/\nfunction splitProps(props, ...keys) {\n const allKeys = Object.getOwnPropertyNames(props);\n const own = new Set(allKeys);\n const taken = /* @__PURE__ */ new Set();\n const split = (group) => {\n const clone = {};\n for (let i = 0; i < group.length; i++) {\n const key = group[i];\n if (taken.has(key) || !own.has(key)) continue;\n const descriptor = Object.getOwnPropertyDescriptor(props, key);\n if (!descriptor) continue;\n if (\"get\" in descriptor || \"set\" in descriptor || !descriptor.enumerable || key === \"__proto__\") Object.defineProperty(clone, key, descriptor);\n else clone[key] = descriptor.value;\n taken.add(key);\n }\n return clone;\n };\n /**\n * The predicate is called with the key alone.\n *\n * Handing it to `filter` passes `(key, index, allKeys)`. A one-parameter predicate cannot\n * see the extras, but a memoized one reads its whole argument list — and the predicates\n * that arrive here are memoized, `isCssProperty` among them. So the memo hashed the entire\n * key array once per prop, and keyed its cache on it: two elements with different prop sets\n * shared no entry even for the same prop name.\n *\n * Worth ~9.7x on that path, and nothing at all on a plain predicate — which is why the\n * bench below it needs a memoized case to see this at all.\n *\n * A loop rather than `filter((k) => key(k))` because the wrapper allocates a closure per\n * group. The two measure the same to within noise; the loop just does not need one.\n */\n const matching = (predicate) => {\n const group = [];\n for (let i = 0; i < allKeys.length; i++) {\n const key = allKeys[i];\n if (predicate(key)) group.push(key);\n }\n return group;\n };\n return keys.map((key) => split(Array.isArray(key) ? key : matching(key))).concat(split(allKeys));\n}\n//#endregion\n//#region src/uniq.ts\nconst uniq = (...items) => {\n const set = items.reduce((acc, currItems) => {\n if (currItems) currItems.forEach((item) => acc.add(item));\n return acc;\n }, /* @__PURE__ */ new Set([]));\n return Array.from(set);\n};\n//#endregion\nexport { cloneStyles, compact, createCss, createMergeCss, filterBaseConditions, getPatternStyles, getSlotCompoundVariant, getSlotRecipes, hypenateProperty, isBaseCondition, isObject, leafClass, mapObject, memo, mergeProps, patternFns, splitProps, toHash, uniq, walkObject, withoutSpace };\n";
|
|
402
430
|
//#endregion
|
|
403
431
|
//#region src/artifacts/generated/normalize-html.mjs.json
|
|
404
432
|
var content$9 = "//#region src/normalize-html.ts\nconst htmlProps = [\n \"htmlSize\",\n \"htmlTranslate\",\n \"htmlWidth\",\n \"htmlHeight\"\n];\nfunction convert(key) {\n return htmlProps.includes(key) ? key.replace(\"html\", \"\").toLowerCase() : key;\n}\nfunction normalizeHTMLProps(props) {\n return Object.fromEntries(Object.entries(props).map(([key, value]) => [convert(key), value]));\n}\nnormalizeHTMLProps.keys = htmlProps;\n//#endregion\nexport { normalizeHTMLProps };\n";
|
|
@@ -425,17 +453,19 @@ function generateHelpers(ctx) {
|
|
|
425
453
|
var content$8 = "//#region src/index.ts\nconst userGenerated = \"\".split(\",\");\nconst allCssProperties = \"WebkitAppearance,WebkitBorderBefore,WebkitBorderBeforeColor,WebkitBorderBeforeStyle,WebkitBorderBeforeWidth,WebkitBoxReflect,WebkitLineClamp,WebkitMask,WebkitMaskAttachment,WebkitMaskClip,WebkitMaskComposite,WebkitMaskImage,WebkitMaskOrigin,WebkitMaskPosition,WebkitMaskPositionX,WebkitMaskPositionY,WebkitMaskRepeat,WebkitMaskRepeatX,WebkitMaskRepeatY,WebkitMaskSize,WebkitOverflowScrolling,WebkitTapHighlightColor,WebkitTextFillColor,WebkitTextStroke,WebkitTextStrokeColor,WebkitTextStrokeWidth,WebkitTouchCallout,WebkitUserModify,WebkitUserSelect,accentColor,alignContent,alignItems,alignSelf,alignTracks,all,anchorName,anchorScope,animation,animationComposition,animationDelay,animationDirection,animationDuration,animationFillMode,animationIterationCount,animationName,animationPlayState,animationRange,animationRangeEnd,animationRangeStart,animationTimeline,animationTimingFunction,appearance,aspectRatio,backdropFilter,backfaceVisibility,background,backgroundAttachment,backgroundBlendMode,backgroundClip,backgroundColor,backgroundImage,backgroundOrigin,backgroundPosition,backgroundPositionX,backgroundPositionY,backgroundRepeat,backgroundSize,blockSize,border,borderBlock,borderBlockColor,borderBlockEnd,borderBlockEndColor,borderBlockEndStyle,borderBlockEndWidth,borderBlockStart,borderBlockStartColor,borderBlockStartStyle,borderBlockStartWidth,borderBlockStyle,borderBlockWidth,borderBottom,borderBottomColor,borderBottomLeftRadius,borderBottomRightRadius,borderBottomStyle,borderBottomWidth,borderCollapse,borderColor,borderEndEndRadius,borderEndStartRadius,borderImage,borderImageOutset,borderImageRepeat,borderImageSlice,borderImageSource,borderImageWidth,borderInline,borderInlineColor,borderInlineEnd,borderInlineEndColor,borderInlineEndStyle,borderInlineEndWidth,borderInlineStart,borderInlineStartColor,borderInlineStartStyle,borderInlineStartWidth,borderInlineStyle,borderInlineWidth,borderLeft,borderLeftColor,borderLeftStyle,borderLeftWidth,borderRadius,borderRight,borderRightColor,borderRightStyle,borderRightWidth,borderSpacing,borderStartEndRadius,borderStartStartRadius,borderStyle,borderTop,borderTopColor,borderTopLeftRadius,borderTopRightRadius,borderTopStyle,borderTopWidth,borderWidth,bottom,boxAlign,boxDecorationBreak,boxDirection,boxFlex,boxFlexGroup,boxLines,boxOrdinalGroup,boxOrient,boxPack,boxShadow,boxSizing,breakAfter,breakBefore,breakInside,captionSide,caret,caretColor,caretShape,clear,clip,clipPath,clipRule,color,colorInterpolationFilters,colorScheme,columnCount,columnFill,columnGap,columnRule,columnRuleColor,columnRuleStyle,columnRuleWidth,columnSpan,columnWidth,columns,contain,containIntrinsicBlockSize,containIntrinsicHeight,containIntrinsicInlineSize,containIntrinsicSize,containIntrinsicWidth,container,containerName,containerType,content,contentVisibility,cornerShape,counterIncrement,counterReset,counterSet,cursor,cx,cy,d,direction,display,dominantBaseline,emptyCells,fieldSizing,fill,fillOpacity,fillRule,filter,flex,flexBasis,flexDirection,flexFlow,flexGrow,flexShrink,flexWrap,float,floodColor,floodOpacity,font,fontFamily,fontFeatureSettings,fontKerning,fontLanguageOverride,fontOpticalSizing,fontPalette,fontSize,fontSizeAdjust,fontSmooth,fontStretch,fontStyle,fontSynthesis,fontSynthesisPosition,fontSynthesisSmallCaps,fontSynthesisStyle,fontSynthesisWeight,fontVariant,fontVariantAlternates,fontVariantCaps,fontVariantEastAsian,fontVariantEmoji,fontVariantLigatures,fontVariantNumeric,fontVariantPosition,fontVariationSettings,fontWeight,forcedColorAdjust,gap,grid,gridArea,gridAutoColumns,gridAutoFlow,gridAutoRows,gridColumn,gridColumnEnd,gridColumnGap,gridColumnStart,gridGap,gridRow,gridRowEnd,gridRowGap,gridRowStart,gridTemplate,gridTemplateAreas,gridTemplateColumns,gridTemplateRows,hangingPunctuation,height,hyphenateCharacter,hyphenateLimitChars,hyphens,imageOrientation,imageRendering,imageResolution,imeMode,initialLetter,initialLetterAlign,inlineSize,inset,insetBlock,insetBlockEnd,insetBlockStart,insetInline,insetInlineEnd,insetInlineStart,interpolateSize,isolation,justifyContent,justifyItems,justifySelf,justifyTracks,left,letterSpacing,lightingColor,lineBreak,lineClamp,lineHeight,lineHeightStep,listStyle,listStyleImage,listStylePosition,listStyleType,margin,marginBlock,marginBlockEnd,marginBlockStart,marginBottom,marginInline,marginInlineEnd,marginInlineStart,marginLeft,marginRight,marginTop,marginTrim,marker,markerEnd,markerMid,markerStart,mask,maskBorder,maskBorderMode,maskBorderOutset,maskBorderRepeat,maskBorderSlice,maskBorderSource,maskBorderWidth,maskClip,maskComposite,maskImage,maskMode,maskOrigin,maskPosition,maskRepeat,maskSize,maskType,masonryAutoFlow,mathDepth,mathShift,mathStyle,maxBlockSize,maxHeight,maxInlineSize,maxLines,maxWidth,minBlockSize,minHeight,minInlineSize,minWidth,mixBlendMode,objectFit,objectPosition,offset,offsetAnchor,offsetDistance,offsetPath,offsetPosition,offsetRotate,opacity,order,orphans,outline,outlineColor,outlineOffset,outlineStyle,outlineWidth,overflow,overflowAnchor,overflowBlock,overflowClipBox,overflowClipMargin,overflowInline,overflowWrap,overflowX,overflowY,overlay,overscrollBehavior,overscrollBehaviorBlock,overscrollBehaviorInline,overscrollBehaviorX,overscrollBehaviorY,padding,paddingBlock,paddingBlockEnd,paddingBlockStart,paddingBottom,paddingInline,paddingInlineEnd,paddingInlineStart,paddingLeft,paddingRight,paddingTop,page,pageBreakAfter,pageBreakBefore,pageBreakInside,paintOrder,perspective,perspectiveOrigin,placeContent,placeItems,placeSelf,pointerEvents,position,positionAnchor,positionArea,positionTry,positionTryFallbacks,positionTryOrder,positionVisibility,printColorAdjust,quotes,r,resize,right,rotate,rowGap,rubyAlign,rubyMerge,rubyPosition,rx,ry,scale,scrollBehavior,scrollMargin,scrollMarginBlock,scrollMarginBlockEnd,scrollMarginBlockStart,scrollMarginBottom,scrollMarginInline,scrollMarginInlineEnd,scrollMarginInlineStart,scrollMarginLeft,scrollMarginRight,scrollMarginTop,scrollPadding,scrollPaddingBlock,scrollPaddingBlockEnd,scrollPaddingBlockStart,scrollPaddingBottom,scrollPaddingInline,scrollPaddingInlineEnd,scrollPaddingInlineStart,scrollPaddingLeft,scrollPaddingRight,scrollPaddingTop,scrollSnapAlign,scrollSnapCoordinate,scrollSnapDestination,scrollSnapPointsX,scrollSnapPointsY,scrollSnapStop,scrollSnapType,scrollSnapTypeX,scrollSnapTypeY,scrollTimeline,scrollTimelineAxis,scrollTimelineName,scrollbarColor,scrollbarGutter,scrollbarWidth,shapeImageThreshold,shapeMargin,shapeOutside,shapeRendering,stopColor,stopOpacity,stroke,strokeDasharray,strokeDashoffset,strokeLinecap,strokeLinejoin,strokeMiterlimit,strokeOpacity,strokeWidth,tabSize,tableLayout,textAlign,textAlignLast,textAnchor,textBox,textBoxEdge,textBoxTrim,textCombineUpright,textDecoration,textDecorationColor,textDecorationLine,textDecorationSkip,textDecorationSkipInk,textDecorationStyle,textDecorationThickness,textEmphasis,textEmphasisColor,textEmphasisPosition,textEmphasisStyle,textIndent,textJustify,textOrientation,textOverflow,textRendering,textShadow,textSizeAdjust,textSpacingTrim,textTransform,textUnderlineOffset,textUnderlinePosition,textWrap,textWrapMode,textWrapStyle,timelineScope,top,touchAction,transform,transformBox,transformOrigin,transformStyle,transition,transitionBehavior,transitionDelay,transitionDuration,transitionProperty,transitionTimingFunction,translate,unicodeBidi,userSelect,vectorEffect,verticalAlign,viewTimeline,viewTimelineAxis,viewTimelineInset,viewTimelineName,viewTransitionName,visibility,whiteSpace,whiteSpaceCollapse,widows,width,willChange,wordBreak,wordSpacing,wordWrap,writingMode,x,y,zIndex,zoom,alignmentBaseline,baselineShift,colorInterpolation,colorRendering,glyphOrientationVertical\".split(\",\").concat(userGenerated);\nconst properties = new Map(allCssProperties.map((prop) => [prop, true]));\nfunction memo(fn) {\n const cache = Object.create(null);\n return (arg) => {\n if (cache[arg] === void 0) cache[arg] = fn(arg);\n return cache[arg];\n };\n}\nconst cssPropertySelectorRegex = /&|@/;\nconst isCssProperty = /* @__PURE__ */ memo((prop) => {\n return properties.has(prop) || prop.startsWith(\"--\") || cssPropertySelectorRegex.test(prop);\n});\n//#endregion\nexport { allCssProperties, isCssProperty };\n";
|
|
426
454
|
//#endregion
|
|
427
455
|
//#region src/artifacts/js/is-valid-prop.ts
|
|
428
|
-
const
|
|
429
|
-
const memoFnDeclarationRegex = /function memo(
|
|
456
|
+
const cssPropListRegex = /const userGenerated = ".*?"\.split\(","\);\s*const allCssProperties = "(.*?)"\.split\(","\)\.concat\(userGenerated\);/;
|
|
457
|
+
const memoFnDeclarationRegex = /function memo(?:.+?)\n((?:var|const|let) cssPropertySelectorRegex)/s;
|
|
430
458
|
function generateIsValidProp(ctx) {
|
|
431
459
|
if (ctx.isTemplateLiteralSyntax) return;
|
|
432
460
|
let content = content$8;
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
461
|
+
const propertyList = content.match(cssPropListRegex);
|
|
462
|
+
if (!propertyList) throw new BambooError("NOT_FOUND", "Could not find the property list in the prebuilt is-valid-prop module. Its bundled shape has changed.");
|
|
463
|
+
const userProperties = match(ctx.jsx.styleProps).with("all", () => Array.from(ctx.properties)).with("minimal", () => ["css"]).with("none", () => ["css"]).exhaustive();
|
|
464
|
+
const browserProperties = ctx.jsx.styleProps === "all" ? propertyList[1].split(",") : [];
|
|
465
|
+
content = content.replace(cssPropListRegex, () => `const allCssProperties = "${uniq(browserProperties, userProperties).join(",")}".split(",");`);
|
|
466
|
+
content = content.replace(memoFnDeclarationRegex, "$1");
|
|
467
|
+
if (ctx.jsx.styleProps === "minimal" || ctx.jsx.styleProps === "none") content = content.replace("/* @__PURE__ */ memo(", "/* @__PURE__ */ (");
|
|
468
|
+
else content = ctx.file.import("memo", "../helpers") + "\n" + content;
|
|
439
469
|
content = ctx.file.import("splitProps", "../helpers") + "\n" + content;
|
|
440
470
|
content += `export const splitCssProps = (props) => splitProps(props, isCssProperty)`;
|
|
441
471
|
return {
|
|
@@ -487,6 +517,56 @@ function generatedJsxHelpers(ctx) {
|
|
|
487
517
|
`) };
|
|
488
518
|
}
|
|
489
519
|
//#endregion
|
|
520
|
+
//#region src/artifacts/js/package-json.ts
|
|
521
|
+
/**
|
|
522
|
+
* The generated output is a plain directory, not an installed package, so bundlers
|
|
523
|
+
* have no `sideEffects` hint for it and must assume every module mutates something.
|
|
524
|
+
* That keeps every module a barrel pulls in — importing a single component from
|
|
525
|
+
* `styled-system/jsx` retains all patterns.
|
|
526
|
+
*
|
|
527
|
+
* The CSS globs are required: `sideEffects: false` alone lets a bundler drop a bare
|
|
528
|
+
* `import 'styled-system/styles.css'`. Both shapes are listed because the stylesheet
|
|
529
|
+
* is emitted at the root (`styles.css`) and, under `splitting`, in `styles/`.
|
|
530
|
+
*
|
|
531
|
+
* `type: module` restates what the directory already is. Adding a package.json makes
|
|
532
|
+
* this directory its own package boundary, so `.js` output would otherwise stop
|
|
533
|
+
* inheriting the consumer's `type` and be re-read as CommonJS; the emitted code is
|
|
534
|
+
* always ESM. It is a no-op for the default `.mjs` extension.
|
|
535
|
+
*
|
|
536
|
+
* `private` is there because the same package boundary makes a workspace glob able to
|
|
537
|
+
* match this directory. It is never published.
|
|
538
|
+
*
|
|
539
|
+
* `name` has to be present for the same reason. This file used to be emitted without
|
|
540
|
+
* one, to keep two outputs in a single workspace from colliding — but a nameless
|
|
541
|
+
* package.json is not a package a workspace scanner skips, it is one it refuses:
|
|
542
|
+
* pnpm, npm and changesets all abort with `missing the "name" field` and no hint as
|
|
543
|
+
* to which directory produced it.
|
|
544
|
+
*
|
|
545
|
+
* The name is derived from `outdir` because that is the only input that is both
|
|
546
|
+
* deterministic and portable — `cwd` is absolute, so putting it here would make the
|
|
547
|
+
* generated output differ per machine. Two projects in one workspace that both keep
|
|
548
|
+
* the default `outdir` therefore still collide, but on a duplicate-name error that
|
|
549
|
+
* names both paths and is resolved by setting `outdir`, rather than on a missing
|
|
550
|
+
* field that points nowhere.
|
|
551
|
+
*/
|
|
552
|
+
function generatePackageJson(ctx) {
|
|
553
|
+
return { json: JSON.stringify({
|
|
554
|
+
name: toPackageName(ctx.config.outdir),
|
|
555
|
+
type: "module",
|
|
556
|
+
private: true,
|
|
557
|
+
sideEffects: ["*.css", "**/*.css"]
|
|
558
|
+
}, null, 2) + "\n" };
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* `outdir` is a path, npm names are not: it may be nested (`src/styled-system`), and
|
|
562
|
+
* npm rejects uppercase, a leading dot or underscore, and anything outside its
|
|
563
|
+
* url-safe set. Path segments are joined rather than dropped so that nested outputs
|
|
564
|
+
* stay distinct from one another.
|
|
565
|
+
*/
|
|
566
|
+
function toPackageName(outdir) {
|
|
567
|
+
return outdir.split(/[\\/]/).filter(Boolean).join("-").toLowerCase().replace(/[^a-z0-9\-._]/g, "-").replace(/^[._]+/, "") || "styled-system";
|
|
568
|
+
}
|
|
569
|
+
//#endregion
|
|
490
570
|
//#region src/artifacts/js/pattern.ts
|
|
491
571
|
function generatePattern(ctx, filters) {
|
|
492
572
|
if (ctx.patterns.isEmpty()) return;
|
|
@@ -497,7 +577,7 @@ function generatePattern(ctx, filters) {
|
|
|
497
577
|
transform,
|
|
498
578
|
defaultValues
|
|
499
579
|
})) ?? "";
|
|
500
|
-
const helperImports = ["getPatternStyles, patternFns"];
|
|
580
|
+
const helperImports = ["getPatternStyles, patternFns, memo"];
|
|
501
581
|
if (patternConfigFn.includes("__spreadValues")) helperImports.push("__spreadValues");
|
|
502
582
|
if (patternConfigFn.includes("__objRest")) helperImports.push("__objRest");
|
|
503
583
|
return {
|
|
@@ -550,7 +630,7 @@ function generatePattern(ctx, filters) {
|
|
|
550
630
|
return ${baseName}Config.transform(_styles, patternFns)
|
|
551
631
|
}
|
|
552
632
|
|
|
553
|
-
export const ${baseName} = (styles) => css(${styleFnName}(styles))
|
|
633
|
+
export const ${baseName} = /* @__PURE__ */ memo((styles) => css(${styleFnName}(styles)))
|
|
554
634
|
${baseName}.raw = ${styleFnName}
|
|
555
635
|
`
|
|
556
636
|
};
|
|
@@ -950,7 +1030,6 @@ function generatePreactJsxFactory(ctx) {
|
|
|
950
1030
|
return { js: outdent`
|
|
951
1031
|
import { h } from 'preact'
|
|
952
1032
|
import { forwardRef } from 'preact/compat'
|
|
953
|
-
import { useMemo } from 'preact/hooks'
|
|
954
1033
|
${ctx.file.import("defaultShouldForwardProp, composeShouldForwardProps, composeCvaFn, getDisplayName", "./factory-helper")}
|
|
955
1034
|
${ctx.file.import("isCssProperty", "./is-valid-prop")}
|
|
956
1035
|
${ctx.file.import("css, cx, cva", "../css/index")}
|
|
@@ -978,11 +1057,13 @@ function generatePreactJsxFactory(ctx) {
|
|
|
978
1057
|
const { as: Element = __base__, unstyled, children, ...restProps } = props
|
|
979
1058
|
|
|
980
1059
|
|
|
981
|
-
|
|
1060
|
+
// Not memoized, deliberately. \`restProps\` is rest destructuring, so it is a fresh
|
|
1061
|
+
// object on every render and a dependency on it can never match — a memo here is a
|
|
1062
|
+
// guaranteed miss that still costs a hook slot, a deps array and a retained cell.
|
|
1063
|
+
const combinedProps = Object.assign({}, defaultProps, restProps)
|
|
982
1064
|
|
|
983
|
-
const [htmlProps, forwardedProps, variantProps, styleProps, elementProps] =
|
|
984
|
-
|
|
985
|
-
}, [combinedProps])
|
|
1065
|
+
const [htmlProps, forwardedProps, variantProps, styleProps, elementProps] =
|
|
1066
|
+
splitProps(combinedProps, normalizeHTMLProps.keys, __shouldForwardProps__, __cvaFn__.variantKeys, isCssProperty)
|
|
986
1067
|
|
|
987
1068
|
function recipeClass() {
|
|
988
1069
|
const { css: cssStyles, ...propStyles } = styleProps
|
|
@@ -1810,7 +1891,7 @@ export type ${typeName}<T extends ElementType> = ComponentProps<T>
|
|
|
1810
1891
|
function generateReactJsxFactory(ctx) {
|
|
1811
1892
|
const { factoryName, componentName } = ctx.jsx;
|
|
1812
1893
|
return { js: outdent`
|
|
1813
|
-
import { createElement, forwardRef
|
|
1894
|
+
import { createElement, forwardRef } from 'react'
|
|
1814
1895
|
${ctx.file.import("css, cx, cva", "../css/index")}
|
|
1815
1896
|
${ctx.file.import("defaultShouldForwardProp, composeShouldForwardProps, composeCvaFn, getDisplayName", "./factory-helper")}
|
|
1816
1897
|
${ctx.file.import("splitProps, normalizeHTMLProps", "../helpers")}
|
|
@@ -1837,11 +1918,13 @@ function generateReactJsxFactory(ctx) {
|
|
|
1837
1918
|
const ${componentName} = /* @__PURE__ */ forwardRef(function ${componentName}(props, ref) {
|
|
1838
1919
|
const { as: Element = __base__, unstyled, children, ...restProps } = props
|
|
1839
1920
|
|
|
1840
|
-
|
|
1921
|
+
// Not memoized, deliberately. \`restProps\` is rest destructuring, so it is a fresh
|
|
1922
|
+
// object on every render and a dependency on it can never match — a memo here is a
|
|
1923
|
+
// guaranteed miss that still costs a hook slot, a deps array and a retained cell.
|
|
1924
|
+
const combinedProps = Object.assign({}, defaultProps, restProps)
|
|
1841
1925
|
|
|
1842
|
-
const [htmlProps, forwardedProps, variantProps, styleProps, elementProps] =
|
|
1843
|
-
|
|
1844
|
-
}, [combinedProps])
|
|
1926
|
+
const [htmlProps, forwardedProps, variantProps, styleProps, elementProps] =
|
|
1927
|
+
splitProps(combinedProps, normalizeHTMLProps.keys, __shouldForwardProps__, __cvaFn__.variantKeys, isCssProperty)
|
|
1845
1928
|
|
|
1846
1929
|
function recipeClass() {
|
|
1847
1930
|
const { css: cssStyles, ...propStyles } = styleProps
|
|
@@ -4526,8 +4609,8 @@ function generatePropTypes(ctx) {
|
|
|
4526
4609
|
* fontSize: '[123px]', // ⚠️ will not throw even if you haven't defined 123px as a token
|
|
4527
4610
|
* })
|
|
4528
4611
|
*
|
|
4529
|
-
* @see https://
|
|
4530
|
-
* @see https://
|
|
4612
|
+
* @see https://bamboocss.com/docs/concepts/writing-styles#stricttokens
|
|
4613
|
+
* @see https://bamboocss.com/docs/concepts/writing-styles#strictpropertyvalues
|
|
4531
4614
|
*/
|
|
4532
4615
|
export type WithEscapeHatch<T> = T | \`[\${string}]\` | WithColorOpacityModifier<T> | WithImportant<T>
|
|
4533
4616
|
|
|
@@ -4539,7 +4622,7 @@ function generatePropTypes(ctx) {
|
|
|
4539
4622
|
* display: 'abc', // ❌ will throw
|
|
4540
4623
|
* })
|
|
4541
4624
|
*
|
|
4542
|
-
* @see https://
|
|
4625
|
+
* @see https://bamboocss.com/docs/concepts/writing-styles#strictpropertyvalues
|
|
4543
4626
|
*/
|
|
4544
4627
|
export type OnlyKnown<Key, Value> = Value extends boolean
|
|
4545
4628
|
? Value
|
|
@@ -4955,6 +5038,15 @@ function setupHelpers(ctx) {
|
|
|
4955
5038
|
}]
|
|
4956
5039
|
};
|
|
4957
5040
|
}
|
|
5041
|
+
function setupPackageJson(ctx) {
|
|
5042
|
+
return {
|
|
5043
|
+
id: "package.json",
|
|
5044
|
+
files: [{
|
|
5045
|
+
file: "package.json",
|
|
5046
|
+
code: generatePackageJson(ctx).json
|
|
5047
|
+
}]
|
|
5048
|
+
};
|
|
5049
|
+
}
|
|
4958
5050
|
function setupDesignTokens(ctx) {
|
|
4959
5051
|
const code = generateTokenJs(ctx);
|
|
4960
5052
|
return {
|
|
@@ -5410,6 +5502,7 @@ const filterArtifactsFiles = (artifacts, filters) => {
|
|
|
5410
5502
|
});
|
|
5411
5503
|
};
|
|
5412
5504
|
const entries = [
|
|
5505
|
+
["package.json", setupPackageJson],
|
|
5413
5506
|
["helpers", setupHelpers],
|
|
5414
5507
|
["design-tokens", setupDesignTokens],
|
|
5415
5508
|
["types-jsx", setupJsxTypes],
|
|
@@ -6053,6 +6146,136 @@ var Generator = class extends Context {
|
|
|
6053
6146
|
const decoder = this.decoder.collect(this.encoder);
|
|
6054
6147
|
sheet.processDecoder(decoder);
|
|
6055
6148
|
};
|
|
6149
|
+
/**
|
|
6150
|
+
* Drop token css variables nothing can reach. Call this only once the sheet holds the
|
|
6151
|
+
* whole stylesheet — a baseline-only sheet has no utilities to reference anything, so
|
|
6152
|
+
* every token would look unused.
|
|
6153
|
+
*
|
|
6154
|
+
* `keep` carries references this cannot see for itself; see `collectTokenReferences`.
|
|
6155
|
+
*/
|
|
6156
|
+
pruneTokens = (sheet, keep) => {
|
|
6157
|
+
if (!this.config.pruneUnusedTokens) return;
|
|
6158
|
+
const layers = sheet.layers;
|
|
6159
|
+
const result = pruneTokenVars({
|
|
6160
|
+
scan: [
|
|
6161
|
+
layers.reset,
|
|
6162
|
+
layers.base,
|
|
6163
|
+
layers.tokens,
|
|
6164
|
+
layers.recipes,
|
|
6165
|
+
layers.recipes_base,
|
|
6166
|
+
layers.recipes_slots,
|
|
6167
|
+
layers.recipes_slots_base,
|
|
6168
|
+
layers.utilities,
|
|
6169
|
+
layers.compositions
|
|
6170
|
+
],
|
|
6171
|
+
target: layers.tokens,
|
|
6172
|
+
tokenVars: this.getTokenVarNames(),
|
|
6173
|
+
keep: new Set([
|
|
6174
|
+
...this.getAlwaysKeptTokenVars(),
|
|
6175
|
+
...this.getThemeTokenVars(),
|
|
6176
|
+
...keep ?? []
|
|
6177
|
+
])
|
|
6178
|
+
});
|
|
6179
|
+
logger.debug("prune:tokens", `Removed ${result.removed} unused token css variable(s)`);
|
|
6180
|
+
return result;
|
|
6181
|
+
};
|
|
6182
|
+
/**
|
|
6183
|
+
* Drop `@keyframes` nothing can reach. Same completeness requirement as
|
|
6184
|
+
* `pruneTokens`: the sheet has to hold the whole stylesheet, or every keyframe looks
|
|
6185
|
+
* unused for want of a utility to reference it.
|
|
6186
|
+
*
|
|
6187
|
+
* `keep` carries names this cannot see for itself; see `collectKeyframeReferences`.
|
|
6188
|
+
*/
|
|
6189
|
+
pruneKeyframes = (sheet, keep) => {
|
|
6190
|
+
if (!this.config.pruneUnusedKeyframes) return;
|
|
6191
|
+
const layers = sheet.layers;
|
|
6192
|
+
const keyframeNames = new Set(Object.keys(this.config.theme?.keyframes ?? {}));
|
|
6193
|
+
const result = pruneKeyframes({
|
|
6194
|
+
scan: [
|
|
6195
|
+
layers.reset,
|
|
6196
|
+
layers.base,
|
|
6197
|
+
layers.tokens,
|
|
6198
|
+
layers.recipes,
|
|
6199
|
+
layers.recipes_base,
|
|
6200
|
+
layers.recipes_slots,
|
|
6201
|
+
layers.recipes_slots_base,
|
|
6202
|
+
layers.utilities,
|
|
6203
|
+
layers.compositions
|
|
6204
|
+
],
|
|
6205
|
+
target: layers.tokens,
|
|
6206
|
+
keyframeNames,
|
|
6207
|
+
keep: new Set([...this.getThemeKeyframeNames(keyframeNames), ...keep ?? []])
|
|
6208
|
+
});
|
|
6209
|
+
logger.debug("prune:keyframes", `Removed ${result.removed} unused keyframe(s)`);
|
|
6210
|
+
return result;
|
|
6211
|
+
};
|
|
6212
|
+
/**
|
|
6213
|
+
* Keyframes the themes name.
|
|
6214
|
+
*
|
|
6215
|
+
* A theme is emitted as its own artifact and injected at runtime, so its css is not in
|
|
6216
|
+
* the sheet being pruned. A theme that points an animation token at a different
|
|
6217
|
+
* keyframe than the base does — `--animations-enter: fade-in` in the base and
|
|
6218
|
+
* `slide-up` under `dark` — would otherwise have that keyframe removed, because
|
|
6219
|
+
* nothing in the pruned sheet ever names it.
|
|
6220
|
+
*/
|
|
6221
|
+
getThemeKeyframeNames = (keyframeNames) => {
|
|
6222
|
+
const names = /* @__PURE__ */ new Set();
|
|
6223
|
+
const themes = this.config.themes;
|
|
6224
|
+
if (!themes || !keyframeNames.size) return names;
|
|
6225
|
+
for (const themeName of Object.keys(themes)) for (const token of getThemeCss(this, themeName).split(/[^\w-]+/)) if (keyframeNames.has(token)) names.add(token);
|
|
6226
|
+
return names;
|
|
6227
|
+
};
|
|
6228
|
+
/**
|
|
6229
|
+
* Every custom property the token system declares. Used as the allow-list of what may
|
|
6230
|
+
* be removed, so custom properties from `globalCss` are never touched.
|
|
6231
|
+
*/
|
|
6232
|
+
getTokenVarNames = () => {
|
|
6233
|
+
const names = /* @__PURE__ */ new Set();
|
|
6234
|
+
for (const values of this.tokens.view.vars.values()) for (const name of values.keys()) names.add(name);
|
|
6235
|
+
return names;
|
|
6236
|
+
};
|
|
6237
|
+
/**
|
|
6238
|
+
* Everything the themes refer to.
|
|
6239
|
+
*
|
|
6240
|
+
* A theme is emitted as its own artifact and injected at runtime, so its css is not in
|
|
6241
|
+
* the sheet being pruned and nothing there points at what it needs. A theme that maps a
|
|
6242
|
+
* token onto a base colour would otherwise be left referring to a declaration that has
|
|
6243
|
+
* been removed.
|
|
6244
|
+
*/
|
|
6245
|
+
getThemeTokenVars = () => {
|
|
6246
|
+
const names = /* @__PURE__ */ new Set();
|
|
6247
|
+
const themes = this.config.themes;
|
|
6248
|
+
if (!themes) return names;
|
|
6249
|
+
for (const themeName of Object.keys(themes)) for (const name of cssVarRefs(getThemeCss(this, themeName))) names.add(name);
|
|
6250
|
+
return names;
|
|
6251
|
+
};
|
|
6252
|
+
/**
|
|
6253
|
+
* Tokens whose javascript value is a `var()` reference rather than a literal.
|
|
6254
|
+
* `token('colors.text')` hands those to the caller as a reference, so the declaration
|
|
6255
|
+
* has to survive whether or not the generated css mentions it. Ordinary tokens resolve
|
|
6256
|
+
* to a literal in javascript and need no such exemption.
|
|
6257
|
+
*
|
|
6258
|
+
* The two cases mirror `generateTokenJs`, which is what decides the value javascript
|
|
6259
|
+
* actually receives:
|
|
6260
|
+
*
|
|
6261
|
+
* - A virtual token, or one carrying a condition, is handed its own `varRef`.
|
|
6262
|
+
* - A negative token is handed `calc(var(--x) * -1)`, so it is a reference too — but to
|
|
6263
|
+
* the *positive* token's declaration. Its own var is never declared, so the name has
|
|
6264
|
+
* to come out of the value.
|
|
6265
|
+
*/
|
|
6266
|
+
getAlwaysKeptTokenVars = () => {
|
|
6267
|
+
const names = /* @__PURE__ */ new Set();
|
|
6268
|
+
this.tokens.allTokens.forEach((token) => {
|
|
6269
|
+
const { isVirtual, isNegative, condition, var: varName } = token.extensions;
|
|
6270
|
+
if (isVirtual || condition !== "base") {
|
|
6271
|
+
if (varName) names.add(varName.startsWith("--") ? varName : `--${varName}`);
|
|
6272
|
+
return;
|
|
6273
|
+
}
|
|
6274
|
+
if (!isNegative) return;
|
|
6275
|
+
for (const name of cssVarRefs(token.value)) names.add(name);
|
|
6276
|
+
});
|
|
6277
|
+
return names;
|
|
6278
|
+
};
|
|
6056
6279
|
getParserCss = (decoder) => {
|
|
6057
6280
|
return generateParserCss(this, decoder);
|
|
6058
6281
|
};
|