@barocss/browser 0.4.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.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Config } from '@barocss/kit';
2
2
  import { GenerateCssRulesResult } from '@barocss/kit';
3
3
  import { IncrementalParser } from '@barocss/kit';
4
+ import { RuleKey } from '@barocss/kit';
4
5
 
5
6
  export declare function baroBoot({ loadingClassName, ...options }?: BaroBootOptions): void;
6
7
 
@@ -16,9 +17,16 @@ export declare class BrowserRuntime {
16
17
  private context;
17
18
  private options;
18
19
  private isDestroyed;
20
+ private existing;
21
+ private existingSheetCount;
22
+ /** #269: classes requested explicitly through addClass(); never reclaimed. */
23
+ private pinned;
24
+ private gc;
25
+ private reclaimedCount;
19
26
  private incrementalParser;
20
27
  private changeDetector;
21
28
  private stylePartitionManager;
29
+ private getCategory;
22
30
  constructor(options?: BrowserRuntimeOptions);
23
31
  /**
24
32
  * Add debug logs (by level)
@@ -46,6 +54,15 @@ export declare class BrowserRuntime {
46
54
  applyParseResults(results: Array<GenerateCssRulesResult>, _opts?: {
47
55
  isBrowser?: boolean;
48
56
  }): void;
57
+ /** #269: a class that must never be reclaimed. */
58
+ private isPermanent;
59
+ /**
60
+ * #269: delete the generated rules of classes no live element uses. Root/@property rules stay
61
+ * (they are shared and harmless); a rule text another cached class still emits is kept.
62
+ */
63
+ private reclaim;
64
+ /** Class names defined by the page's own stylesheets (BaroCSS's sheets and cross-origin sheets excluded). */
65
+ getExistingClasses(): Set<string>;
49
66
  /**
50
67
  * MutationObserver instance method to automatically call addClass when class attributes change in DOM
51
68
  */
@@ -65,6 +82,12 @@ export declare class BrowserRuntime {
65
82
  runtime: {
66
83
  cachedClasses: number;
67
84
  rootCacheSize: number;
85
+ ruleCount: number;
86
+ reclaimedClasses: number;
87
+ gc: {
88
+ trackedClasses: number;
89
+ candidates: number;
90
+ } | null;
68
91
  };
69
92
  ast: {
70
93
  size: number;
@@ -101,6 +124,12 @@ export declare class BrowserRuntime {
101
124
  runtime: {
102
125
  cachedClasses: number;
103
126
  rootCacheSize: number;
127
+ ruleCount: number;
128
+ reclaimedClasses: number;
129
+ gc: {
130
+ trackedClasses: number;
131
+ candidates: number;
132
+ } | null;
104
133
  };
105
134
  ast: {
106
135
  size: number;
@@ -128,6 +157,30 @@ export declare interface BrowserRuntimeOptions {
128
157
  styleId?: string;
129
158
  insertionPoint?: 'head' | 'body' | HTMLElement;
130
159
  maxRulesPerPartition?: number;
160
+ /**
161
+ * #210: skip classes the page's existing (non-BaroCSS, same-origin) stylesheets already define,
162
+ * so a built app plus the runtime injects only what the build is missing. Opt-in. A class counts
163
+ * as covered only when a rule's selector starts with it (e.g. `.p-4`, `.md\:p-4` inside @media,
164
+ * `.hover\:x:hover`), so a class seen only as a descendant (`.group:hover .x`) is not skipped.
165
+ * The index is rebuilt when `document.styleSheets.length` changes. A page class that leads a selector
166
+ * with the same name is treated as covered, and rules added later to an already-indexed sheet aren't seen.
167
+ */
168
+ skipExisting?: boolean;
169
+ /**
170
+ * #269: reclaim the rules of classes that no element inside the observed root carries any more.
171
+ * On by default; it only acts on classes seen through `observe()`. A class is deleted only after
172
+ * its refcount has stayed 0 for `gcGraceMs` and a live-DOM re-check finds no element with it.
173
+ * Never reclaimed: classes passed to `addClass()`, classes any pre-existing (non-BaroCSS) sheet
174
+ * defines (build output, server sheet), root/@property/preflight rules. `false` disables it.
175
+ */
176
+ gc?: boolean;
177
+ /** #269: how long a class must stay unused before its rules are deleted (default 3000 ms). */
178
+ gcGraceMs?: number;
179
+ /**
180
+ * #269: soft cap on cached classes. When exceeded, unused (refcount 0) classes are evicted
181
+ * oldest-first without waiting for the grace period; classes in use are never evicted. Default: no cap.
182
+ */
183
+ maxRules?: number;
131
184
  }
132
185
 
133
186
  /**
@@ -152,13 +205,17 @@ export declare class ChangeDetector {
152
205
  private incrementalParser;
153
206
  /** Reference to BrowserRuntime for CSS injection (optional) */
154
207
  private BrowserRuntime?;
208
+ private getCategory;
155
209
  /**
156
210
  * Create a new ChangeDetector instance
157
211
  *
158
212
  * @param incrementalParser - IncrementalParser instance for class processing
159
213
  * @param BrowserRuntime - Optional BrowserRuntime instance for CSS injection
160
214
  */
161
- constructor(incrementalParser: IncrementalParser, BrowserRuntime?: BrowserRuntime);
215
+ constructor(incrementalParser: IncrementalParser, BrowserRuntime?: BrowserRuntime, getCategory?: (cls: string) => string | undefined);
216
+ /** #269: refcount/GC tracker, when the runtime has GC enabled. */
217
+ private gc;
218
+ setGc(gc: ClassGc | null): void;
162
219
  setParser(parser: IncrementalParser): void;
163
220
  /**
164
221
  * Starts observing DOM changes for new CSS classes
@@ -206,19 +263,101 @@ export declare class ChangeDetector {
206
263
  disconnect(): void;
207
264
  }
208
265
 
209
- export declare function getRuntime(options: BrowserRuntimeOptions): BrowserRuntime;
266
+ declare class ClassGc {
267
+ private host;
268
+ private graceMs;
269
+ private maxRules;
270
+ private now;
271
+ private counts;
272
+ private counted;
273
+ /** class -> time its count reached 0 (insertion order = oldest first). */
274
+ private candidates;
275
+ private timer;
276
+ private root;
277
+ constructor(host: ClassGcHost, graceMs: number, maxRules: number, now?: () => number);
278
+ /** Start counting for a new root: count every element currently inside it. */
279
+ setRoot(root: Element): void;
280
+ count(cls: string): number;
281
+ /** Re-count `el` and (optionally) all its descendants from their current state. */
282
+ reconcileTree(node: Node): void;
283
+ reconcile(el: Element): void;
284
+ /** Call after a mutation batch has been counted and its classes inserted. */
285
+ afterBatch(): void;
286
+ private schedule;
287
+ /** Reclaim candidates whose grace period elapsed (plus LRU overflow). Public for tests. */
288
+ sweep(): void;
289
+ private inDom;
290
+ cancel(): void;
291
+ stats(): {
292
+ trackedClasses: number;
293
+ candidates: number;
294
+ };
295
+ }
296
+
297
+ /**
298
+ * #269: per-class refcount over the observed root, plus a delayed sweep that
299
+ * reclaims classes no live element carries any more.
300
+ *
301
+ * Counting is reconciliation, not delta arithmetic: for every element a
302
+ * mutation batch touches (attribute target, added subtree, removed subtree) we
303
+ * compare the classes we last counted for it with what it carries *now*
304
+ * (nothing if it is no longer inside the root). That makes the count
305
+ * independent of record order, so remove-then-re-add in one batch, moves
306
+ * between parents and edits made while detached all settle to the true state.
307
+ * An element we miss can only leak a count (rule kept), never drop one.
308
+ *
309
+ * Before a class is reclaimed the sweep re-checks the live DOM
310
+ * (`getElementsByClassName`), so even a miscount cannot unstyle a live element.
311
+ */
312
+ declare interface ClassGcHost {
313
+ /** Delete the rules of these classes. */
314
+ reclaim(classes: string[]): void;
315
+ /** Whether a class must never be reclaimed (pinned, found in a pre-existing sheet, ...). */
316
+ isPermanent(cls: string): boolean;
317
+ /** Number of generated rules/classes currently cached (for the LRU cap). */
318
+ cachedCount(): number;
319
+ }
320
+
321
+ /** Collect literal className tokens from a json-render Spec's flat elements map. */
322
+ export declare function collectJsonRenderClassNames(spec: unknown): string[];
323
+
324
+ /**
325
+ * Returns the shared runtime, creating it on first use. If a live runtime
326
+ * already exists and `options.config` is a different config object, it is
327
+ * applied via `updateConfig` (which replaces the whole config), so an early
328
+ * `getRuntime()` never makes a later `baroStart({ config })` lose its config.
329
+ */
330
+ export declare function getRuntime(options?: BrowserRuntimeOptions): BrowserRuntime;
331
+
332
+ /** Tailwind 4 layer order, declared by BaroCSS's first <style> in <head>. */
333
+ export declare const LAYER_ORDER = "@layer theme, base, components, utilities;";
210
334
 
211
335
  export declare function normalizeClassName(className: any): string;
212
336
 
213
337
  export declare function normalizeClassNameList(className: any): string[];
214
338
 
339
+ /** Submit literal classes synchronously before UI mount; the caller validates class support. */
340
+ export declare function preloadJsonRenderClasses(spec: unknown, runtime: Pick<BrowserRuntime, 'addClass'>): void;
341
+
342
+ export declare const shadcnTheme: {
343
+ colors: Record<string, string>;
344
+ borderRadius: Record<string, string>;
345
+ };
346
+
215
347
  export declare interface StylePartition {
216
348
  id: string;
217
349
  styles: string[];
218
350
  styleElement: HTMLStyleElement;
351
+ /** Sort keys parallel to `styles` / the sheet's cssRules (#254). */
352
+ keys?: RuleKey[];
219
353
  }
220
354
 
221
355
  export declare class StylePartitionManager {
356
+ /**
357
+ * Insert `rule` at its Tailwind variant position within `partition` (#254):
358
+ * one insertRule at a binary-searched index, no sheet rewrite.
359
+ */
360
+ private insertSorted;
222
361
  private partitions;
223
362
  private categoryPartitions;
224
363
  private partitionCounter;
@@ -227,7 +366,8 @@ export declare class StylePartitionManager {
227
366
  private classToPartitionMap;
228
367
  private classToCategoryPartitionMap;
229
368
  private styleIdPrefix;
230
- constructor(insertionPoint: HTMLElement, maxRulesPerPartition?: number, styleIdPrefix?: string);
369
+ private getCategory;
370
+ constructor(insertionPoint: HTMLElement, maxRulesPerPartition?: number, styleIdPrefix?: string, getCategory?: (cls: string) => string | undefined);
231
371
  private initializeDefaultPartition;
232
372
  private createNewCategoryPartition;
233
373
  private createNewPartition;
@@ -254,11 +394,20 @@ export declare class StylePartitionManager {
254
394
  success: number;
255
395
  failed: number;
256
396
  };
397
+ /**
398
+ * Remove one generated rule (#269 GC). Keeps `styles`, the #254 `keys` and the
399
+ * sheet's cssRules parallel: one deleteRule at the rule's index, or a text
400
+ * rebuild when the sheet isn't solely ours / has no CSSOM. Returns whether
401
+ * the rule was found.
402
+ */
403
+ removeRule(rule: string, category?: string): boolean;
404
+ /** Number of generated (non-root, non-preflight) rules currently held. */
405
+ get ruleCount(): number;
257
406
  /**
258
407
  * 특정 규칙이 어느 파티션에 있는지 찾기
259
408
  */
260
409
  findRulePartition(rule: string): StylePartition | null;
261
- updateRuleContent(category: string, ruleContent: string): void;
410
+ updateRuleContent(category: string, ruleContent: string, atDocumentStart?: boolean): void;
262
411
  /**
263
412
  * 모든 파티션 정리
264
413
  */