Skip to content

Commit 167f58f

Browse files
committed
fix(tasty): cleanup
1 parent df35834 commit 167f58f

File tree

2 files changed

+71
-21
lines changed

2 files changed

+71
-21
lines changed

src/tasty/debug.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,7 @@ export const tastyDebug = {
664664
const registry = (injector.instance as any)['sheetManager']?.getRegistry(
665665
root,
666666
);
667-
if (registry?.keyframesCache) {
667+
if (registry) {
668668
for (const entry of registry.keyframesCache.values()) {
669669
keyframes.push({
670670
name: entry.name,

src/tasty/injector/sheet-manager.ts

Lines changed: 70 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,64 @@ export class SheetManager {
352352
return this.insertRule(registry, flattenedRules, className, root);
353353
}
354354

355+
/**
356+
* Adjust rule indices after deletion to account for shifting
357+
*/
358+
private adjustIndicesAfterDeletion(
359+
registry: RootRegistry,
360+
sheetIndex: number,
361+
startIdx: number,
362+
endIdx: number,
363+
deleteCount: number,
364+
deletedRuleInfo: RuleInfo,
365+
): void {
366+
try {
367+
// Helper function to adjust a single RuleInfo
368+
const adjustRuleInfo = (info: RuleInfo): void => {
369+
if (info === deletedRuleInfo) return; // Skip the deleted rule
370+
if (info.sheetIndex !== sheetIndex) return; // Different sheet
371+
372+
const infoEnd = (info.endRuleIndex as number) ?? info.ruleIndex;
373+
374+
if (info.ruleIndex > endIdx) {
375+
// Rule is after deleted range - shift left
376+
info.ruleIndex = Math.max(0, info.ruleIndex - deleteCount);
377+
if (info.endRuleIndex != null) {
378+
info.endRuleIndex = Math.max(info.ruleIndex, infoEnd - deleteCount);
379+
}
380+
} else if (info.ruleIndex <= endIdx && infoEnd >= startIdx) {
381+
// Rule overlaps with deleted range (should not normally happen)
382+
// Clamp endRuleIndex to avoid pointing into deleted region
383+
if (info.endRuleIndex != null) {
384+
const newEnd = Math.max(info.ruleIndex, startIdx - 1);
385+
info.endRuleIndex = Math.max(info.ruleIndex, newEnd);
386+
}
387+
}
388+
};
389+
390+
// Adjust active rules
391+
for (const info of registry.rules.values()) {
392+
adjustRuleInfo(info);
393+
}
394+
395+
// Adjust unused rules (their ruleInfo references the same objects as in rules map)
396+
for (const unused of registry.unusedRules.values()) {
397+
adjustRuleInfo(unused.ruleInfo);
398+
}
399+
400+
// Adjust keyframes indices stored in cache
401+
for (const entry of registry.keyframesCache.values()) {
402+
const ki = entry.info as KeyframesInfo;
403+
if (ki.sheetIndex !== sheetIndex) continue;
404+
if (ki.ruleIndex > endIdx) {
405+
ki.ruleIndex = Math.max(0, ki.ruleIndex - deleteCount);
406+
}
407+
}
408+
} catch (_) {
409+
// Defensive: do not let index adjustments crash cleanup
410+
}
411+
}
412+
355413
/**
356414
* Delete a CSS rule from the sheet
357415
*/
@@ -384,13 +442,22 @@ export class SheetManager {
384442
);
385443

386444
if (Number.isFinite(startIdx) && endIdx >= startIdx) {
445+
const deleteCount = endIdx - startIdx + 1;
387446
for (let idx = endIdx; idx >= startIdx; idx--) {
388447
if (idx < 0 || idx >= styleSheet.cssRules.length) continue;
389448
styleSheet.deleteRule(idx);
390449
}
391-
sheet.ruleCount = Math.max(
392-
0,
393-
sheet.ruleCount - (endIdx - startIdx + 1),
450+
sheet.ruleCount = Math.max(0, sheet.ruleCount - deleteCount);
451+
452+
// After deletion, all subsequent rule indices shift left by deleteCount.
453+
// We must adjust stored indices for all other RuleInfo within the same sheet.
454+
this.adjustIndicesAfterDeletion(
455+
registry,
456+
ruleInfo.sheetIndex,
457+
startIdx,
458+
endIdx,
459+
deleteCount,
460+
ruleInfo,
394461
);
395462
}
396463
}
@@ -612,23 +679,6 @@ export class SheetManager {
612679
continue;
613680
}
614681

615-
// EXTRA SAFETY: If this class is present in the DOM, skip deletion.
616-
// This protects against refCount drift or races where the style is actually in use.
617-
try {
618-
const canQuery = rootNode && (rootNode as any).querySelector;
619-
if (canQuery) {
620-
const el = (rootNode as any).querySelector?.(`.${className}`);
621-
if (el) {
622-
// Class is currently used in DOM; do not delete its rules
623-
// Also remove it from unused list to avoid repeated checks
624-
registry.unusedRules.delete(className);
625-
continue;
626-
}
627-
}
628-
} catch (_) {
629-
// If querying fails for any reason, proceed with other safeguards only
630-
}
631-
632682
// Optional last-resort safety: ensure the sheet element still exists
633683
const sheetInfo = registry.sheets[ruleInfo.sheetIndex];
634684
if (!sheetInfo || !sheetInfo.sheet) {

0 commit comments

Comments
 (0)