@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/dist/index.es.js CHANGED
@@ -1,38 +1,4 @@
1
- import { parseClassName as y, createContext as g, IncrementalParser as p, clearAstCache as x } from "@barocss/kit";
2
- const w = /^\s*@(media|container)\s+([^{]*)\{/, E = /prefers-color-scheme|\bprint\b|forced-colors|orientation/, S = /(?:min-width\s*:\s*|width\s*>=?\s*)([\d.]+)(px|rem|em)?/, A = /(?:max-width\s*:\s*|width\s*<=?\s*)([\d.]+)(px|rem|em)?/;
3
- function m(n, t) {
4
- const e = parseFloat(n);
5
- return t === "rem" || t === "em" ? e * 16 : e;
6
- }
7
- function M(n, t) {
8
- const e = n === "container", s = S.exec(t);
9
- if (s) return [e ? 4 : 2, m(s[1], s[2])];
10
- const i = A.exec(t);
11
- return i ? [e ? 3 : 1, -m(i[1], i[2])] : !e && E.test(t) ? [5, 0] : [0, 0];
12
- }
13
- function C(n) {
14
- const t = [];
15
- let e = n, s;
16
- for (; s = w.exec(e); ) {
17
- const [i, r] = M(s[1], s[2]);
18
- t.push(i, r), e = e.slice(s[0].length);
19
- }
20
- return t;
21
- }
22
- function P(n, t) {
23
- const e = Math.min(n.length, t.length);
24
- for (let s = 0; s < e; s++)
25
- if (n[s] !== t[s]) return n[s] - t[s];
26
- return n.length - t.length;
27
- }
28
- function v(n, t) {
29
- let e = 0, s = n.length;
30
- for (; e < s; ) {
31
- const i = e + s >> 1;
32
- P(n[i], t) <= 0 ? e = i + 1 : s = i;
33
- }
34
- return e;
35
- }
1
+ import { parseClassName as y, upperBound as b, ruleSortKey as m, compareKeys as S, createContext as p, IncrementalParser as C, clearAstCache as x } from "@barocss/kit";
36
2
  class d {
37
3
  constructor(t, e = 50, s = "barocss-style-partition-", i = (r) => y(r).utility?.category) {
38
4
  this.partitions = [], this.categoryPartitions = /* @__PURE__ */ new Map(), this.partitionCounter = 0, this.maxRulesPerPartition = 50, this.classToPartitionMap = /* @__PURE__ */ new Map(), this.classToCategoryPartitionMap = /* @__PURE__ */ new Map(), this.styleIdPrefix = "barocss-style-partition-", this.insertionPoint = t, this.maxRulesPerPartition = e, this.styleIdPrefix = s, this.getCategory = i, this.initializeDefaultPartition();
@@ -42,8 +8,8 @@ class d {
42
8
  * one insertRule at a binary-searched index, no sheet rewrite.
43
9
  */
44
10
  insertSorted(t, e, s) {
45
- const i = t.keys ??= [], r = v(i, s), o = t.styleElement.sheet;
46
- o && o.cssRules.length === i.length ? (o.insertRule(this.escapeCssRule(e), r), t.styles.splice(r, 0, e)) : (t.styles.splice(r, 0, e), t.styleElement.textContent = t.styles.join(`
11
+ const i = t.keys ??= [], r = b(i, s), n = t.styleElement.sheet;
12
+ n && n.cssRules.length === i.length ? (n.insertRule(this.escapeCssRule(e), r), t.styles.splice(r, 0, e)) : (t.styles.splice(r, 0, e), t.styleElement.textContent = t.styles.join(`
47
13
  `) + `
48
14
  `), i.splice(r, 0, s);
49
15
  }
@@ -103,10 +69,10 @@ class d {
103
69
  addRule(t) {
104
70
  if (this.hasRule(t))
105
71
  return !1;
106
- const e = C(t);
72
+ const e = m(t);
107
73
  let s = this.partitions.findIndex((r) => {
108
- const o = r.keys;
109
- return !!o && o.length > 0 && P(o[o.length - 1], e) > 0;
74
+ const n = r.keys;
75
+ return !!n && n.length > 0 && S(n[n.length - 1], e) > 0;
110
76
  });
111
77
  s === -1 && (this.currentPartition.styles.length >= this.maxRulesPerPartition && this.createNewPartition(), s = this.partitions.length - 1);
112
78
  const i = this.partitions[s];
@@ -125,7 +91,7 @@ class d {
125
91
  let s = this.getCategoryPartition(e);
126
92
  s || (s = this.createNewCategoryPartition(e));
127
93
  try {
128
- this.insertSorted(s, t, C(t));
94
+ this.insertSorted(s, t, m(t));
129
95
  } catch (i) {
130
96
  return console.warn(
131
97
  `[StylePartitionManager] Failed to insert rule in category: ${e} ${t}`,
@@ -156,14 +122,41 @@ class d {
156
122
  for (const i of t) {
157
123
  const r = this.getCategory(i.cls);
158
124
  if (r)
159
- for (const o of i.cssList)
160
- this.addCategoryRule(o, r);
125
+ for (const n of i.cssList)
126
+ this.addCategoryRule(n, r);
161
127
  else
162
- for (const o of i.cssList)
163
- this.addRule(o) ? e++ : s++;
128
+ for (const n of i.cssList)
129
+ this.addRule(n) ? e++ : s++;
164
130
  }
165
131
  return { success: e, failed: s };
166
132
  }
133
+ /**
134
+ * Remove one generated rule (#269 GC). Keeps `styles`, the #254 `keys` and the
135
+ * sheet's cssRules parallel: one deleteRule at the rule's index, or a text
136
+ * rebuild when the sheet isn't solely ours / has no CSSOM. Returns whether
137
+ * the rule was found.
138
+ */
139
+ removeRule(t, e) {
140
+ let s;
141
+ if (e) {
142
+ if (this.classToCategoryPartitionMap.get(t) !== e) return !1;
143
+ s = this.categoryPartitions.get(e);
144
+ } else {
145
+ const o = this.classToPartitionMap.get(t);
146
+ s = o === void 0 ? void 0 : this.partitions[o];
147
+ }
148
+ if (!s) return !1;
149
+ const i = s.styles.indexOf(t);
150
+ if (i === -1) return !1;
151
+ const r = s.styleElement.sheet, n = !!r && r.cssRules.length === s.styles.length;
152
+ return s.styles.splice(i, 1), s.keys?.splice(i, 1), n && r ? r.deleteRule(i) : s.styleElement.textContent = s.styles.length ? s.styles.join(`
153
+ `) + `
154
+ ` : "", e ? this.classToCategoryPartitionMap.delete(t) : this.classToPartitionMap.delete(t), !0;
155
+ }
156
+ /** Number of generated (non-root, non-preflight) rules currently held. */
157
+ get ruleCount() {
158
+ return this.classToPartitionMap.size + this.classToCategoryPartitionMap.size;
159
+ }
167
160
  /**
168
161
  * 특정 규칙이 어느 파티션에 있는지 찾기
169
162
  */
@@ -194,13 +187,13 @@ class d {
194
187
  }), this.partitions = [], this.categoryPartitions.clear(), this.partitionCounter = 0, this.classToPartitionMap.clear(), this.classToCategoryPartitionMap.clear();
195
188
  }
196
189
  }
197
- function N(n) {
198
- return n ? typeof n == "object" && typeof n.baseVal == "string" ? n.baseVal : n.toString() : "";
190
+ function w(a) {
191
+ return a ? typeof a == "object" && typeof a.baseVal == "string" ? a.baseVal : a.toString() : "";
199
192
  }
200
- function h(n) {
201
- return n ? N(n).split(/\s+/).filter(Boolean) : [];
193
+ function u(a) {
194
+ return a ? w(a).split(/\s+/).filter(Boolean) : [];
202
195
  }
203
- class D {
196
+ class E {
204
197
  /**
205
198
  * Create a new ChangeDetector instance
206
199
  *
@@ -208,7 +201,10 @@ class D {
208
201
  * @param BrowserRuntime - Optional BrowserRuntime instance for CSS injection
209
202
  */
210
203
  constructor(t, e, s = (i) => y(i).utility?.category) {
211
- this.observer = null, this.incrementalParser = t, this.BrowserRuntime = e, this.getCategory = s;
204
+ this.observer = null, this.gc = null, this.incrementalParser = t, this.BrowserRuntime = e, this.getCategory = s;
205
+ }
206
+ setGc(t) {
207
+ this.gc = t;
212
208
  }
213
209
  setParser(t) {
214
210
  this.incrementalParser = t;
@@ -230,28 +226,29 @@ class D {
230
226
  */
231
227
  observe(t = document.body, e) {
232
228
  return typeof window > "u" ? new MutationObserver(() => {
233
- }) : (this.observer && this.observer.disconnect(), this.observer = new MutationObserver((s) => {
234
- const i = /* @__PURE__ */ new Set();
235
- if (s.forEach((r) => {
236
- if (r.type === "attributes" && r.attributeName === "class" && t.contains(r.target)) {
237
- const o = r.target;
238
- o.className && h(o.className).forEach((c) => {
239
- this.incrementalParser.isProcessed(c) || i.add(c);
229
+ }) : (this.observer && this.observer.disconnect(), this.gc?.setRoot(t), this.observer = new MutationObserver((s) => {
230
+ const i = /* @__PURE__ */ new Set(), r = this.gc;
231
+ if (s.forEach((n) => {
232
+ if (r && (n.type === "attributes" ? r.reconcile(n.target) : n.type === "childList" && (n.removedNodes.forEach((o) => r.reconcileTree(o)), n.addedNodes.forEach((o) => r.reconcileTree(o)))), n.type === "attributes" && n.attributeName === "class" && t.contains(n.target)) {
233
+ const o = n.target;
234
+ o.className && u(o.className).forEach((l) => {
235
+ this.incrementalParser.isProcessed(l) || i.add(l);
240
236
  });
241
237
  }
242
- r.type === "childList" && r.addedNodes.forEach((o) => {
238
+ n.type === "childList" && n.addedNodes.forEach((o) => {
243
239
  if (o.nodeType === Node.ELEMENT_NODE && t.contains(o)) {
244
- const a = o;
245
- this.processElement(a, i), a.querySelectorAll("[class]").forEach((c) => {
246
- this.processElement(c, i);
240
+ const c = o;
241
+ this.processElement(c, i), c.querySelectorAll("[class]").forEach((l) => {
242
+ this.processElement(l, i);
247
243
  });
248
244
  }
249
245
  });
250
246
  }), i.size > 0) {
251
- const r = Array.from(i), o = this.incrementalParser.processClasses(r);
247
+ const n = Array.from(i), o = this.incrementalParser.processClasses(n);
252
248
  this.BrowserRuntime?.applyParseResults(o);
253
249
  } else
254
250
  this.BrowserRuntime?.applyParseResults([]);
251
+ r?.afterBatch();
255
252
  }), this.observer.observe(t, {
256
253
  attributes: !0,
257
254
  subtree: !0,
@@ -264,19 +261,19 @@ class D {
264
261
  */
265
262
  scanExistingClasses(t, e) {
266
263
  const s = /* @__PURE__ */ new Set();
267
- t.className && h(t.className).forEach((o) => {
268
- this.incrementalParser.isProcessed(o) || s.add(o);
264
+ t.className && u(t.className).forEach((n) => {
265
+ this.incrementalParser.isProcessed(n) || s.add(n);
269
266
  });
270
267
  const i = t.querySelectorAll("[class]");
271
268
  for (const r of i)
272
269
  if (r.className) {
273
- const o = h(r.className);
274
- for (const a of o)
275
- this.incrementalParser.isProcessed(a) || s.add(a);
270
+ const n = u(r.className);
271
+ for (const o of n)
272
+ this.incrementalParser.isProcessed(o) || s.add(o);
276
273
  }
277
274
  if (s.size > 0) {
278
- const r = Array.from(s), o = this.incrementalParser.processClasses(r), a = o.filter((u) => this.getCategory(u.cls) === "layout"), c = o.filter((u) => this.getCategory(u.cls) !== "layout");
279
- this.BrowserRuntime?.applyParseResults(a), e?.onReady?.(), this.BrowserRuntime?.applyParseResults(c);
275
+ const r = Array.from(s), n = this.incrementalParser.processClasses(r), o = n.filter((l) => this.getCategory(l.cls) === "layout"), c = n.filter((l) => this.getCategory(l.cls) !== "layout");
276
+ this.BrowserRuntime?.applyParseResults(o), e?.onReady?.(), this.BrowserRuntime?.applyParseResults(c);
280
277
  } else
281
278
  e?.onReady?.();
282
279
  }
@@ -293,7 +290,7 @@ class D {
293
290
  * @param newClasses - Set to collect newly discovered class names
294
291
  */
295
292
  processElement(t, e) {
296
- t.className && h(t.className).forEach((i) => {
293
+ t.className && u(t.className).forEach((i) => {
297
294
  this.incrementalParser.isProcessed(i) || e.add(i);
298
295
  });
299
296
  }
@@ -308,44 +305,131 @@ class D {
308
305
  this.observer && (this.observer.disconnect(), this.observer = null);
309
306
  }
310
307
  }
311
- function I(n) {
312
- return n.replace(/\\([0-9a-fA-F]{1,6})\s?|\\(.)/g, (t, e, s) => e ? String.fromCodePoint(parseInt(e, 16)) : s);
308
+ function M(a) {
309
+ return a.replace(/\\([0-9a-fA-F]{1,6})\s?|\\(.)/g, (t, e, s) => e ? String.fromCodePoint(parseInt(e, 16)) : s);
313
310
  }
314
- const L = /^\s*\.((?:\\[0-9a-fA-F]{1,6}\s?|\\.|[\w-]|[^\x00-\x7F])+)/;
315
- function T(n) {
311
+ const v = /^\s*(?::(?:where|is)\(\s*)?\.((?:\\[0-9a-fA-F]{1,6}\s?|\\.|[\w-]|[^\x00-\x7F])+)/;
312
+ function A(a) {
316
313
  const t = [];
317
314
  let e = 0, s = 0;
318
- for (let i = 0; i < n.length; i++) {
319
- const r = n[i];
320
- r === "\\" ? i++ : r === "(" || r === "[" ? e++ : r === ")" || r === "]" ? e-- : r === "," && e === 0 && (t.push(n.slice(s, i)), s = i + 1);
315
+ for (let i = 0; i < a.length; i++) {
316
+ const r = a[i];
317
+ r === "\\" ? i++ : r === "(" || r === "[" ? e++ : r === ")" || r === "]" ? e-- : r === "," && e === 0 && (t.push(a.slice(s, i)), s = i + 1);
318
+ }
319
+ return t.push(a.slice(s)), t;
320
+ }
321
+ function P(a, t = /* @__PURE__ */ new Set()) {
322
+ for (const e of Array.from(a)) {
323
+ if (e.type === 7 && typeof e.name == "string") {
324
+ t.add(e.name);
325
+ continue;
326
+ }
327
+ const s = e.cssRules;
328
+ s && s.length && P(s, t);
321
329
  }
322
- return t.push(n.slice(s)), t;
330
+ return t;
323
331
  }
324
- function b(n, t = /* @__PURE__ */ new Set()) {
325
- for (const e of Array.from(n)) {
332
+ function g(a, t = /* @__PURE__ */ new Set()) {
333
+ for (const e of Array.from(a)) {
326
334
  const s = e.selectorText;
327
335
  if (typeof s == "string")
328
- for (const r of T(s)) {
329
- const o = L.exec(r);
330
- o && t.add(I(o[1]));
336
+ for (const r of A(s)) {
337
+ const n = v.exec(r);
338
+ n && t.add(M(n[1]));
331
339
  }
332
340
  const i = e.cssRules;
333
- i && i.length && b(i, t);
341
+ i && i.length && g(i, t);
334
342
  }
335
343
  return t;
336
344
  }
337
- const $ = "@layer theme, base, components, utilities;";
338
- class k {
345
+ class T {
346
+ constructor(t, e, s, i = () => Date.now()) {
347
+ this.host = t, this.graceMs = e, this.maxRules = s, this.now = i, this.counts = /* @__PURE__ */ new Map(), this.counted = /* @__PURE__ */ new WeakMap(), this.candidates = /* @__PURE__ */ new Map(), this.timer = null, this.root = null;
348
+ }
349
+ /** Start counting for a new root: count every element currently inside it. */
350
+ setRoot(t) {
351
+ this.counts.clear(), this.counted = /* @__PURE__ */ new WeakMap(), this.candidates.clear(), this.cancel(), this.root = t, this.reconcileTree(t);
352
+ }
353
+ count(t) {
354
+ return this.counts.get(t) ?? 0;
355
+ }
356
+ /** Re-count `el` and (optionally) all its descendants from their current state. */
357
+ reconcileTree(t) {
358
+ if (t.nodeType !== 1) return;
359
+ const e = t;
360
+ this.reconcile(e), e.querySelectorAll("[class]").forEach((s) => this.reconcile(s));
361
+ }
362
+ reconcile(t) {
363
+ const e = this.root, i = !!e && e.contains(t) ? Array.from(new Set(u(t.getAttribute("class")))) : [], r = this.counted.get(t);
364
+ if (!r && i.length === 0) return;
365
+ const n = new Set(r ?? []), o = new Set(i);
366
+ for (const c of o) {
367
+ if (n.has(c)) continue;
368
+ const l = (this.counts.get(c) ?? 0) + 1;
369
+ this.counts.set(c, l), this.candidates.delete(c);
370
+ }
371
+ for (const c of n) {
372
+ if (o.has(c)) continue;
373
+ const l = (this.counts.get(c) ?? 0) - 1;
374
+ l > 0 ? this.counts.set(c, l) : (this.counts.delete(c), this.candidates.delete(c), this.candidates.set(c, this.now()));
375
+ }
376
+ i.length ? this.counted.set(t, i) : this.counted.delete(t);
377
+ }
378
+ /** Call after a mutation batch has been counted and its classes inserted. */
379
+ afterBatch() {
380
+ this.candidates.size !== 0 && (this.host.cachedCount() > this.maxRules ? this.schedule(0) : this.schedule(this.graceMs));
381
+ }
382
+ schedule(t) {
383
+ if (this.timer !== null) {
384
+ if (t > 0) return;
385
+ clearTimeout(this.timer);
386
+ }
387
+ this.timer = setTimeout(() => {
388
+ this.timer = null, this.sweep();
389
+ }, t);
390
+ }
391
+ /** Reclaim candidates whose grace period elapsed (plus LRU overflow). Public for tests. */
392
+ sweep() {
393
+ const t = this.now(), e = Math.max(0, this.host.cachedCount() - this.maxRules), s = [];
394
+ let i = 0;
395
+ for (const [r, n] of this.candidates) {
396
+ const o = t - n >= this.graceMs;
397
+ !o && i >= e || (this.candidates.delete(r), !(this.count(r) > 0 || this.inDom(r) || this.host.isPermanent(r)) && (s.push(r), o || i++));
398
+ }
399
+ s.length && this.host.reclaim(s), this.candidates.size && this.schedule(this.graceMs);
400
+ }
401
+ inDom(t) {
402
+ const e = this.root;
403
+ if (!e) return !1;
404
+ const s = e.ownerDocument ?? document;
405
+ return e.classList.contains(t) || s.documentElement.classList.contains(t) || s.getElementsByClassName(t).length > 0;
406
+ }
407
+ cancel() {
408
+ this.timer !== null && clearTimeout(this.timer), this.timer = null;
409
+ }
410
+ stats() {
411
+ return { trackedClasses: this.counts.size, candidates: this.candidates.size };
412
+ }
413
+ }
414
+ const D = "style[data-barocss-ssr]", L = "@layer theme, base, components, utilities;";
415
+ class N {
339
416
  constructor(t = {}) {
340
- this.cache = /* @__PURE__ */ new Map(), this.rootCache = /* @__PURE__ */ new Set(), this.isDestroyed = !1, this.existing = null, this.existingSheetCount = -1, this.getCategory = (s) => y(s, this.context).utility?.category;
417
+ this.cache = /* @__PURE__ */ new Map(), this.rootCache = /* @__PURE__ */ new Set(), this.isDestroyed = !1, this.existing = null, this.existingKeyframes = /* @__PURE__ */ new Set(), this.existingSheetCount = -1, this.pinned = /* @__PURE__ */ new Set(), this.gc = null, this.reclaimedCount = 0, this.ssrRules = [], this.ssrClasses = /* @__PURE__ */ new Set(), this.observedOnce = !1, this.getCategory = (s) => y(s, this.context).utility?.category;
341
418
  const e = {};
342
419
  this.options = {
343
420
  config: t.config || e,
344
421
  styleId: t.styleId || "barocss-runtime",
345
422
  insertionPoint: t.insertionPoint || "head",
346
423
  maxRulesPerPartition: t.maxRulesPerPartition || 50,
347
- skipExisting: t.skipExisting ?? !1
348
- }, this.context = g(this.options.config), this.incrementalParser = new p(this.context), this.changeDetector = new D(this.incrementalParser, this, this.getCategory), this.stylePartitionManager = new d(this.getInsertionPoint(), this.options.maxRulesPerPartition, `${this.options.styleId}-partition`, this.getCategory), this.init();
424
+ skipExisting: t.skipExisting ?? !1,
425
+ gc: t.gc ?? !0,
426
+ gcGraceMs: t.gcGraceMs ?? 3e3,
427
+ maxRules: t.maxRules ?? 1 / 0
428
+ }, this.context = p(this.options.config), this.incrementalParser = new C(this.context), this.changeDetector = new E(this.incrementalParser, this, this.getCategory), this.stylePartitionManager = new d(this.getInsertionPoint(), this.options.maxRulesPerPartition, `${this.options.styleId}-partition`, this.getCategory), this.options.gc && (this.gc = new T({
429
+ reclaim: (s) => this.reclaim(s),
430
+ isPermanent: (s) => this.isPermanent(s),
431
+ cachedCount: () => this.cache.size
432
+ }, this.options.gcGraceMs, this.options.maxRules), this.changeDetector.setGc(this.gc)), this.init();
349
433
  }
350
434
  // Debugging and logging helpers
351
435
  /**
@@ -355,7 +439,38 @@ class k {
355
439
  (console[t] || console.log)(`[BrowserRuntime:${t.toUpperCase()}] ${e}`, s || "");
356
440
  }
357
441
  init() {
358
- console.log("[BrowserRuntime] init"), this.injectPreflightCSS(), this.ensureCssVars();
442
+ console.log("[BrowserRuntime] init"), this.injectPreflightCSS(), this.ensureCssVars(), this.adoptSsrSheets();
443
+ }
444
+ /**
445
+ * #268: adopt the class rules of server-rendered `<style data-barocss-ssr>` sheets in <head>, at startup
446
+ * (constructor and the first observe()). Each rule moves
447
+ * (same task, so no paint in between) into the partition its class would get if generated here, at
448
+ * its #254 sorted position, so a later client `sm:` rule lands before a server `lg:` rule. Its classes
449
+ * are never regenerated and never reclaimed. `:root`, `@property` and `@keyframes` stay in the sheet.
450
+ */
451
+ adoptSsrSheets() {
452
+ if (typeof document > "u") return;
453
+ const t = [];
454
+ if (document.head) {
455
+ for (const e of Array.from(document.head.querySelectorAll(`${D}:not([data-barocss-adopted])`))) {
456
+ const s = e.sheet;
457
+ if (!s) continue;
458
+ e.setAttribute("data-barocss-adopted", "");
459
+ const i = [];
460
+ for (let r = s.cssRules.length - 1; r >= 0; r--) {
461
+ const n = s.cssRules[r], o = g([n]);
462
+ o.size !== 0 && (o.forEach((c) => this.ssrClasses.add(c)), i.unshift({ css: n.cssText, cls: o.values().next().value }), s.deleteRule(r));
463
+ }
464
+ t.push(...i);
465
+ }
466
+ t.length !== 0 && (this.ssrRules.push(...t), this.insertSsrRules(t));
467
+ }
468
+ }
469
+ insertSsrRules(t) {
470
+ for (const { css: e, cls: s } of t) {
471
+ const i = this.getCategory(s);
472
+ i ? this.stylePartitionManager.addCategoryRule(e, i) : this.stylePartitionManager.addRule(e);
473
+ }
359
474
  }
360
475
  injectPreflightCSS() {
361
476
  const t = this.options.config.preflight ?? !0;
@@ -363,7 +478,7 @@ class k {
363
478
  const e = this.context.getPreflightCSS(t);
364
479
  this.stylePartitionManager.updateRuleContent(
365
480
  "preflight",
366
- `${$}
481
+ `${L}
367
482
  @layer base {
368
483
  ${e}
369
484
  }`,
@@ -392,8 +507,8 @@ ${e}
392
507
  */
393
508
  addClass(t) {
394
509
  if (this.isDestroyed) return;
395
- const e = this.normalizeClasses(t);
396
- this.processClasses(e);
510
+ const e = this.normalizeClasses(t).filter(Boolean);
511
+ e.forEach((s) => this.pinned.add(s)), this.processClasses(e);
397
512
  }
398
513
  /**
399
514
  * Process classes
@@ -414,48 +529,79 @@ ${e}
414
529
  applyParseResults(t, e) {
415
530
  if (this.isDestroyed) return;
416
531
  if (this.getInsertionPoint().isConnected && this.stylePartitionManager.hasDetachedPartitions()) {
417
- const r = Array.from(this.cache.values());
418
- this.reset(), t = [...r, ...t], t.forEach((o) => this.incrementalParser.markProcessed(o.cls));
532
+ const n = Array.from(this.cache.values());
533
+ this.reset(), t = [...n, ...t], t.forEach((o) => this.incrementalParser.markProcessed(o.cls));
419
534
  }
420
- if (this.options.skipExisting && t.length > 0 && typeof document < "u") {
421
- const r = this.getExistingClasses();
422
- t = t.filter((o) => !r.has(o.cls));
535
+ if (this.ssrClasses.size > 0 && (t = t.filter((n) => !this.ssrClasses.has(n.cls))), this.options.skipExisting && t.length > 0 && typeof document < "u") {
536
+ const n = this.getExistingClasses();
537
+ t = t.filter((o) => !n.has(o.cls));
423
538
  }
424
539
  if (t.length === 0) return;
425
- const s = [], i = [];
426
- for (const r of t)
427
- if (r.css && Array.isArray(r.cssList) && (s.push(r), this.cache.set(r.cls, r)), r.rootCss && Array.isArray(r.rootCssList))
428
- for (const o of r.rootCssList)
540
+ const s = [], i = [], r = this.options.skipExisting && typeof document < "u" ? (this.getExistingClasses(), this.existingKeyframes) : null;
541
+ for (const n of t)
542
+ if (n.css && Array.isArray(n.cssList) && (s.push(n), this.cache.set(n.cls, n)), n.rootCss && Array.isArray(n.rootCssList))
543
+ for (const o of n.rootCssList) {
544
+ if (r?.size) {
545
+ const c = /^\s*@keyframes\s+([^\s{]+)/.exec(o)?.[1];
546
+ if (c && r.has(c)) continue;
547
+ }
429
548
  this.rootCache.has(o) || (this.rootCache.add(o), i.push(o));
549
+ }
430
550
  i.length > 0 && this.stylePartitionManager.addRootRules(i.filter(Boolean)), s.length > 0 && this.stylePartitionManager.addRules(s), this.debugLog("info", `Applied ${t.length} parser results`, {
431
551
  cssRuleCount: s.length,
432
552
  rootCssCount: i.length
433
553
  });
434
554
  }
555
+ /** #269: a class that must never be reclaimed. */
556
+ isPermanent(t) {
557
+ return this.pinned.has(t) || this.ssrClasses.has(t) || typeof document > "u" ? !0 : this.getExistingClasses().has(t);
558
+ }
559
+ /**
560
+ * #269: delete the generated rules of classes no live element uses. Root/@property/@keyframes rules stay
561
+ * (they are shared and harmless); a rule text another cached class still emits is kept.
562
+ */
563
+ reclaim(t) {
564
+ if (this.isDestroyed) return;
565
+ const e = t.filter((r) => this.cache.has(r));
566
+ if (e.length === 0) return;
567
+ const s = e.map((r) => this.cache.get(r));
568
+ e.forEach((r) => {
569
+ this.cache.delete(r), this.incrementalParser.unmarkProcessed(r);
570
+ });
571
+ const i = /* @__PURE__ */ new Set();
572
+ for (const r of this.cache.values()) r.cssList.forEach((n) => i.add(n));
573
+ for (const r of s) {
574
+ const n = this.getCategory(r.cls);
575
+ for (const o of r.cssList)
576
+ i.has(o) || this.stylePartitionManager.removeRule(o, n);
577
+ }
578
+ this.reclaimedCount += e.length;
579
+ }
435
580
  /** Class names defined by the page's own stylesheets (BaroCSS's sheets and cross-origin sheets excluded). */
436
581
  getExistingClasses() {
437
- const t = Array.from(document.styleSheets).filter((s) => {
438
- const i = s.ownerNode;
439
- return !(i && typeof i.hasAttribute == "function" && (i.hasAttribute("data-barocss") || (i.id || "").startsWith(this.options.styleId)));
582
+ const t = new Set(Array.from(document.querySelectorAll("style[data-barocss]"), (r) => r.sheet)), e = Array.from(document.styleSheets).filter((r) => {
583
+ if (t.has(r)) return !1;
584
+ const n = r.ownerNode;
585
+ return !(n && typeof n.hasAttribute == "function" && (n.hasAttribute("data-barocss") || (n.id || "").startsWith(this.options.styleId)));
440
586
  });
441
- if (this.existing && t.length === this.existingSheetCount) return this.existing;
442
- const e = /* @__PURE__ */ new Set();
443
- for (const s of t) {
444
- let i;
587
+ if (this.existing && e.length === this.existingSheetCount) return this.existing;
588
+ const s = /* @__PURE__ */ new Set(), i = /* @__PURE__ */ new Set();
589
+ for (const r of e) {
590
+ let n;
445
591
  try {
446
- i = s.cssRules;
592
+ n = r.cssRules;
447
593
  } catch {
448
594
  continue;
449
595
  }
450
- b(i, e);
596
+ g(n, s), P(n, i);
451
597
  }
452
- return this.existing = e, this.existingSheetCount = t.length, e;
598
+ return this.existingKeyframes = i, this.existing = s, this.existingSheetCount = e.length, s;
453
599
  }
454
600
  /**
455
601
  * MutationObserver instance method to automatically call addClass when class attributes change in DOM
456
602
  */
457
603
  observe(t = document.body, e) {
458
- return this.changeDetector.observe(t, e);
604
+ return this.observedOnce || (this.observedOnce = !0, this.adoptSsrSheets()), this.changeDetector.observe(t, e);
459
605
  }
460
606
  normalizeClasses(t) {
461
607
  return Array.isArray(t) ? t.flatMap((e) => e.split(/\s+/)) : t.split(/\s+/);
@@ -482,7 +628,10 @@ ${e}
482
628
  return {
483
629
  runtime: {
484
630
  cachedClasses: this.cache.size,
485
- rootCacheSize: this.rootCache.size
631
+ rootCacheSize: this.rootCache.size,
632
+ ruleCount: this.stylePartitionManager.ruleCount,
633
+ reclaimedClasses: this.reclaimedCount,
634
+ gc: this.gc?.stats() ?? null
486
635
  },
487
636
  ast: t.cacheStats.ast,
488
637
  incremental: t
@@ -492,15 +641,15 @@ ${e}
492
641
  * Clear all caches (useful for debugging or memory management)
493
642
  */
494
643
  clearCaches() {
495
- this.isDestroyed || (this.cache.clear(), this.rootCache.clear(), x(this.context), this.incrementalParser.clearProcessed(), this.stylePartitionManager.cleanup(), this.stylePartitionManager = new d(this.getInsertionPoint(), this.options.maxRulesPerPartition, `${this.options.styleId}-partition`, this.getCategory), this.injectPreflightCSS(), this.ensureCssVars());
644
+ this.isDestroyed || (this.cache.clear(), this.rootCache.clear(), x(this.context), this.incrementalParser.clearProcessed(), this.stylePartitionManager.cleanup(), this.stylePartitionManager = new d(this.getInsertionPoint(), this.options.maxRulesPerPartition, `${this.options.styleId}-partition`, this.getCategory), this.injectPreflightCSS(), this.ensureCssVars(), this.insertSsrRules(this.ssrRules));
496
645
  }
497
646
  reset() {
498
- this.isDestroyed || (this.cache.clear(), this.rootCache.clear(), this.incrementalParser.clearProcessed(), this.stylePartitionManager.cleanup(), this.stylePartitionManager = new d(this.getInsertionPoint(), this.options.maxRulesPerPartition, `${this.options.styleId}-partition`, this.getCategory), this.injectPreflightCSS(), this.ensureCssVars());
647
+ this.isDestroyed || (this.cache.clear(), this.rootCache.clear(), this.incrementalParser.clearProcessed(), this.stylePartitionManager.cleanup(), this.stylePartitionManager = new d(this.getInsertionPoint(), this.options.maxRulesPerPartition, `${this.options.styleId}-partition`, this.getCategory), this.injectPreflightCSS(), this.ensureCssVars(), this.insertSsrRules(this.ssrRules));
499
648
  }
500
649
  updateConfig(t) {
501
650
  if (this.isDestroyed) return;
502
651
  const e = Array.from(this.cache.keys());
503
- this.options.config = t, this.context = g(t), this.incrementalParser = new p(this.context), this.changeDetector.setParser(this.incrementalParser), this.reset(), e.length > 0 && this.addClass(e);
652
+ this.options.config = t, this.context = p(t), this.incrementalParser = new C(this.context), this.changeDetector.setParser(this.incrementalParser), this.reset(), e.length > 0 && this.addClass(e);
504
653
  }
505
654
  removeClass(t) {
506
655
  if (this.isDestroyed) return;
@@ -508,7 +657,7 @@ ${e}
508
657
  s.length !== this.cache.size && (this.reset(), s.forEach((i) => this.incrementalParser.markProcessed(i.cls)), this.applyParseResults(s));
509
658
  }
510
659
  destroy() {
511
- this.isDestroyed || (this.changeDetector.disconnect(), this.stylePartitionManager.cleanup(), this.cache.clear(), this.rootCache.clear(), this.isDestroyed = !0);
660
+ this.isDestroyed || (this.changeDetector.disconnect(), this.gc?.cancel(), this.stylePartitionManager.cleanup(), this.cache.clear(), this.rootCache.clear(), this.isDestroyed = !0);
512
661
  }
513
662
  getStats() {
514
663
  return {
@@ -520,28 +669,28 @@ ${e}
520
669
  };
521
670
  }
522
671
  }
523
- let l = null, f;
524
- function B(n = {}) {
525
- return !l || l.getStats().isDestroyed ? (l = new k(n), f = n.config) : n.config && n.config !== f && (l.updateConfig(n.config), f = n.config), l;
672
+ let h = null, f;
673
+ function I(a = {}) {
674
+ return !h || h.getStats().isDestroyed ? (h = new N(a), f = a.config) : a.config && a.config !== f && (h.updateConfig(a.config), f = a.config), h;
526
675
  }
527
- function R({ loadingClassName: n = "baro-boot", ...t } = {}) {
676
+ function R({ loadingClassName: a = "baro-boot", ...t } = {}) {
528
677
  if (!document.body) {
529
- document.addEventListener("DOMContentLoaded", () => R({ loadingClassName: n, ...t }), { once: !0 });
678
+ document.addEventListener("DOMContentLoaded", () => R({ loadingClassName: a, ...t }), { once: !0 });
530
679
  return;
531
680
  }
532
- const e = `${n}-doing`, s = `${n}-done`;
681
+ const e = `${a}-doing`, s = `${a}-done`;
533
682
  try {
534
- document.body.classList.add(e), B(t).observe(document.body, { scan: !0, onReady: () => {
683
+ document.body.classList.add(e), I(t).observe(document.body, { scan: !0, onReady: () => {
535
684
  document.body.classList.remove(e), document.body.classList.add(s);
536
685
  } });
537
686
  } catch (i) {
538
687
  document.body?.classList.remove(e), console.error("BaroCSS boot failed:", i);
539
688
  }
540
689
  }
541
- const V = R;
542
- function j(n) {
543
- if (!n || typeof n != "object" || Array.isArray(n)) return [];
544
- const t = n.elements;
690
+ const $ = R;
691
+ function k(a) {
692
+ if (!a || typeof a != "object" || Array.isArray(a)) return [];
693
+ const t = a.elements;
545
694
  if (!t || typeof t != "object" || Array.isArray(t)) return [];
546
695
  const e = /* @__PURE__ */ new Set();
547
696
  for (const s of Object.keys(t)) {
@@ -549,15 +698,15 @@ function j(n) {
549
698
  if (!i || typeof i != "object" || Array.isArray(i)) continue;
550
699
  const r = i.props;
551
700
  if (!r || typeof r != "object" || Array.isArray(r)) continue;
552
- const o = r.className;
553
- if (typeof o == "string")
554
- for (const a of o.split(/\s+/))
555
- a && e.add(a);
701
+ const n = r.className;
702
+ if (typeof n == "string")
703
+ for (const o of n.split(/\s+/))
704
+ o && e.add(o);
556
705
  }
557
706
  return Array.from(e);
558
707
  }
559
- function O(n, t) {
560
- const e = j(n);
708
+ function j(a, t) {
709
+ const e = k(a);
561
710
  e.length > 0 && t.addClass(e);
562
711
  }
563
712
  const z = [
@@ -592,8 +741,8 @@ const z = [
592
741
  "sidebar-accent-foreground",
593
742
  "sidebar-border",
594
743
  "sidebar-ring"
595
- ], F = {
596
- colors: Object.fromEntries(z.map((n) => [n, `var(--${n})`])),
744
+ ], O = {
745
+ colors: Object.fromEntries(z.map((a) => [a, `var(--${a})`])),
597
746
  borderRadius: {
598
747
  sm: "calc(var(--radius) - 4px)",
599
748
  md: "calc(var(--radius) - 2px)",
@@ -602,16 +751,17 @@ const z = [
602
751
  }
603
752
  };
604
753
  export {
605
- k as BrowserRuntime,
606
- D as ChangeDetector,
607
- $ as LAYER_ORDER,
754
+ N as BrowserRuntime,
755
+ E as ChangeDetector,
756
+ L as LAYER_ORDER,
757
+ D as SSR_STYLE_SELECTOR,
608
758
  d as StylePartitionManager,
609
759
  R as baroBoot,
610
- V as baroStart,
611
- j as collectJsonRenderClassNames,
612
- B as getRuntime,
613
- N as normalizeClassName,
614
- h as normalizeClassNameList,
615
- O as preloadJsonRenderClasses,
616
- F as shadcnTheme
760
+ $ as baroStart,
761
+ k as collectJsonRenderClassNames,
762
+ I as getRuntime,
763
+ w as normalizeClassName,
764
+ u as normalizeClassNameList,
765
+ j as preloadJsonRenderClasses,
766
+ O as shadcnTheme
617
767
  };