@barocss/browser 0.5.0 → 0.6.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,40 +1,6 @@
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 g, upperBound as R, ruleSortKey as y, compareKeys as b, createContext as m, IncrementalParser as p, clearAstCache as w } from "@barocss/kit";
36
2
  class d {
37
- constructor(t, e = 50, s = "barocss-style-partition-", i = (r) => y(r).utility?.category) {
3
+ constructor(t, e = 50, s = "barocss-style-partition-", i = (r) => g(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();
39
5
  }
40
6
  /**
@@ -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 = R(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 = y(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 && b(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, y(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 a = this.classToPartitionMap.get(t);
146
+ s = a === void 0 ? void 0 : this.partitions[a];
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,21 +187,24 @@ 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 x(o) {
191
+ return o ? typeof o == "object" && typeof o.baseVal == "string" ? o.baseVal : o.toString() : "";
199
192
  }
200
- function h(n) {
201
- return n ? N(n).split(/\s+/).filter(Boolean) : [];
193
+ function u(o) {
194
+ return o ? x(o).split(/\s+/).filter(Boolean) : [];
202
195
  }
203
- class D {
196
+ class E {
204
197
  /**
205
198
  * Create a new ChangeDetector instance
206
199
  *
207
200
  * @param incrementalParser - IncrementalParser instance for class processing
208
201
  * @param BrowserRuntime - Optional BrowserRuntime instance for CSS injection
209
202
  */
210
- constructor(t, e, s = (i) => y(i).utility?.category) {
211
- this.observer = null, this.incrementalParser = t, this.BrowserRuntime = e, this.getCategory = s;
203
+ constructor(t, e, s = (i) => g(i).utility?.category) {
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((a) => r.reconcileTree(a)), n.addedNodes.forEach((a) => r.reconcileTree(a)))), n.type === "attributes" && n.attributeName === "class" && t.contains(n.target)) {
233
+ const a = n.target;
234
+ a.className && u(a.className).forEach((l) => {
235
+ this.incrementalParser.isProcessed(l) || i.add(l);
240
236
  });
241
237
  }
242
- r.type === "childList" && r.addedNodes.forEach((o) => {
243
- 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);
238
+ n.type === "childList" && n.addedNodes.forEach((a) => {
239
+ if (a.nodeType === Node.ELEMENT_NODE && t.contains(a)) {
240
+ const c = a;
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);
252
- this.BrowserRuntime?.applyParseResults(o);
247
+ const n = Array.from(i), a = this.incrementalParser.processClasses(n);
248
+ this.BrowserRuntime?.applyParseResults(a);
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,18 +261,18 @@ 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)
270
+ const n = u(r.className);
271
+ for (const a of n)
275
272
  this.incrementalParser.isProcessed(a) || s.add(a);
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");
275
+ const r = Array.from(s), n = this.incrementalParser.processClasses(r), a = n.filter((l) => this.getCategory(l.cls) === "layout"), c = n.filter((l) => this.getCategory(l.cls) !== "layout");
279
276
  this.BrowserRuntime?.applyParseResults(a), e?.onReady?.(), this.BrowserRuntime?.applyParseResults(c);
280
277
  } else
281
278
  e?.onReady?.();
@@ -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,120 @@ 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 S(o) {
309
+ return o.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 M = /^\s*\.((?:\\[0-9a-fA-F]{1,6}\s?|\\.|[\w-]|[^\x00-\x7F])+)/;
312
+ function v(o) {
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 < o.length; i++) {
316
+ const r = o[i];
317
+ r === "\\" ? i++ : r === "(" || r === "[" ? e++ : r === ")" || r === "]" ? e-- : r === "," && e === 0 && (t.push(o.slice(s, i)), s = i + 1);
321
318
  }
322
- return t.push(n.slice(s)), t;
319
+ return t.push(o.slice(s)), t;
323
320
  }
324
- function b(n, t = /* @__PURE__ */ new Set()) {
325
- for (const e of Array.from(n)) {
321
+ function C(o, t = /* @__PURE__ */ new Set()) {
322
+ for (const e of Array.from(o)) {
326
323
  const s = e.selectorText;
327
324
  if (typeof s == "string")
328
- for (const r of T(s)) {
329
- const o = L.exec(r);
330
- o && t.add(I(o[1]));
325
+ for (const r of v(s)) {
326
+ const n = M.exec(r);
327
+ n && t.add(S(n[1]));
331
328
  }
332
329
  const i = e.cssRules;
333
- i && i.length && b(i, t);
330
+ i && i.length && C(i, t);
334
331
  }
335
332
  return t;
336
333
  }
337
- const $ = "@layer theme, base, components, utilities;";
338
- class k {
334
+ class A {
335
+ constructor(t, e, s, i = () => Date.now()) {
336
+ 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;
337
+ }
338
+ /** Start counting for a new root: count every element currently inside it. */
339
+ setRoot(t) {
340
+ this.counts.clear(), this.counted = /* @__PURE__ */ new WeakMap(), this.candidates.clear(), this.cancel(), this.root = t, this.reconcileTree(t);
341
+ }
342
+ count(t) {
343
+ return this.counts.get(t) ?? 0;
344
+ }
345
+ /** Re-count `el` and (optionally) all its descendants from their current state. */
346
+ reconcileTree(t) {
347
+ if (t.nodeType !== 1) return;
348
+ const e = t;
349
+ this.reconcile(e), e.querySelectorAll("[class]").forEach((s) => this.reconcile(s));
350
+ }
351
+ reconcile(t) {
352
+ const e = this.root, i = !!e && e.contains(t) ? Array.from(new Set(u(t.getAttribute("class")))) : [], r = this.counted.get(t);
353
+ if (!r && i.length === 0) return;
354
+ const n = new Set(r ?? []), a = new Set(i);
355
+ for (const c of a) {
356
+ if (n.has(c)) continue;
357
+ const l = (this.counts.get(c) ?? 0) + 1;
358
+ this.counts.set(c, l), this.candidates.delete(c);
359
+ }
360
+ for (const c of n) {
361
+ if (a.has(c)) continue;
362
+ const l = (this.counts.get(c) ?? 0) - 1;
363
+ l > 0 ? this.counts.set(c, l) : (this.counts.delete(c), this.candidates.delete(c), this.candidates.set(c, this.now()));
364
+ }
365
+ i.length ? this.counted.set(t, i) : this.counted.delete(t);
366
+ }
367
+ /** Call after a mutation batch has been counted and its classes inserted. */
368
+ afterBatch() {
369
+ this.candidates.size !== 0 && (this.host.cachedCount() > this.maxRules ? this.schedule(0) : this.schedule(this.graceMs));
370
+ }
371
+ schedule(t) {
372
+ if (this.timer !== null) {
373
+ if (t > 0) return;
374
+ clearTimeout(this.timer);
375
+ }
376
+ this.timer = setTimeout(() => {
377
+ this.timer = null, this.sweep();
378
+ }, t);
379
+ }
380
+ /** Reclaim candidates whose grace period elapsed (plus LRU overflow). Public for tests. */
381
+ sweep() {
382
+ const t = this.now(), e = Math.max(0, this.host.cachedCount() - this.maxRules), s = [];
383
+ let i = 0;
384
+ for (const [r, n] of this.candidates) {
385
+ const a = t - n >= this.graceMs;
386
+ !a && i >= e || (this.candidates.delete(r), !(this.count(r) > 0 || this.inDom(r) || this.host.isPermanent(r)) && (s.push(r), a || i++));
387
+ }
388
+ s.length && this.host.reclaim(s), this.candidates.size && this.schedule(this.graceMs);
389
+ }
390
+ inDom(t) {
391
+ const e = this.root;
392
+ if (!e) return !1;
393
+ const s = e.ownerDocument ?? document;
394
+ return e.classList.contains(t) || s.documentElement.classList.contains(t) || s.getElementsByClassName(t).length > 0;
395
+ }
396
+ cancel() {
397
+ this.timer !== null && clearTimeout(this.timer), this.timer = null;
398
+ }
399
+ stats() {
400
+ return { trackedClasses: this.counts.size, candidates: this.candidates.size };
401
+ }
402
+ }
403
+ const D = "@layer theme, base, components, utilities;";
404
+ class L {
339
405
  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;
406
+ this.cache = /* @__PURE__ */ new Map(), this.rootCache = /* @__PURE__ */ new Set(), this.isDestroyed = !1, this.existing = null, this.existingSheetCount = -1, this.pinned = /* @__PURE__ */ new Set(), this.gc = null, this.reclaimedCount = 0, this.getCategory = (s) => g(s, this.context).utility?.category;
341
407
  const e = {};
342
408
  this.options = {
343
409
  config: t.config || e,
344
410
  styleId: t.styleId || "barocss-runtime",
345
411
  insertionPoint: t.insertionPoint || "head",
346
412
  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();
413
+ skipExisting: t.skipExisting ?? !1,
414
+ gc: t.gc ?? !0,
415
+ gcGraceMs: t.gcGraceMs ?? 3e3,
416
+ maxRules: t.maxRules ?? 1 / 0
417
+ }, this.context = m(this.options.config), this.incrementalParser = new p(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 A({
418
+ reclaim: (s) => this.reclaim(s),
419
+ isPermanent: (s) => this.isPermanent(s),
420
+ cachedCount: () => this.cache.size
421
+ }, this.options.gcGraceMs, this.options.maxRules), this.changeDetector.setGc(this.gc)), this.init();
349
422
  }
350
423
  // Debugging and logging helpers
351
424
  /**
@@ -363,7 +436,7 @@ class k {
363
436
  const e = this.context.getPreflightCSS(t);
364
437
  this.stylePartitionManager.updateRuleContent(
365
438
  "preflight",
366
- `${$}
439
+ `${D}
367
440
  @layer base {
368
441
  ${e}
369
442
  }`,
@@ -392,8 +465,8 @@ ${e}
392
465
  */
393
466
  addClass(t) {
394
467
  if (this.isDestroyed) return;
395
- const e = this.normalizeClasses(t);
396
- this.processClasses(e);
468
+ const e = this.normalizeClasses(t).filter(Boolean);
469
+ e.forEach((s) => this.pinned.add(s)), this.processClasses(e);
397
470
  }
398
471
  /**
399
472
  * Process classes
@@ -415,41 +488,67 @@ ${e}
415
488
  if (this.isDestroyed) return;
416
489
  if (this.getInsertionPoint().isConnected && this.stylePartitionManager.hasDetachedPartitions()) {
417
490
  const r = Array.from(this.cache.values());
418
- this.reset(), t = [...r, ...t], t.forEach((o) => this.incrementalParser.markProcessed(o.cls));
491
+ this.reset(), t = [...r, ...t], t.forEach((n) => this.incrementalParser.markProcessed(n.cls));
419
492
  }
420
493
  if (this.options.skipExisting && t.length > 0 && typeof document < "u") {
421
494
  const r = this.getExistingClasses();
422
- t = t.filter((o) => !r.has(o.cls));
495
+ t = t.filter((n) => !r.has(n.cls));
423
496
  }
424
497
  if (t.length === 0) return;
425
498
  const s = [], i = [];
426
499
  for (const r of t)
427
500
  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)
429
- this.rootCache.has(o) || (this.rootCache.add(o), i.push(o));
501
+ for (const n of r.rootCssList)
502
+ this.rootCache.has(n) || (this.rootCache.add(n), i.push(n));
430
503
  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
504
  cssRuleCount: s.length,
432
505
  rootCssCount: i.length
433
506
  });
434
507
  }
508
+ /** #269: a class that must never be reclaimed. */
509
+ isPermanent(t) {
510
+ return this.pinned.has(t) || typeof document > "u" ? !0 : this.getExistingClasses().has(t);
511
+ }
512
+ /**
513
+ * #269: delete the generated rules of classes no live element uses. Root/@property rules stay
514
+ * (they are shared and harmless); a rule text another cached class still emits is kept.
515
+ */
516
+ reclaim(t) {
517
+ if (this.isDestroyed) return;
518
+ const e = t.filter((r) => this.cache.has(r));
519
+ if (e.length === 0) return;
520
+ const s = e.map((r) => this.cache.get(r));
521
+ e.forEach((r) => {
522
+ this.cache.delete(r), this.incrementalParser.unmarkProcessed(r);
523
+ });
524
+ const i = /* @__PURE__ */ new Set();
525
+ for (const r of this.cache.values()) r.cssList.forEach((n) => i.add(n));
526
+ for (const r of s) {
527
+ const n = this.getCategory(r.cls);
528
+ for (const a of r.cssList)
529
+ i.has(a) || this.stylePartitionManager.removeRule(a, n);
530
+ }
531
+ this.reclaimedCount += e.length;
532
+ }
435
533
  /** Class names defined by the page's own stylesheets (BaroCSS's sheets and cross-origin sheets excluded). */
436
534
  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)));
535
+ const t = new Set(Array.from(document.querySelectorAll("style[data-barocss]"), (i) => i.sheet)), e = Array.from(document.styleSheets).filter((i) => {
536
+ if (t.has(i)) return !1;
537
+ const r = i.ownerNode;
538
+ return !(r && typeof r.hasAttribute == "function" && (r.hasAttribute("data-barocss") || (r.id || "").startsWith(this.options.styleId)));
440
539
  });
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;
540
+ if (this.existing && e.length === this.existingSheetCount) return this.existing;
541
+ const s = /* @__PURE__ */ new Set();
542
+ for (const i of e) {
543
+ let r;
445
544
  try {
446
- i = s.cssRules;
545
+ r = i.cssRules;
447
546
  } catch {
448
547
  continue;
449
548
  }
450
- b(i, e);
549
+ C(r, s);
451
550
  }
452
- return this.existing = e, this.existingSheetCount = t.length, e;
551
+ return this.existing = s, this.existingSheetCount = e.length, s;
453
552
  }
454
553
  /**
455
554
  * MutationObserver instance method to automatically call addClass when class attributes change in DOM
@@ -482,7 +581,10 @@ ${e}
482
581
  return {
483
582
  runtime: {
484
583
  cachedClasses: this.cache.size,
485
- rootCacheSize: this.rootCache.size
584
+ rootCacheSize: this.rootCache.size,
585
+ ruleCount: this.stylePartitionManager.ruleCount,
586
+ reclaimedClasses: this.reclaimedCount,
587
+ gc: this.gc?.stats() ?? null
486
588
  },
487
589
  ast: t.cacheStats.ast,
488
590
  incremental: t
@@ -492,7 +594,7 @@ ${e}
492
594
  * Clear all caches (useful for debugging or memory management)
493
595
  */
494
596
  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());
597
+ this.isDestroyed || (this.cache.clear(), this.rootCache.clear(), w(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());
496
598
  }
497
599
  reset() {
498
600
  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());
@@ -500,7 +602,7 @@ ${e}
500
602
  updateConfig(t) {
501
603
  if (this.isDestroyed) return;
502
604
  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);
605
+ this.options.config = t, this.context = m(t), this.incrementalParser = new p(this.context), this.changeDetector.setParser(this.incrementalParser), this.reset(), e.length > 0 && this.addClass(e);
504
606
  }
505
607
  removeClass(t) {
506
608
  if (this.isDestroyed) return;
@@ -508,7 +610,7 @@ ${e}
508
610
  s.length !== this.cache.size && (this.reset(), s.forEach((i) => this.incrementalParser.markProcessed(i.cls)), this.applyParseResults(s));
509
611
  }
510
612
  destroy() {
511
- this.isDestroyed || (this.changeDetector.disconnect(), this.stylePartitionManager.cleanup(), this.cache.clear(), this.rootCache.clear(), this.isDestroyed = !0);
613
+ this.isDestroyed || (this.changeDetector.disconnect(), this.gc?.cancel(), this.stylePartitionManager.cleanup(), this.cache.clear(), this.rootCache.clear(), this.isDestroyed = !0);
512
614
  }
513
615
  getStats() {
514
616
  return {
@@ -520,28 +622,28 @@ ${e}
520
622
  };
521
623
  }
522
624
  }
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;
625
+ let h = null, f;
626
+ function N(o = {}) {
627
+ return !h || h.getStats().isDestroyed ? (h = new L(o), f = o.config) : o.config && o.config !== f && (h.updateConfig(o.config), f = o.config), h;
526
628
  }
527
- function R({ loadingClassName: n = "baro-boot", ...t } = {}) {
629
+ function P({ loadingClassName: o = "baro-boot", ...t } = {}) {
528
630
  if (!document.body) {
529
- document.addEventListener("DOMContentLoaded", () => R({ loadingClassName: n, ...t }), { once: !0 });
631
+ document.addEventListener("DOMContentLoaded", () => P({ loadingClassName: o, ...t }), { once: !0 });
530
632
  return;
531
633
  }
532
- const e = `${n}-doing`, s = `${n}-done`;
634
+ const e = `${o}-doing`, s = `${o}-done`;
533
635
  try {
534
- document.body.classList.add(e), B(t).observe(document.body, { scan: !0, onReady: () => {
636
+ document.body.classList.add(e), N(t).observe(document.body, { scan: !0, onReady: () => {
535
637
  document.body.classList.remove(e), document.body.classList.add(s);
536
638
  } });
537
639
  } catch (i) {
538
640
  document.body?.classList.remove(e), console.error("BaroCSS boot failed:", i);
539
641
  }
540
642
  }
541
- const V = R;
542
- function j(n) {
543
- if (!n || typeof n != "object" || Array.isArray(n)) return [];
544
- const t = n.elements;
643
+ const z = P;
644
+ function T(o) {
645
+ if (!o || typeof o != "object" || Array.isArray(o)) return [];
646
+ const t = o.elements;
545
647
  if (!t || typeof t != "object" || Array.isArray(t)) return [];
546
648
  const e = /* @__PURE__ */ new Set();
547
649
  for (const s of Object.keys(t)) {
@@ -549,18 +651,18 @@ function j(n) {
549
651
  if (!i || typeof i != "object" || Array.isArray(i)) continue;
550
652
  const r = i.props;
551
653
  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+/))
654
+ const n = r.className;
655
+ if (typeof n == "string")
656
+ for (const a of n.split(/\s+/))
555
657
  a && e.add(a);
556
658
  }
557
659
  return Array.from(e);
558
660
  }
559
- function O(n, t) {
560
- const e = j(n);
661
+ function B(o, t) {
662
+ const e = T(o);
561
663
  e.length > 0 && t.addClass(e);
562
664
  }
563
- const z = [
665
+ const I = [
564
666
  "background",
565
667
  "foreground",
566
668
  "card",
@@ -592,8 +694,8 @@ const z = [
592
694
  "sidebar-accent-foreground",
593
695
  "sidebar-border",
594
696
  "sidebar-ring"
595
- ], F = {
596
- colors: Object.fromEntries(z.map((n) => [n, `var(--${n})`])),
697
+ ], $ = {
698
+ colors: Object.fromEntries(I.map((o) => [o, `var(--${o})`])),
597
699
  borderRadius: {
598
700
  sm: "calc(var(--radius) - 4px)",
599
701
  md: "calc(var(--radius) - 2px)",
@@ -602,16 +704,16 @@ const z = [
602
704
  }
603
705
  };
604
706
  export {
605
- k as BrowserRuntime,
606
- D as ChangeDetector,
607
- $ as LAYER_ORDER,
707
+ L as BrowserRuntime,
708
+ E as ChangeDetector,
709
+ D as LAYER_ORDER,
608
710
  d as StylePartitionManager,
609
- 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
711
+ P as baroBoot,
712
+ z as baroStart,
713
+ T as collectJsonRenderClassNames,
714
+ N as getRuntime,
715
+ x as normalizeClassName,
716
+ u as normalizeClassNameList,
717
+ B as preloadJsonRenderClasses,
718
+ $ as shadcnTheme
617
719
  };