@@ -39,6 +39,46 @@ export class StyleInjector {
3939 this . sheetManager = new SheetManager ( config ) ;
4040 }
4141
42+ /**
43+ * Allocate a className for a cacheKey without injecting styles yet.
44+ * This allows separating className allocation (render phase) from style injection (insertion phase).
45+ */
46+ allocateClassName (
47+ cacheKey : string ,
48+ options ?: { root ?: Document | ShadowRoot } ,
49+ ) : { className : string ; isNewAllocation : boolean } {
50+ const root = options ?. root || document ;
51+ const registry = this . sheetManager . getRegistry ( root ) ;
52+
53+ // Check if we can reuse existing className for this cache key
54+ if ( registry . rules . has ( cacheKey ) ) {
55+ const existingRuleInfo = registry . rules . get ( cacheKey ) ! ;
56+ return {
57+ className : existingRuleInfo . className ,
58+ isNewAllocation : false ,
59+ } ;
60+ }
61+
62+ // Generate new className and reserve it
63+ const className = generateClassName ( registry . classCounter ++ ) ;
64+
65+ // Create placeholder RuleInfo to reserve the className
66+ const placeholderRuleInfo = {
67+ className,
68+ ruleIndex : - 1 , // Placeholder - will be set during actual injection
69+ sheetIndex : - 1 , // Placeholder - will be set during actual injection
70+ } ;
71+
72+ // Reserve both className and cacheKey mappings
73+ registry . rules . set ( className , placeholderRuleInfo ) ;
74+ registry . rules . set ( cacheKey , placeholderRuleInfo ) ;
75+
76+ return {
77+ className,
78+ isNewAllocation : true ,
79+ } ;
80+ }
81+
4282 /**
4383 * Inject styles from StyleResult objects
4484 */
@@ -61,27 +101,36 @@ export class StyleInjector {
61101 // Check if we can reuse based on cache key
62102 const cacheKey = options ?. cacheKey ;
63103 let className : string ;
104+ let isPreAllocated = false ;
64105
65106 if ( cacheKey && registry . rules . has ( cacheKey ) ) {
66107 // Reuse existing class for this cache key
67108 const existingRuleInfo = registry . rules . get ( cacheKey ) ! ;
68109 className = existingRuleInfo . className ;
69- const currentRefCount = registry . refCounts . get ( className ) || 0 ;
70- registry . refCounts . set ( className , currentRefCount + 1 ) ;
71110
72- // Update metrics
73- if ( registry . metrics ) {
74- registry . metrics . hits ++ ;
75- }
111+ // Check if this is a placeholder (pre-allocated but not yet injected)
112+ isPreAllocated =
113+ existingRuleInfo . ruleIndex === - 1 && existingRuleInfo . sheetIndex === - 1 ;
76114
77- return {
78- className,
79- dispose : ( ) => this . dispose ( className , registry ) ,
80- } ;
81- }
115+ if ( ! isPreAllocated ) {
116+ // Already injected - just increment refCount
117+ const currentRefCount = registry . refCounts . get ( className ) || 0 ;
118+ registry . refCounts . set ( className , currentRefCount + 1 ) ;
82119
83- // Generate new className
84- className = generateClassName ( registry . classCounter ++ ) ;
120+ // Update metrics
121+ if ( registry . metrics ) {
122+ registry . metrics . hits ++ ;
123+ }
124+
125+ return {
126+ className,
127+ dispose : ( ) => this . dispose ( className , registry ) ,
128+ } ;
129+ }
130+ } else {
131+ // Generate new className
132+ className = generateClassName ( registry . classCounter ++ ) ;
133+ }
85134
86135 // Process rules: handle needsClassName flag and apply specificity
87136 const rulesToInsert = rules . map ( ( rule ) => {
@@ -147,11 +196,19 @@ export class StyleInjector {
147196
148197 // Store in registry
149198 registry . refCounts . set ( className , 1 ) ;
150- registry . rules . set ( className , ruleInfo ) ;
151199
152- // Also store by cache key if provided
153- if ( cacheKey ) {
154- registry . rules . set ( cacheKey , ruleInfo ) ;
200+ if ( isPreAllocated ) {
201+ // Update the existing placeholder entries with real rule info
202+ registry . rules . set ( className , ruleInfo ) ;
203+ if ( cacheKey ) {
204+ registry . rules . set ( cacheKey , ruleInfo ) ;
205+ }
206+ } else {
207+ // Store new entries
208+ registry . rules . set ( className , ruleInfo ) ;
209+ if ( cacheKey ) {
210+ registry . rules . set ( cacheKey , ruleInfo ) ;
211+ }
155212 }
156213
157214 // Update metrics
0 commit comments