From 0c56c0ab024f3f566eb1f9981b474431dd9ed759 Mon Sep 17 00:00:00 2001 From: Just One More Night Date: Thu, 17 Sep 2026 20:41:42 +0700 Subject: [PATCH] fix: avoid retaining detached style containers --- src/Dom/dynamicCSS.ts | 4 ++-- tests/dynamicCSS.test.tsx | 43 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/Dom/dynamicCSS.ts b/src/Dom/dynamicCSS.ts index 1abf1b7f..efe78f81 100644 --- a/src/Dom/dynamicCSS.ts +++ b/src/Dom/dynamicCSS.ts @@ -5,7 +5,7 @@ const APPEND_ORDER = 'data-rc-order'; const APPEND_PRIORITY = 'data-rc-priority'; const MARK_KEY = `rc-util-key`; -const containerCache = new Map(); +let containerCache = new WeakMap(); export type ContainerType = Element | ShadowRoot; export type Prepend = boolean | 'queue'; @@ -155,7 +155,7 @@ function syncRealContainer(container: ContainerType, option: Options) { * manually clear container cache to avoid global cache in unit testes */ export function clearContainerCache() { - containerCache.clear(); + containerCache = new WeakMap(); } export function updateCSS( diff --git a/tests/dynamicCSS.test.tsx b/tests/dynamicCSS.test.tsx index 0b3b5db2..77aff27e 100644 --- a/tests/dynamicCSS.test.tsx +++ b/tests/dynamicCSS.test.tsx @@ -235,4 +235,47 @@ describe('dynamicCSS', () => { expect(targetContainer.contains(SecondStyle)).toBeTruthy(); }); }); + describe('ShadowRoot and container cache', () => { + afterEach(() => { + clearContainerCache(); + const styles = document.querySelectorAll('style'); + styles.forEach(style => { + style.parentNode?.removeChild(style); + }); + }); + + it('injects, updates and clears styles within a ShadowRoot container', () => { + const host = document.createElement('div'); + document.body.appendChild(host); + const shadowRoot = host.attachShadow({ mode: 'open' }); + + const style = updateCSS('.shadow-rule { color: blue; }', 'shadow-key', { + attachTo: shadowRoot, + }); + + expect(shadowRoot.contains(style)).toBeTruthy(); + expect(document.head.querySelector('style')).toBeFalsy(); + expect(style.innerHTML).toEqual('.shadow-rule { color: blue; }'); + + // In-place update within shadowRoot + updateCSS('.shadow-rule { color: red; }', 'shadow-key', { + attachTo: shadowRoot, + }); + expect(shadowRoot.querySelectorAll('style')).toHaveLength(1); + expect(style.innerHTML).toEqual('.shadow-rule { color: red; }'); + + // clearContainerCache should reset WeakMap cache without breaking subsequent operations + clearContainerCache(); + updateCSS('.shadow-rule { color: green; }', 'shadow-key', { + attachTo: shadowRoot, + }); + expect(shadowRoot.querySelectorAll('style')).toHaveLength(1); + expect(style.innerHTML).toEqual('.shadow-rule { color: green; }'); + + removeCSS('shadow-key', { attachTo: shadowRoot }); + expect(shadowRoot.querySelector('style')).toBeFalsy(); + + document.body.removeChild(host); + }); + }); });