@barocss/browser 0.5.0 → 0.7.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/README.md +25 -1
- package/dist/cdn/barocss.js +455 -47
- package/dist/cdn/barocss.js.map +1 -1
- package/dist/cdn/barocss.umd.cjs +1 -1
- package/dist/cdn/barocss.umd.cjs.map +1 -1
- package/dist/index.d.ts +124 -19
- package/dist/index.es.js +298 -148
- package/dist/index.umd.js +7 -5
- package/package.json +2 -2
package/dist/cdn/barocss.js
CHANGED
|
@@ -178,8 +178,12 @@ function clearContextCaches(ctx) {
|
|
|
178
178
|
}
|
|
179
179
|
const utilityRegistry = [];
|
|
180
180
|
function registerUtility(util, ctx) {
|
|
181
|
-
|
|
182
|
-
|
|
181
|
+
const state = ctx && getContextState(ctx);
|
|
182
|
+
if (ctx && !state) throw new Error("Utility registration requires a context from createContext");
|
|
183
|
+
(state?.utilities || utilityRegistry).push(util);
|
|
184
|
+
if (ctx) {
|
|
185
|
+
clearContextCaches(ctx);
|
|
186
|
+
} else {
|
|
183
187
|
parseResultCache.clear();
|
|
184
188
|
utilityCache.clear();
|
|
185
189
|
}
|
|
@@ -273,7 +277,12 @@ function staticUtility(name, decls, opts, ctx) {
|
|
|
273
277
|
description: opts?.description,
|
|
274
278
|
category: opts?.category,
|
|
275
279
|
priority: opts?.priority
|
|
276
|
-
});
|
|
280
|
+
}, ctx);
|
|
281
|
+
}
|
|
282
|
+
function spacingKeyValue(ctx, key, negative) {
|
|
283
|
+
if (key === "px" || !/^[a-zA-Z][\w-]*$/.test(key) || ctx.theme("spacing", key) == null) return null;
|
|
284
|
+
const ref = `var(--spacing-${key})`;
|
|
285
|
+
return negative ? `calc(${ref} * -1)` : ref;
|
|
277
286
|
}
|
|
278
287
|
function functionalUtility(opts, ctx) {
|
|
279
288
|
registerUtility({
|
|
@@ -343,14 +352,17 @@ function functionalUtility(opts, ctx) {
|
|
|
343
352
|
if (opts.supportsFraction && /^-?\d+\/\d+$/.test(value)) {
|
|
344
353
|
finalValue = value;
|
|
345
354
|
}
|
|
355
|
+
const spacingKey = opts.spacingKeys ? spacingKeyValue(ctx2, String(finalValue).replace(/^-/, ""), !!parsedUtility.negative) : null;
|
|
346
356
|
if (parsedUtility.negative && opts.supportsNegative && opts.handleNegativeBareValue) {
|
|
347
|
-
const bare = opts.handleNegativeBareValue({ value: String(finalValue).replace(/^-/, ""), ctx: ctx2, token, extra });
|
|
357
|
+
const bare = opts.handleNegativeBareValue({ value: String(finalValue).replace(/^-/, ""), ctx: ctx2, token, extra }) ?? spacingKey;
|
|
348
358
|
if (bare == null) return [];
|
|
349
359
|
finalValue = bare;
|
|
350
360
|
} else if (opts.handleBareValue) {
|
|
351
|
-
const bare = opts.handleBareValue({ value: finalValue, ctx: ctx2, token, extra });
|
|
361
|
+
const bare = opts.handleBareValue({ value: finalValue, ctx: ctx2, token, extra }) ?? spacingKey;
|
|
352
362
|
if (bare == null) return [];
|
|
353
363
|
finalValue = bare;
|
|
364
|
+
} else if (spacingKey) {
|
|
365
|
+
finalValue = spacingKey;
|
|
354
366
|
} else if (!/^-?(\d|\.\d)/.test(String(finalValue))) {
|
|
355
367
|
return [];
|
|
356
368
|
}
|
|
@@ -366,7 +378,7 @@ function functionalUtility(opts, ctx) {
|
|
|
366
378
|
description: opts.description,
|
|
367
379
|
category: opts.category,
|
|
368
380
|
priority: opts.priority
|
|
369
|
-
});
|
|
381
|
+
}, ctx);
|
|
370
382
|
}
|
|
371
383
|
const MATH_FNS = /* @__PURE__ */ new Set(["calc", "min", "max", "clamp"]);
|
|
372
384
|
function expandThemeFunctions(value) {
|
|
@@ -485,14 +497,23 @@ function parseClassName(className, ctx) {
|
|
|
485
497
|
if (cache.has(className)) {
|
|
486
498
|
return cache.get(className);
|
|
487
499
|
}
|
|
488
|
-
let important = false;
|
|
489
500
|
let realClassName = className;
|
|
490
|
-
|
|
501
|
+
const classPrefix = ctx ? configuredClassPrefix(ctx) : "";
|
|
502
|
+
if (classPrefix) {
|
|
503
|
+
if (!className.startsWith(classPrefix + ":")) {
|
|
504
|
+
const none = { modifiers: [], utility: null };
|
|
505
|
+
cache.set(className, none);
|
|
506
|
+
return none;
|
|
507
|
+
}
|
|
508
|
+
realClassName = className.slice(classPrefix.length + 1);
|
|
509
|
+
}
|
|
510
|
+
let important = false;
|
|
511
|
+
if (realClassName.startsWith("!")) {
|
|
491
512
|
important = true;
|
|
492
|
-
realClassName =
|
|
493
|
-
} else if (
|
|
513
|
+
realClassName = realClassName.slice(1);
|
|
514
|
+
} else if (realClassName.length > 1 && realClassName.endsWith("!")) {
|
|
494
515
|
important = true;
|
|
495
|
-
realClassName =
|
|
516
|
+
realClassName = realClassName.slice(0, -1);
|
|
496
517
|
}
|
|
497
518
|
const tokens = tokenize(realClassName);
|
|
498
519
|
const result = parseTokens(tokens, ctx);
|
|
@@ -502,6 +523,10 @@ function parseClassName(className, ctx) {
|
|
|
502
523
|
cache.set(className, result);
|
|
503
524
|
return result;
|
|
504
525
|
}
|
|
526
|
+
function configuredClassPrefix(ctx) {
|
|
527
|
+
const configured = ctx.config("prefix");
|
|
528
|
+
return typeof configured === "string" && /^[a-z]+$/.test(configured) ? configured : "";
|
|
529
|
+
}
|
|
505
530
|
function parseTokens(tokens, ctx) {
|
|
506
531
|
const modifiers = [];
|
|
507
532
|
let utility = null;
|
|
@@ -561,6 +586,18 @@ function isSafeVariantToken(value) {
|
|
|
561
586
|
function hasCommentToken(value) {
|
|
562
587
|
return value.includes("/*") || value.includes("*/");
|
|
563
588
|
}
|
|
589
|
+
function hasCommentDelimiter(text) {
|
|
590
|
+
for (let i = 0; i < text.length - 1; i++) {
|
|
591
|
+
const c = text[i];
|
|
592
|
+
if (c === "\\") {
|
|
593
|
+
i++;
|
|
594
|
+
continue;
|
|
595
|
+
}
|
|
596
|
+
const n = text[i + 1];
|
|
597
|
+
if (c === "/" && n === "*" || c === "*" && n === "/") return true;
|
|
598
|
+
}
|
|
599
|
+
return false;
|
|
600
|
+
}
|
|
564
601
|
function isStructureSafeValue(value) {
|
|
565
602
|
if (hasCommentToken(value)) return false;
|
|
566
603
|
return isSafeVariantValue(value, true);
|
|
@@ -708,6 +745,7 @@ function parseUtility(value, ctx) {
|
|
|
708
745
|
priority
|
|
709
746
|
};
|
|
710
747
|
}
|
|
748
|
+
const isSafePrelude = (text) => !hasCommentDelimiter(String(text ?? ""));
|
|
711
749
|
const isSafeDecl = (prop, value) => isStructureSafeValue(String(prop)) && isStructureSafeValue(String(value ?? ""));
|
|
712
750
|
const importantPrefix = "!important";
|
|
713
751
|
function astToCss(ast, baseSelector, opts, _indent = "") {
|
|
@@ -771,6 +809,7 @@ function astToCss(ast, baseSelector, opts, _indent = "") {
|
|
|
771
809
|
}).join(", ");
|
|
772
810
|
}
|
|
773
811
|
}
|
|
812
|
+
if (!isSafePrelude(selector)) return "";
|
|
774
813
|
if (minify) {
|
|
775
814
|
const css = `${indent}${selector}{${astToCss(
|
|
776
815
|
node.nodes,
|
|
@@ -795,6 +834,7 @@ ${astToCss(
|
|
|
795
834
|
}
|
|
796
835
|
}
|
|
797
836
|
case "style-rule": {
|
|
837
|
+
if (!isSafePrelude(node.selector)) return "";
|
|
798
838
|
if (minify) {
|
|
799
839
|
const css = `${indent}${node.selector} {${astToCss(
|
|
800
840
|
node.nodes,
|
|
@@ -819,6 +859,7 @@ ${astToCss(
|
|
|
819
859
|
}
|
|
820
860
|
}
|
|
821
861
|
case "at-rule": {
|
|
862
|
+
if (!isSafePrelude(node.name) || !isSafePrelude(node.params)) return "";
|
|
822
863
|
if (minify) {
|
|
823
864
|
const css = `${indent}@${node.name} ${node.params}{${astToCss(
|
|
824
865
|
node.nodes,
|
|
@@ -871,7 +912,7 @@ function rootToCss(nodes, opts) {
|
|
|
871
912
|
if (isSafeDecl(node.prop, node.value)) {
|
|
872
913
|
list.push(`${node.prop}: ${node.value};`);
|
|
873
914
|
}
|
|
874
|
-
} else if (node.type === "at-rule") {
|
|
915
|
+
} else if (node.type === "at-rule" && isSafePrelude(node.name) && isSafePrelude(node.params)) {
|
|
875
916
|
{
|
|
876
917
|
list.push(
|
|
877
918
|
`@${node.name} ${node.params} {
|
|
@@ -1005,24 +1046,41 @@ function animationToCssVars(animations2) {
|
|
|
1005
1046
|
}
|
|
1006
1047
|
return result;
|
|
1007
1048
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1049
|
+
const COMMENT_OR_BLOCK = /\/\*|\*\/|[{};]/;
|
|
1050
|
+
function keyframesBlock(name, frames) {
|
|
1051
|
+
if (!name || COMMENT_OR_BLOCK.test(name) || /\s/.test(name) || !frames || typeof frames !== "object") return "";
|
|
1052
|
+
let body = "";
|
|
1053
|
+
for (const [step, props] of Object.entries(frames)) {
|
|
1054
|
+
if (COMMENT_OR_BLOCK.test(step) || !props || typeof props !== "object") return "";
|
|
1055
|
+
let decls = "";
|
|
1056
|
+
for (const [prop, value] of Object.entries(props)) {
|
|
1057
|
+
const v2 = String(value);
|
|
1058
|
+
if (COMMENT_OR_BLOCK.test(prop) || COMMENT_OR_BLOCK.test(v2)) return "";
|
|
1059
|
+
decls += ` ${prop}: ${v2};
|
|
1014
1060
|
`;
|
|
1015
|
-
for (const step in frames) {
|
|
1016
|
-
css += ` ${step} {`;
|
|
1017
|
-
const props = frames[step];
|
|
1018
|
-
for (const prop in props) {
|
|
1019
|
-
css += ` ${prop}: ${props[prop]};`;
|
|
1020
|
-
}
|
|
1021
|
-
css += " }\n";
|
|
1022
1061
|
}
|
|
1023
|
-
|
|
1062
|
+
body += ` ${step} {
|
|
1063
|
+
${decls} }
|
|
1064
|
+
`;
|
|
1065
|
+
}
|
|
1066
|
+
return `@keyframes ${name} {
|
|
1067
|
+
${body}}`;
|
|
1068
|
+
}
|
|
1069
|
+
function referencedKeyframes(css, ctx) {
|
|
1070
|
+
if (!css.includes("animation")) return [];
|
|
1071
|
+
const all = ctx.theme("keyframes");
|
|
1072
|
+
if (!all || typeof all !== "object") return [];
|
|
1073
|
+
const names = /* @__PURE__ */ new Set();
|
|
1074
|
+
for (const m of css.matchAll(/(?:^|[\s;{])animation(?:-name)?\s*:\s*([^;}]+)/g)) {
|
|
1075
|
+
const value = m[1].replace(/var\(--animate-([\w-]+)\)/g, (whole, key) => {
|
|
1076
|
+
const v2 = ctx.theme("animations", key) ?? ctx.theme("animation", key);
|
|
1077
|
+
return typeof v2 === "string" ? v2 : whole;
|
|
1078
|
+
});
|
|
1079
|
+
for (const word of value.split(/[\s,()]+/)) {
|
|
1080
|
+
if (word && Object.prototype.hasOwnProperty.call(all, word)) names.add(word);
|
|
1081
|
+
}
|
|
1024
1082
|
}
|
|
1025
|
-
return
|
|
1083
|
+
return [...names].map((n) => keyframesBlock(n, all[n])).filter(Boolean);
|
|
1026
1084
|
}
|
|
1027
1085
|
function transitionTimingFunctionToCssVars(transition) {
|
|
1028
1086
|
const result = {};
|
|
@@ -1090,7 +1148,7 @@ function themeToCssVarsAll(theme) {
|
|
|
1090
1148
|
...borderRadiusToCssVars(theme.borderRadius),
|
|
1091
1149
|
...zIndexToCssVars(theme.zIndex),
|
|
1092
1150
|
...opacityToCssVars(theme.opacity),
|
|
1093
|
-
...animationToCssVars(theme.animations),
|
|
1151
|
+
...animationToCssVars({ ...theme.animations, ...theme.animation }),
|
|
1094
1152
|
...transitionTimingFunctionToCssVars(theme.transitionTimingFunction),
|
|
1095
1153
|
...transitionDurationToCssVars(theme.transitionDuration),
|
|
1096
1154
|
...transitionDelayToCssVars(theme.transitionDelay),
|
|
@@ -1099,8 +1157,13 @@ function themeToCssVarsAll(theme) {
|
|
|
1099
1157
|
// keyframes handled separately
|
|
1100
1158
|
};
|
|
1101
1159
|
}
|
|
1160
|
+
function isSelfReferencingVar(name, value) {
|
|
1161
|
+
if (typeof value !== "string") return false;
|
|
1162
|
+
const m = /^var\(\s*(--[\w-]+)\s*(?:,[\s\S]*)?\)$/.exec(value.trim());
|
|
1163
|
+
return !!m && m[1] === name.trim();
|
|
1164
|
+
}
|
|
1102
1165
|
function toCssVarsBlock(vars, extra = "") {
|
|
1103
|
-
return ":root,:host {\n" + Object.entries(vars).map(([k, v2]) => ` ${k}: ${v2};`).join("\n") + "\n}\n" + extra + "\n";
|
|
1166
|
+
return ":root,:host {\n" + Object.entries(vars).filter(([k, v2]) => !isSelfReferencingVar(k, v2)).map(([k, v2]) => ` ${k}: ${v2};`).join("\n") + "\n}\n" + extra + "\n";
|
|
1104
1167
|
}
|
|
1105
1168
|
const BARO_VAR = /--baro-/g;
|
|
1106
1169
|
const PREFIXED_KEYS = /* @__PURE__ */ new Set(["prop", "value", "params", "selector", "nodes", "items"]);
|
|
@@ -1451,6 +1514,7 @@ function generateCssRules(classList, ctx, opts) {
|
|
|
1451
1514
|
const css = rootToCss([node]);
|
|
1452
1515
|
rootCssList.push(css);
|
|
1453
1516
|
}
|
|
1517
|
+
rootCssList.push(...referencedKeyframes(cssList.join("\n"), ctx));
|
|
1454
1518
|
return {
|
|
1455
1519
|
cls,
|
|
1456
1520
|
ast: allCleanAst,
|
|
@@ -1713,6 +1777,15 @@ class IncrementalParser {
|
|
|
1713
1777
|
markProcessed(cls) {
|
|
1714
1778
|
this.processedClasses.add(cls);
|
|
1715
1779
|
}
|
|
1780
|
+
/**
|
|
1781
|
+
* Forgets that a class was processed, so a later request generates it again
|
|
1782
|
+
* (used when the browser runtime reclaims an unused class's rules, #269).
|
|
1783
|
+
*
|
|
1784
|
+
* @param cls - The CSS class name to forget
|
|
1785
|
+
*/
|
|
1786
|
+
unmarkProcessed(cls) {
|
|
1787
|
+
this.processedClasses.delete(cls);
|
|
1788
|
+
}
|
|
1716
1789
|
/**
|
|
1717
1790
|
* Process classes synchronously and update BrowserRuntime cache
|
|
1718
1791
|
* This method is used by ChangeDetector for scan operations
|
|
@@ -2238,11 +2311,7 @@ const keyframes = {
|
|
|
2238
2311
|
}
|
|
2239
2312
|
},
|
|
2240
2313
|
ping: {
|
|
2241
|
-
"75%": {
|
|
2242
|
-
transform: "scale(2)",
|
|
2243
|
-
opacity: "0"
|
|
2244
|
-
},
|
|
2245
|
-
"100%": {
|
|
2314
|
+
"75%, 100%": {
|
|
2246
2315
|
transform: "scale(2)",
|
|
2247
2316
|
opacity: "0"
|
|
2248
2317
|
}
|
|
@@ -2252,14 +2321,15 @@ const keyframes = {
|
|
|
2252
2321
|
opacity: "0.5"
|
|
2253
2322
|
}
|
|
2254
2323
|
},
|
|
2324
|
+
// #274: Tailwind 4.1.13's frames (0%/100% share the up position; 50% is the floor).
|
|
2255
2325
|
bounce: {
|
|
2256
|
-
"0%": {
|
|
2326
|
+
"0%, 100%": {
|
|
2257
2327
|
transform: "translateY(-25%)",
|
|
2258
|
-
"animation-timing-function": "cubic-bezier(0.8,0,1,1)"
|
|
2328
|
+
"animation-timing-function": "cubic-bezier(0.8, 0, 1, 1)"
|
|
2259
2329
|
},
|
|
2260
|
-
"
|
|
2330
|
+
"50%": {
|
|
2261
2331
|
transform: "none",
|
|
2262
|
-
"animation-timing-function": "cubic-bezier(0,0,0.2,1)"
|
|
2332
|
+
"animation-timing-function": "cubic-bezier(0, 0, 0.2, 1)"
|
|
2263
2333
|
}
|
|
2264
2334
|
}
|
|
2265
2335
|
};
|
|
@@ -2334,6 +2404,48 @@ const defaultTheme = {
|
|
|
2334
2404
|
// Tailwind 4.1.13 --aspect-* (aspect-video → var(--aspect-video))
|
|
2335
2405
|
aspect: { video: "16 / 9" }
|
|
2336
2406
|
};
|
|
2407
|
+
const customUtilityName = /^[A-Za-z_][A-Za-z0-9_-]*$/;
|
|
2408
|
+
const customUtilityProp = /^(--[A-Za-z0-9_-]+|-?[A-Za-z][A-Za-z0-9-]*)$/;
|
|
2409
|
+
function validateCustomUtility(name, decls) {
|
|
2410
|
+
if (typeof name !== "string" || !customUtilityName.test(name)) return null;
|
|
2411
|
+
if (!decls || typeof decls !== "object" || Array.isArray(decls)) return null;
|
|
2412
|
+
const out = [];
|
|
2413
|
+
for (const [prop, raw] of Object.entries(decls)) {
|
|
2414
|
+
if (typeof raw !== "string" && typeof raw !== "number") return null;
|
|
2415
|
+
const value = String(raw).trim();
|
|
2416
|
+
if (!customUtilityProp.test(prop) || !value || !isStructureSafeValue(value) || hasCommentDelimiter(value)) return null;
|
|
2417
|
+
out.push([prop, value]);
|
|
2418
|
+
}
|
|
2419
|
+
return out.length ? out : null;
|
|
2420
|
+
}
|
|
2421
|
+
function registerCustomUtilities(ctx, utilities) {
|
|
2422
|
+
if (!utilities || typeof utilities !== "object" || Array.isArray(utilities)) return;
|
|
2423
|
+
const list = getUtility(ctx);
|
|
2424
|
+
const builtins = [...list];
|
|
2425
|
+
const before = list.length;
|
|
2426
|
+
for (const [name, decls] of Object.entries(utilities)) {
|
|
2427
|
+
const safe = validateCustomUtility(name, decls);
|
|
2428
|
+
if (!safe) {
|
|
2429
|
+
debugWarn(`[BAROCSS] Ignoring invalid custom utility "${name}"`);
|
|
2430
|
+
continue;
|
|
2431
|
+
}
|
|
2432
|
+
const shadowed = builtins.filter((u) => u.match(name));
|
|
2433
|
+
registerUtility({
|
|
2434
|
+
name,
|
|
2435
|
+
category: "custom",
|
|
2436
|
+
match: (className) => className === name,
|
|
2437
|
+
handler: (value, c, token) => {
|
|
2438
|
+
let base = [];
|
|
2439
|
+
for (const reg of shadowed) {
|
|
2440
|
+
base = reg.handler(value, c, token, reg) || [];
|
|
2441
|
+
if (base.length > 0) break;
|
|
2442
|
+
}
|
|
2443
|
+
return [...base, ...safe.map(([prop, v]) => decl(prop, v))];
|
|
2444
|
+
}
|
|
2445
|
+
}, ctx);
|
|
2446
|
+
}
|
|
2447
|
+
if (list.length > before) list.unshift(...list.splice(before));
|
|
2448
|
+
}
|
|
2337
2449
|
const preflightMinimalCSS = `
|
|
2338
2450
|
/* BaroCSS Preflight - Minimal Reset */
|
|
2339
2451
|
/* ================================= */
|
|
@@ -3216,9 +3328,7 @@ function resolveTheme(config) {
|
|
|
3216
3328
|
}
|
|
3217
3329
|
function themeToCssVars(theme) {
|
|
3218
3330
|
const vars = themeToCssVarsAll(theme);
|
|
3219
|
-
const result = toCssVarsBlock(vars
|
|
3220
|
-
${keyframesToCss(theme.keyframes || {})}
|
|
3221
|
-
`);
|
|
3331
|
+
const result = toCssVarsBlock(vars);
|
|
3222
3332
|
return result;
|
|
3223
3333
|
}
|
|
3224
3334
|
function createContext(configObj) {
|
|
@@ -3272,6 +3382,7 @@ function createContext(configObj) {
|
|
|
3272
3382
|
}
|
|
3273
3383
|
};
|
|
3274
3384
|
initializeContextState(ctx, getUtility(), getModifier());
|
|
3385
|
+
registerCustomUtilities(ctx, configObj.utilities);
|
|
3275
3386
|
return ctx;
|
|
3276
3387
|
}
|
|
3277
3388
|
function parseFraction(input) {
|
|
@@ -3682,6 +3793,7 @@ staticUtility("snap-proximity", [["--baro-scroll-snap-strictness", "proximity"]]
|
|
|
3682
3793
|
].forEach(([name, prop]) => {
|
|
3683
3794
|
functionalUtility({
|
|
3684
3795
|
name: `scroll-${name}`,
|
|
3796
|
+
spacingKeys: true,
|
|
3685
3797
|
prop,
|
|
3686
3798
|
supportsArbitrary: true,
|
|
3687
3799
|
supportsCustomProperty: true,
|
|
@@ -3709,6 +3821,7 @@ staticUtility("snap-proximity", [["--baro-scroll-snap-strictness", "proximity"]]
|
|
|
3709
3821
|
].forEach(([name, prop]) => {
|
|
3710
3822
|
functionalUtility({
|
|
3711
3823
|
name: `scroll-${name}`,
|
|
3824
|
+
spacingKeys: true,
|
|
3712
3825
|
prop,
|
|
3713
3826
|
supportsArbitrary: true,
|
|
3714
3827
|
supportsCustomProperty: true,
|
|
@@ -3867,10 +3980,12 @@ staticUtility("animate-bounce", [["animation", "var(--animate-bounce)"]], { cate
|
|
|
3867
3980
|
staticUtility("animate-none", [["animation", "none"]], { category: "transitions" });
|
|
3868
3981
|
functionalUtility({
|
|
3869
3982
|
name: "animate",
|
|
3870
|
-
|
|
3983
|
+
// #274: theme.animations (and Tailwind's theme.animation) names, e.g. theme.extend.animation.wiggle.
|
|
3984
|
+
themeKeys: ["animations", "animation"],
|
|
3871
3985
|
supportsArbitrary: true,
|
|
3872
3986
|
supportsCustomProperty: true,
|
|
3873
|
-
handle: (value, ctx, token) => {
|
|
3987
|
+
handle: (value, ctx, token, extra) => {
|
|
3988
|
+
if (extra?.realThemeValue) return [decl("animation", `var(--animate-${extra.realThemeValue})`)];
|
|
3874
3989
|
if (token.customProperty) {
|
|
3875
3990
|
return [decl("animation", `var(${value})`)];
|
|
3876
3991
|
}
|
|
@@ -5242,6 +5357,7 @@ staticUtility("sticky", [["position", "sticky"]], { category: "layout" });
|
|
|
5242
5357
|
staticUtility(`-${name}-px`, [[prop, "-1px"]], { category: "layout" });
|
|
5243
5358
|
functionalUtility({
|
|
5244
5359
|
name,
|
|
5360
|
+
spacingKeys: true,
|
|
5245
5361
|
prop,
|
|
5246
5362
|
supportsNegative: true,
|
|
5247
5363
|
supportsFraction: true,
|
|
@@ -5272,6 +5388,7 @@ staticUtility("invisible", [["visibility", "hidden"]], { category: "layout" });
|
|
|
5272
5388
|
staticUtility("collapse", [["visibility", "collapse"]], { category: "layout" });
|
|
5273
5389
|
functionalUtility({
|
|
5274
5390
|
name: "gap-x",
|
|
5391
|
+
spacingKeys: true,
|
|
5275
5392
|
prop: "column-gap",
|
|
5276
5393
|
supportsArbitrary: true,
|
|
5277
5394
|
// gap-x-[10vw]
|
|
@@ -5288,6 +5405,7 @@ functionalUtility({
|
|
|
5288
5405
|
});
|
|
5289
5406
|
functionalUtility({
|
|
5290
5407
|
name: "gap-y",
|
|
5408
|
+
spacingKeys: true,
|
|
5291
5409
|
prop: "row-gap",
|
|
5292
5410
|
supportsArbitrary: true,
|
|
5293
5411
|
// gap-y-[10vw]
|
|
@@ -5304,6 +5422,7 @@ functionalUtility({
|
|
|
5304
5422
|
});
|
|
5305
5423
|
functionalUtility({
|
|
5306
5424
|
name: "gap",
|
|
5425
|
+
spacingKeys: true,
|
|
5307
5426
|
prop: "gap",
|
|
5308
5427
|
supportsArbitrary: true,
|
|
5309
5428
|
// gap-[10vw]
|
|
@@ -5821,6 +5940,7 @@ functionalUtility({
|
|
|
5821
5940
|
].forEach(([name, prop]) => {
|
|
5822
5941
|
staticUtility(`${name}-px`, [[prop, "1px"]], { category: "spacing" });
|
|
5823
5942
|
functionalUtility({
|
|
5943
|
+
spacingKeys: true,
|
|
5824
5944
|
name,
|
|
5825
5945
|
prop,
|
|
5826
5946
|
supportsArbitrary: true,
|
|
@@ -5845,6 +5965,7 @@ functionalUtility({
|
|
|
5845
5965
|
staticUtility(`${name}-px`, [[prop, "1px"]], { category: "spacing" });
|
|
5846
5966
|
staticUtility(`-${name}-px`, [[prop, "-1px"]], { category: "spacing" });
|
|
5847
5967
|
functionalUtility({
|
|
5968
|
+
spacingKeys: true,
|
|
5848
5969
|
name,
|
|
5849
5970
|
prop,
|
|
5850
5971
|
supportsNegative: true,
|
|
@@ -5875,6 +5996,7 @@ const SPACE_SELECTOR = ":where(& > :not(:last-child))";
|
|
|
5875
5996
|
() => rule(SPACE_SELECTOR, [decl(rev, "1")])
|
|
5876
5997
|
], { category: "spacing" });
|
|
5877
5998
|
functionalUtility({
|
|
5999
|
+
spacingKeys: true,
|
|
5878
6000
|
name,
|
|
5879
6001
|
supportsNegative: true,
|
|
5880
6002
|
supportsArbitrary: true,
|
|
@@ -5924,6 +6046,7 @@ const SPACE_SELECTOR = ":where(& > :not(:last-child))";
|
|
|
5924
6046
|
staticUtility(name, [["width", value]]);
|
|
5925
6047
|
});
|
|
5926
6048
|
functionalUtility({
|
|
6049
|
+
spacingKeys: true,
|
|
5927
6050
|
name: "w",
|
|
5928
6051
|
prop: "width",
|
|
5929
6052
|
supportsArbitrary: true,
|
|
@@ -5956,6 +6079,7 @@ functionalUtility({
|
|
|
5956
6079
|
staticUtility(name, [["width", w], ["height", h]]);
|
|
5957
6080
|
});
|
|
5958
6081
|
functionalUtility({
|
|
6082
|
+
spacingKeys: true,
|
|
5959
6083
|
name: "size",
|
|
5960
6084
|
supportsArbitrary: true,
|
|
5961
6085
|
supportsCustomProperty: true,
|
|
@@ -5995,6 +6119,7 @@ functionalUtility({
|
|
|
5995
6119
|
staticUtility(name, [["height", value]]);
|
|
5996
6120
|
});
|
|
5997
6121
|
functionalUtility({
|
|
6122
|
+
spacingKeys: true,
|
|
5998
6123
|
name: "h",
|
|
5999
6124
|
prop: "height",
|
|
6000
6125
|
supportsArbitrary: true,
|
|
@@ -6025,6 +6150,7 @@ functionalUtility({
|
|
|
6025
6150
|
staticUtility(name, [["min-height", value]], { category: "sizing" });
|
|
6026
6151
|
});
|
|
6027
6152
|
functionalUtility({
|
|
6153
|
+
spacingKeys: true,
|
|
6028
6154
|
name: "min-h",
|
|
6029
6155
|
prop: "min-height",
|
|
6030
6156
|
supportsArbitrary: true,
|
|
@@ -6055,6 +6181,7 @@ functionalUtility({
|
|
|
6055
6181
|
staticUtility(name, [["max-height", value]], { category: "sizing" });
|
|
6056
6182
|
});
|
|
6057
6183
|
functionalUtility({
|
|
6184
|
+
spacingKeys: true,
|
|
6058
6185
|
name: "max-h",
|
|
6059
6186
|
prop: "max-height",
|
|
6060
6187
|
supportsArbitrary: true,
|
|
@@ -6101,6 +6228,7 @@ functionalUtility({
|
|
|
6101
6228
|
staticUtility(name, [["min-width", value]], { category: "sizing" });
|
|
6102
6229
|
});
|
|
6103
6230
|
functionalUtility({
|
|
6231
|
+
spacingKeys: true,
|
|
6104
6232
|
name: "min-w",
|
|
6105
6233
|
prop: "min-width",
|
|
6106
6234
|
supportsArbitrary: true,
|
|
@@ -6136,6 +6264,7 @@ functionalUtility({
|
|
|
6136
6264
|
staticUtility(name, [["max-width", value]], { category: "sizing" });
|
|
6137
6265
|
});
|
|
6138
6266
|
functionalUtility({
|
|
6267
|
+
spacingKeys: true,
|
|
6139
6268
|
name: "max-w",
|
|
6140
6269
|
prop: "max-width",
|
|
6141
6270
|
supportsArbitrary: true,
|
|
@@ -8795,6 +8924,41 @@ class StylePartitionManager {
|
|
|
8795
8924
|
}
|
|
8796
8925
|
return { success, failed };
|
|
8797
8926
|
}
|
|
8927
|
+
/**
|
|
8928
|
+
* Remove one generated rule (#269 GC). Keeps `styles`, the #254 `keys` and the
|
|
8929
|
+
* sheet's cssRules parallel: one deleteRule at the rule's index, or a text
|
|
8930
|
+
* rebuild when the sheet isn't solely ours / has no CSSOM. Returns whether
|
|
8931
|
+
* the rule was found.
|
|
8932
|
+
*/
|
|
8933
|
+
removeRule(rule2, category) {
|
|
8934
|
+
let partition;
|
|
8935
|
+
if (category) {
|
|
8936
|
+
if (this.classToCategoryPartitionMap.get(rule2) !== category) return false;
|
|
8937
|
+
partition = this.categoryPartitions.get(category);
|
|
8938
|
+
} else {
|
|
8939
|
+
const partitionIndex = this.classToPartitionMap.get(rule2);
|
|
8940
|
+
partition = partitionIndex === void 0 ? void 0 : this.partitions[partitionIndex];
|
|
8941
|
+
}
|
|
8942
|
+
if (!partition) return false;
|
|
8943
|
+
const index = partition.styles.indexOf(rule2);
|
|
8944
|
+
if (index === -1) return false;
|
|
8945
|
+
const sheet = partition.styleElement.sheet;
|
|
8946
|
+
const inSync = !!sheet && sheet.cssRules.length === partition.styles.length;
|
|
8947
|
+
partition.styles.splice(index, 1);
|
|
8948
|
+
partition.keys?.splice(index, 1);
|
|
8949
|
+
if (inSync && sheet) {
|
|
8950
|
+
sheet.deleteRule(index);
|
|
8951
|
+
} else {
|
|
8952
|
+
partition.styleElement.textContent = partition.styles.length ? partition.styles.join("\n") + "\n" : "";
|
|
8953
|
+
}
|
|
8954
|
+
if (category) this.classToCategoryPartitionMap.delete(rule2);
|
|
8955
|
+
else this.classToPartitionMap.delete(rule2);
|
|
8956
|
+
return true;
|
|
8957
|
+
}
|
|
8958
|
+
/** Number of generated (non-root, non-preflight) rules currently held. */
|
|
8959
|
+
get ruleCount() {
|
|
8960
|
+
return this.classToPartitionMap.size + this.classToCategoryPartitionMap.size;
|
|
8961
|
+
}
|
|
8798
8962
|
/**
|
|
8799
8963
|
* 특정 규칙이 어느 파티션에 있는지 찾기
|
|
8800
8964
|
*/
|
|
@@ -8860,10 +9024,14 @@ class ChangeDetector {
|
|
|
8860
9024
|
*/
|
|
8861
9025
|
constructor(incrementalParser, BrowserRuntime2, getCategory = (cls) => parseClassName(cls).utility?.category) {
|
|
8862
9026
|
this.observer = null;
|
|
9027
|
+
this.gc = null;
|
|
8863
9028
|
this.incrementalParser = incrementalParser;
|
|
8864
9029
|
this.BrowserRuntime = BrowserRuntime2;
|
|
8865
9030
|
this.getCategory = getCategory;
|
|
8866
9031
|
}
|
|
9032
|
+
setGc(gc) {
|
|
9033
|
+
this.gc = gc;
|
|
9034
|
+
}
|
|
8867
9035
|
setParser(parser) {
|
|
8868
9036
|
this.incrementalParser = parser;
|
|
8869
9037
|
}
|
|
@@ -8890,9 +9058,19 @@ class ChangeDetector {
|
|
|
8890
9058
|
if (this.observer) {
|
|
8891
9059
|
this.observer.disconnect();
|
|
8892
9060
|
}
|
|
9061
|
+
this.gc?.setRoot(root);
|
|
8893
9062
|
this.observer = new MutationObserver((mutations) => {
|
|
8894
9063
|
const newClasses = /* @__PURE__ */ new Set();
|
|
9064
|
+
const gc = this.gc;
|
|
8895
9065
|
mutations.forEach((mutation) => {
|
|
9066
|
+
if (gc) {
|
|
9067
|
+
if (mutation.type === "attributes") {
|
|
9068
|
+
gc.reconcile(mutation.target);
|
|
9069
|
+
} else if (mutation.type === "childList") {
|
|
9070
|
+
mutation.removedNodes.forEach((node) => gc.reconcileTree(node));
|
|
9071
|
+
mutation.addedNodes.forEach((node) => gc.reconcileTree(node));
|
|
9072
|
+
}
|
|
9073
|
+
}
|
|
8896
9074
|
if (mutation.type === "attributes" && mutation.attributeName === "class" && root.contains(mutation.target)) {
|
|
8897
9075
|
const target = mutation.target;
|
|
8898
9076
|
if (target.className) {
|
|
@@ -8923,6 +9101,7 @@ class ChangeDetector {
|
|
|
8923
9101
|
} else {
|
|
8924
9102
|
this.BrowserRuntime?.applyParseResults([]);
|
|
8925
9103
|
}
|
|
9104
|
+
gc?.afterBatch();
|
|
8926
9105
|
});
|
|
8927
9106
|
this.observer.observe(root, {
|
|
8928
9107
|
attributes: true,
|
|
@@ -9010,7 +9189,7 @@ class ChangeDetector {
|
|
|
9010
9189
|
function unescapeCssIdent(s) {
|
|
9011
9190
|
return s.replace(/\\([0-9a-fA-F]{1,6})\s?|\\(.)/g, (_m, hex, ch) => hex ? String.fromCodePoint(parseInt(hex, 16)) : ch);
|
|
9012
9191
|
}
|
|
9013
|
-
const LEADING_CLASS = /^\s
|
|
9192
|
+
const LEADING_CLASS = /^\s*(?::(?:where|is)\(\s*)?\.((?:\\[0-9a-fA-F]{1,6}\s?|\\.|[\w-]|[^\x00-\x7F])+)/;
|
|
9014
9193
|
function splitTopLevel(sel) {
|
|
9015
9194
|
const parts = [];
|
|
9016
9195
|
let depth = 0, start = 0;
|
|
@@ -9027,6 +9206,17 @@ function splitTopLevel(sel) {
|
|
|
9027
9206
|
parts.push(sel.slice(start));
|
|
9028
9207
|
return parts;
|
|
9029
9208
|
}
|
|
9209
|
+
function collectKeyframeNames(rules, out = /* @__PURE__ */ new Set()) {
|
|
9210
|
+
for (const rule2 of Array.from(rules)) {
|
|
9211
|
+
if (rule2.type === 7 && typeof rule2.name === "string") {
|
|
9212
|
+
out.add(rule2.name);
|
|
9213
|
+
continue;
|
|
9214
|
+
}
|
|
9215
|
+
const inner = rule2.cssRules;
|
|
9216
|
+
if (inner && inner.length) collectKeyframeNames(inner, out);
|
|
9217
|
+
}
|
|
9218
|
+
return out;
|
|
9219
|
+
}
|
|
9030
9220
|
function collectLeadingClasses(rules, out = /* @__PURE__ */ new Set()) {
|
|
9031
9221
|
for (const rule2 of Array.from(rules)) {
|
|
9032
9222
|
const selectorText = rule2.selectorText;
|
|
@@ -9041,6 +9231,116 @@ function collectLeadingClasses(rules, out = /* @__PURE__ */ new Set()) {
|
|
|
9041
9231
|
}
|
|
9042
9232
|
return out;
|
|
9043
9233
|
}
|
|
9234
|
+
class ClassGc {
|
|
9235
|
+
constructor(host, graceMs, maxRules, now = () => Date.now()) {
|
|
9236
|
+
this.host = host;
|
|
9237
|
+
this.graceMs = graceMs;
|
|
9238
|
+
this.maxRules = maxRules;
|
|
9239
|
+
this.now = now;
|
|
9240
|
+
this.counts = /* @__PURE__ */ new Map();
|
|
9241
|
+
this.counted = /* @__PURE__ */ new WeakMap();
|
|
9242
|
+
this.candidates = /* @__PURE__ */ new Map();
|
|
9243
|
+
this.timer = null;
|
|
9244
|
+
this.root = null;
|
|
9245
|
+
}
|
|
9246
|
+
/** Start counting for a new root: count every element currently inside it. */
|
|
9247
|
+
setRoot(root) {
|
|
9248
|
+
this.counts.clear();
|
|
9249
|
+
this.counted = /* @__PURE__ */ new WeakMap();
|
|
9250
|
+
this.candidates.clear();
|
|
9251
|
+
this.cancel();
|
|
9252
|
+
this.root = root;
|
|
9253
|
+
this.reconcileTree(root);
|
|
9254
|
+
}
|
|
9255
|
+
count(cls) {
|
|
9256
|
+
return this.counts.get(cls) ?? 0;
|
|
9257
|
+
}
|
|
9258
|
+
/** Re-count `el` and (optionally) all its descendants from their current state. */
|
|
9259
|
+
reconcileTree(node) {
|
|
9260
|
+
if (node.nodeType !== 1) return;
|
|
9261
|
+
const el = node;
|
|
9262
|
+
this.reconcile(el);
|
|
9263
|
+
el.querySelectorAll("[class]").forEach((child) => this.reconcile(child));
|
|
9264
|
+
}
|
|
9265
|
+
reconcile(el) {
|
|
9266
|
+
const root = this.root;
|
|
9267
|
+
const live = !!root && root.contains(el);
|
|
9268
|
+
const next = live ? Array.from(new Set(normalizeClassNameList(el.getAttribute("class")))) : [];
|
|
9269
|
+
const prev = this.counted.get(el);
|
|
9270
|
+
if (!prev && next.length === 0) return;
|
|
9271
|
+
const prevSet = new Set(prev ?? []);
|
|
9272
|
+
const nextSet = new Set(next);
|
|
9273
|
+
for (const cls of nextSet) {
|
|
9274
|
+
if (prevSet.has(cls)) continue;
|
|
9275
|
+
const c = (this.counts.get(cls) ?? 0) + 1;
|
|
9276
|
+
this.counts.set(cls, c);
|
|
9277
|
+
this.candidates.delete(cls);
|
|
9278
|
+
}
|
|
9279
|
+
for (const cls of prevSet) {
|
|
9280
|
+
if (nextSet.has(cls)) continue;
|
|
9281
|
+
const c = (this.counts.get(cls) ?? 0) - 1;
|
|
9282
|
+
if (c > 0) {
|
|
9283
|
+
this.counts.set(cls, c);
|
|
9284
|
+
} else {
|
|
9285
|
+
this.counts.delete(cls);
|
|
9286
|
+
this.candidates.delete(cls);
|
|
9287
|
+
this.candidates.set(cls, this.now());
|
|
9288
|
+
}
|
|
9289
|
+
}
|
|
9290
|
+
if (next.length) this.counted.set(el, next);
|
|
9291
|
+
else this.counted.delete(el);
|
|
9292
|
+
}
|
|
9293
|
+
/** Call after a mutation batch has been counted and its classes inserted. */
|
|
9294
|
+
afterBatch() {
|
|
9295
|
+
if (this.candidates.size === 0) return;
|
|
9296
|
+
if (this.host.cachedCount() > this.maxRules) {
|
|
9297
|
+
this.schedule(0);
|
|
9298
|
+
} else {
|
|
9299
|
+
this.schedule(this.graceMs);
|
|
9300
|
+
}
|
|
9301
|
+
}
|
|
9302
|
+
schedule(delay) {
|
|
9303
|
+
if (this.timer !== null) {
|
|
9304
|
+
if (delay > 0) return;
|
|
9305
|
+
clearTimeout(this.timer);
|
|
9306
|
+
}
|
|
9307
|
+
this.timer = setTimeout(() => {
|
|
9308
|
+
this.timer = null;
|
|
9309
|
+
this.sweep();
|
|
9310
|
+
}, delay);
|
|
9311
|
+
}
|
|
9312
|
+
/** Reclaim candidates whose grace period elapsed (plus LRU overflow). Public for tests. */
|
|
9313
|
+
sweep() {
|
|
9314
|
+
const now = this.now();
|
|
9315
|
+
const overflow = Math.max(0, this.host.cachedCount() - this.maxRules);
|
|
9316
|
+
const doomed = [];
|
|
9317
|
+
let evicted = 0;
|
|
9318
|
+
for (const [cls, since] of this.candidates) {
|
|
9319
|
+
const expired = now - since >= this.graceMs;
|
|
9320
|
+
if (!expired && evicted >= overflow) continue;
|
|
9321
|
+
this.candidates.delete(cls);
|
|
9322
|
+
if (this.count(cls) > 0 || this.inDom(cls) || this.host.isPermanent(cls)) continue;
|
|
9323
|
+
doomed.push(cls);
|
|
9324
|
+
if (!expired) evicted++;
|
|
9325
|
+
}
|
|
9326
|
+
if (doomed.length) this.host.reclaim(doomed);
|
|
9327
|
+
if (this.candidates.size) this.schedule(this.graceMs);
|
|
9328
|
+
}
|
|
9329
|
+
inDom(cls) {
|
|
9330
|
+
const root = this.root;
|
|
9331
|
+
if (!root) return false;
|
|
9332
|
+
const doc = root.ownerDocument ?? document;
|
|
9333
|
+
return root.classList.contains(cls) || doc.documentElement.classList.contains(cls) || doc.getElementsByClassName(cls).length > 0;
|
|
9334
|
+
}
|
|
9335
|
+
cancel() {
|
|
9336
|
+
if (this.timer !== null) clearTimeout(this.timer);
|
|
9337
|
+
this.timer = null;
|
|
9338
|
+
}
|
|
9339
|
+
stats() {
|
|
9340
|
+
return { trackedClasses: this.counts.size, candidates: this.candidates.size };
|
|
9341
|
+
}
|
|
9342
|
+
}
|
|
9343
|
+
const SSR_STYLE_SELECTOR = "style[data-barocss-ssr]";
|
|
9044
9344
|
const LAYER_ORDER = "@layer theme, base, components, utilities;";
|
|
9045
9345
|
class BrowserRuntime {
|
|
9046
9346
|
constructor(options = {}) {
|
|
@@ -9048,7 +9348,14 @@ class BrowserRuntime {
|
|
|
9048
9348
|
this.rootCache = /* @__PURE__ */ new Set();
|
|
9049
9349
|
this.isDestroyed = false;
|
|
9050
9350
|
this.existing = null;
|
|
9351
|
+
this.existingKeyframes = /* @__PURE__ */ new Set();
|
|
9051
9352
|
this.existingSheetCount = -1;
|
|
9353
|
+
this.pinned = /* @__PURE__ */ new Set();
|
|
9354
|
+
this.gc = null;
|
|
9355
|
+
this.reclaimedCount = 0;
|
|
9356
|
+
this.ssrRules = [];
|
|
9357
|
+
this.ssrClasses = /* @__PURE__ */ new Set();
|
|
9358
|
+
this.observedOnce = false;
|
|
9052
9359
|
this.getCategory = (cls) => parseClassName(cls, this.context).utility?.category;
|
|
9053
9360
|
const defaultConfig = {};
|
|
9054
9361
|
this.options = {
|
|
@@ -9056,12 +9363,23 @@ class BrowserRuntime {
|
|
|
9056
9363
|
styleId: options.styleId || "barocss-runtime",
|
|
9057
9364
|
insertionPoint: options.insertionPoint || "head",
|
|
9058
9365
|
maxRulesPerPartition: options.maxRulesPerPartition || 50,
|
|
9059
|
-
skipExisting: options.skipExisting ?? false
|
|
9366
|
+
skipExisting: options.skipExisting ?? false,
|
|
9367
|
+
gc: options.gc ?? true,
|
|
9368
|
+
gcGraceMs: options.gcGraceMs ?? 3e3,
|
|
9369
|
+
maxRules: options.maxRules ?? Infinity
|
|
9060
9370
|
};
|
|
9061
9371
|
this.context = createContext(this.options.config);
|
|
9062
9372
|
this.incrementalParser = new IncrementalParser(this.context);
|
|
9063
9373
|
this.changeDetector = new ChangeDetector(this.incrementalParser, this, this.getCategory);
|
|
9064
9374
|
this.stylePartitionManager = new StylePartitionManager(this.getInsertionPoint(), this.options.maxRulesPerPartition, `${this.options.styleId}-partition`, this.getCategory);
|
|
9375
|
+
if (this.options.gc) {
|
|
9376
|
+
this.gc = new ClassGc({
|
|
9377
|
+
reclaim: (classes) => this.reclaim(classes),
|
|
9378
|
+
isPermanent: (cls) => this.isPermanent(cls),
|
|
9379
|
+
cachedCount: () => this.cache.size
|
|
9380
|
+
}, this.options.gcGraceMs, this.options.maxRules);
|
|
9381
|
+
this.changeDetector.setGc(this.gc);
|
|
9382
|
+
}
|
|
9065
9383
|
this.init();
|
|
9066
9384
|
}
|
|
9067
9385
|
// Debugging and logging helpers
|
|
@@ -9076,6 +9394,44 @@ class BrowserRuntime {
|
|
|
9076
9394
|
console.log("[BrowserRuntime] init");
|
|
9077
9395
|
this.injectPreflightCSS();
|
|
9078
9396
|
this.ensureCssVars();
|
|
9397
|
+
this.adoptSsrSheets();
|
|
9398
|
+
}
|
|
9399
|
+
/**
|
|
9400
|
+
* #268: adopt the class rules of server-rendered `<style data-barocss-ssr>` sheets in <head>, at startup
|
|
9401
|
+
* (constructor and the first observe()). Each rule moves
|
|
9402
|
+
* (same task, so no paint in between) into the partition its class would get if generated here, at
|
|
9403
|
+
* its #254 sorted position, so a later client `sm:` rule lands before a server `lg:` rule. Its classes
|
|
9404
|
+
* are never regenerated and never reclaimed. `:root`, `@property` and `@keyframes` stay in the sheet.
|
|
9405
|
+
*/
|
|
9406
|
+
adoptSsrSheets() {
|
|
9407
|
+
if (typeof document === "undefined") return;
|
|
9408
|
+
const adopted = [];
|
|
9409
|
+
if (!document.head) return;
|
|
9410
|
+
for (const el of Array.from(document.head.querySelectorAll(`${SSR_STYLE_SELECTOR}:not([data-barocss-adopted])`))) {
|
|
9411
|
+
const sheet = el.sheet;
|
|
9412
|
+
if (!sheet) continue;
|
|
9413
|
+
el.setAttribute("data-barocss-adopted", "");
|
|
9414
|
+
const moved = [];
|
|
9415
|
+
for (let i = sheet.cssRules.length - 1; i >= 0; i--) {
|
|
9416
|
+
const rule2 = sheet.cssRules[i];
|
|
9417
|
+
const classes = collectLeadingClasses([rule2]);
|
|
9418
|
+
if (classes.size === 0) continue;
|
|
9419
|
+
classes.forEach((cls) => this.ssrClasses.add(cls));
|
|
9420
|
+
moved.unshift({ css: rule2.cssText, cls: classes.values().next().value });
|
|
9421
|
+
sheet.deleteRule(i);
|
|
9422
|
+
}
|
|
9423
|
+
adopted.push(...moved);
|
|
9424
|
+
}
|
|
9425
|
+
if (adopted.length === 0) return;
|
|
9426
|
+
this.ssrRules.push(...adopted);
|
|
9427
|
+
this.insertSsrRules(adopted);
|
|
9428
|
+
}
|
|
9429
|
+
insertSsrRules(rules) {
|
|
9430
|
+
for (const { css, cls } of rules) {
|
|
9431
|
+
const category = this.getCategory(cls);
|
|
9432
|
+
if (category) this.stylePartitionManager.addCategoryRule(css, category);
|
|
9433
|
+
else this.stylePartitionManager.addRule(css);
|
|
9434
|
+
}
|
|
9079
9435
|
}
|
|
9080
9436
|
injectPreflightCSS() {
|
|
9081
9437
|
const level = this.options.config.preflight ?? true;
|
|
@@ -9113,7 +9469,8 @@ ${preflightCSS}
|
|
|
9113
9469
|
*/
|
|
9114
9470
|
addClass(classes) {
|
|
9115
9471
|
if (this.isDestroyed) return;
|
|
9116
|
-
const classList = this.normalizeClasses(classes);
|
|
9472
|
+
const classList = this.normalizeClasses(classes).filter(Boolean);
|
|
9473
|
+
classList.forEach((cls) => this.pinned.add(cls));
|
|
9117
9474
|
this.processClasses(classList);
|
|
9118
9475
|
}
|
|
9119
9476
|
/**
|
|
@@ -9142,6 +9499,7 @@ ${preflightCSS}
|
|
|
9142
9499
|
results = [...existingResults, ...results];
|
|
9143
9500
|
results.forEach((result) => this.incrementalParser.markProcessed(result.cls));
|
|
9144
9501
|
}
|
|
9502
|
+
if (this.ssrClasses.size > 0) results = results.filter((result) => !this.ssrClasses.has(result.cls));
|
|
9145
9503
|
if (this.options.skipExisting && results.length > 0 && typeof document !== "undefined") {
|
|
9146
9504
|
const existing = this.getExistingClasses();
|
|
9147
9505
|
results = results.filter((result) => !existing.has(result.cls));
|
|
@@ -9149,6 +9507,7 @@ ${preflightCSS}
|
|
|
9149
9507
|
if (results.length === 0) return;
|
|
9150
9508
|
const cssRules = [];
|
|
9151
9509
|
const rootCssRules = [];
|
|
9510
|
+
const pageKeyframes = this.options.skipExisting && typeof document !== "undefined" ? (this.getExistingClasses(), this.existingKeyframes) : null;
|
|
9152
9511
|
for (const result of results) {
|
|
9153
9512
|
if (result.css && Array.isArray(result.cssList)) {
|
|
9154
9513
|
cssRules.push(result);
|
|
@@ -9156,6 +9515,10 @@ ${preflightCSS}
|
|
|
9156
9515
|
}
|
|
9157
9516
|
if (result.rootCss && Array.isArray(result.rootCssList)) {
|
|
9158
9517
|
for (const rootCss of result.rootCssList) {
|
|
9518
|
+
if (pageKeyframes?.size) {
|
|
9519
|
+
const kf = /^\s*@keyframes\s+([^\s{]+)/.exec(rootCss)?.[1];
|
|
9520
|
+
if (kf && pageKeyframes.has(kf)) continue;
|
|
9521
|
+
}
|
|
9159
9522
|
if (!this.rootCache.has(rootCss)) {
|
|
9160
9523
|
this.rootCache.add(rootCss);
|
|
9161
9524
|
rootCssRules.push(rootCss);
|
|
@@ -9174,14 +9537,46 @@ ${preflightCSS}
|
|
|
9174
9537
|
rootCssCount: rootCssRules.length
|
|
9175
9538
|
});
|
|
9176
9539
|
}
|
|
9540
|
+
/** #269: a class that must never be reclaimed. */
|
|
9541
|
+
isPermanent(cls) {
|
|
9542
|
+
if (this.pinned.has(cls) || this.ssrClasses.has(cls)) return true;
|
|
9543
|
+
if (typeof document === "undefined") return true;
|
|
9544
|
+
return this.getExistingClasses().has(cls);
|
|
9545
|
+
}
|
|
9546
|
+
/**
|
|
9547
|
+
* #269: delete the generated rules of classes no live element uses. Root/@property/@keyframes rules stay
|
|
9548
|
+
* (they are shared and harmless); a rule text another cached class still emits is kept.
|
|
9549
|
+
*/
|
|
9550
|
+
reclaim(classes) {
|
|
9551
|
+
if (this.isDestroyed) return;
|
|
9552
|
+
const victims = classes.filter((cls) => this.cache.has(cls));
|
|
9553
|
+
if (victims.length === 0) return;
|
|
9554
|
+
const results = victims.map((cls) => this.cache.get(cls));
|
|
9555
|
+
victims.forEach((cls) => {
|
|
9556
|
+
this.cache.delete(cls);
|
|
9557
|
+
this.incrementalParser.unmarkProcessed(cls);
|
|
9558
|
+
});
|
|
9559
|
+
const stillUsed = /* @__PURE__ */ new Set();
|
|
9560
|
+
for (const result of this.cache.values()) result.cssList.forEach((css) => stillUsed.add(css));
|
|
9561
|
+
for (const result of results) {
|
|
9562
|
+
const category = this.getCategory(result.cls);
|
|
9563
|
+
for (const css of result.cssList) {
|
|
9564
|
+
if (!stillUsed.has(css)) this.stylePartitionManager.removeRule(css, category);
|
|
9565
|
+
}
|
|
9566
|
+
}
|
|
9567
|
+
this.reclaimedCount += victims.length;
|
|
9568
|
+
}
|
|
9177
9569
|
/** Class names defined by the page's own stylesheets (BaroCSS's sheets and cross-origin sheets excluded). */
|
|
9178
9570
|
getExistingClasses() {
|
|
9571
|
+
const own = new Set(Array.from(document.querySelectorAll("style[data-barocss]"), (s) => s.sheet));
|
|
9179
9572
|
const sheets = Array.from(document.styleSheets).filter((sheet) => {
|
|
9573
|
+
if (own.has(sheet)) return false;
|
|
9180
9574
|
const owner = sheet.ownerNode;
|
|
9181
9575
|
return !(owner && typeof owner.hasAttribute === "function" && (owner.hasAttribute("data-barocss") || (owner.id || "").startsWith(this.options.styleId)));
|
|
9182
9576
|
});
|
|
9183
9577
|
if (this.existing && sheets.length === this.existingSheetCount) return this.existing;
|
|
9184
9578
|
const out = /* @__PURE__ */ new Set();
|
|
9579
|
+
const keyframes2 = /* @__PURE__ */ new Set();
|
|
9185
9580
|
for (const sheet of sheets) {
|
|
9186
9581
|
let rules;
|
|
9187
9582
|
try {
|
|
@@ -9190,7 +9585,9 @@ ${preflightCSS}
|
|
|
9190
9585
|
continue;
|
|
9191
9586
|
}
|
|
9192
9587
|
collectLeadingClasses(rules, out);
|
|
9588
|
+
collectKeyframeNames(rules, keyframes2);
|
|
9193
9589
|
}
|
|
9590
|
+
this.existingKeyframes = keyframes2;
|
|
9194
9591
|
this.existing = out;
|
|
9195
9592
|
this.existingSheetCount = sheets.length;
|
|
9196
9593
|
return out;
|
|
@@ -9199,6 +9596,10 @@ ${preflightCSS}
|
|
|
9199
9596
|
* MutationObserver instance method to automatically call addClass when class attributes change in DOM
|
|
9200
9597
|
*/
|
|
9201
9598
|
observe(root = document.body, options) {
|
|
9599
|
+
if (!this.observedOnce) {
|
|
9600
|
+
this.observedOnce = true;
|
|
9601
|
+
this.adoptSsrSheets();
|
|
9602
|
+
}
|
|
9202
9603
|
return this.changeDetector.observe(root, options);
|
|
9203
9604
|
}
|
|
9204
9605
|
normalizeClasses(classes) {
|
|
@@ -9228,7 +9629,10 @@ ${preflightCSS}
|
|
|
9228
9629
|
return {
|
|
9229
9630
|
runtime: {
|
|
9230
9631
|
cachedClasses: this.cache.size,
|
|
9231
|
-
rootCacheSize: this.rootCache.size
|
|
9632
|
+
rootCacheSize: this.rootCache.size,
|
|
9633
|
+
ruleCount: this.stylePartitionManager.ruleCount,
|
|
9634
|
+
reclaimedClasses: this.reclaimedCount,
|
|
9635
|
+
gc: this.gc?.stats() ?? null
|
|
9232
9636
|
},
|
|
9233
9637
|
ast: incremental.cacheStats.ast,
|
|
9234
9638
|
incremental
|
|
@@ -9247,6 +9651,7 @@ ${preflightCSS}
|
|
|
9247
9651
|
this.stylePartitionManager = new StylePartitionManager(this.getInsertionPoint(), this.options.maxRulesPerPartition, `${this.options.styleId}-partition`, this.getCategory);
|
|
9248
9652
|
this.injectPreflightCSS();
|
|
9249
9653
|
this.ensureCssVars();
|
|
9654
|
+
this.insertSsrRules(this.ssrRules);
|
|
9250
9655
|
}
|
|
9251
9656
|
reset() {
|
|
9252
9657
|
if (this.isDestroyed) return;
|
|
@@ -9257,6 +9662,7 @@ ${preflightCSS}
|
|
|
9257
9662
|
this.stylePartitionManager = new StylePartitionManager(this.getInsertionPoint(), this.options.maxRulesPerPartition, `${this.options.styleId}-partition`, this.getCategory);
|
|
9258
9663
|
this.injectPreflightCSS();
|
|
9259
9664
|
this.ensureCssVars();
|
|
9665
|
+
this.insertSsrRules(this.ssrRules);
|
|
9260
9666
|
}
|
|
9261
9667
|
updateConfig(newConfig) {
|
|
9262
9668
|
if (this.isDestroyed) return;
|
|
@@ -9282,6 +9688,7 @@ ${preflightCSS}
|
|
|
9282
9688
|
destroy() {
|
|
9283
9689
|
if (this.isDestroyed) return;
|
|
9284
9690
|
this.changeDetector.disconnect();
|
|
9691
|
+
this.gc?.cancel();
|
|
9285
9692
|
this.stylePartitionManager.cleanup();
|
|
9286
9693
|
this.cache.clear();
|
|
9287
9694
|
this.rootCache.clear();
|
|
@@ -9398,6 +9805,7 @@ export {
|
|
|
9398
9805
|
BrowserRuntime,
|
|
9399
9806
|
ChangeDetector,
|
|
9400
9807
|
LAYER_ORDER,
|
|
9808
|
+
SSR_STYLE_SELECTOR,
|
|
9401
9809
|
StylePartitionManager,
|
|
9402
9810
|
baroBoot,
|
|
9403
9811
|
baroStart,
|