From 997a8b454fe2f3c2ab55767dbf52b0b9b112cec4 Mon Sep 17 00:00:00 2001 From: Alexander Harding Date: Fri, 27 Jun 2025 17:15:46 -0500 Subject: [PATCH 1/3] fix(utils): single global stylesheet instance for performance --- src/utils/shadow-root.ts | 20 ++++++++++---------- src/utils/style.ts | 16 ++++++++++++++++ 2 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 src/utils/style.ts diff --git a/src/utils/shadow-root.ts b/src/utils/shadow-root.ts index 2de279f2604..85c00e087d4 100644 --- a/src/utils/shadow-root.ts +++ b/src/utils/shadow-root.ts @@ -1,9 +1,17 @@ import { BUILD } from '@app-data'; import { globalStyles } from '@app-globals'; -import { supportsConstructableStylesheets } from '@platform'; import { CMP_FLAGS } from '@utils'; import type * as d from '../declarations'; +import { createStyleSheetIfNeededAndSupported } from './style'; + +/** + * Create a single, shared global stylesheet for all shadow roots. + * + * This singleton avoids the performance and memory hit of + * creating a new CSSStyleSheet every time a shadow root is created. + */ +export const globalStyleSheet = createStyleSheetIfNeededAndSupported(globalStyles); export function createShadowRoot(this: HTMLElement, cmpMeta: d.ComponentRuntimeMeta) { const shadowRoot = BUILD.shadowDelegatesFocus @@ -13,13 +21,5 @@ export function createShadowRoot(this: HTMLElement, cmpMeta: d.ComponentRuntimeM }) : this.attachShadow({ mode: 'open' }); - /** - * If constructable stylesheets are supported, we can use them to - * add the global styles to the shadow root. - */ - if (supportsConstructableStylesheets) { - const sheet = new CSSStyleSheet(); - sheet.replaceSync(globalStyles); - shadowRoot.adoptedStyleSheets.push(sheet); - } + if (globalStyleSheet) shadowRoot.adoptedStyleSheets.push(globalStyleSheet); } diff --git a/src/utils/style.ts b/src/utils/style.ts new file mode 100644 index 00000000000..7b8e8204358 --- /dev/null +++ b/src/utils/style.ts @@ -0,0 +1,16 @@ +import { supportsConstructableStylesheets } from '@platform'; + +/** + * If (1) styles is not empty string, and (2) constructable stylesheets are supported, + * then make a stylesheet. + * + * @param styles - The styles to add to the stylesheet. If empty string, then no stylesheet is returned. + * @returns A stylesheet if it can be created, otherwise undefined. + */ +export function createStyleSheetIfNeededAndSupported(styles: string): CSSStyleSheet | undefined { + if (!styles || !supportsConstructableStylesheets) return undefined; + + const sheet = new CSSStyleSheet(); + sheet.replaceSync(styles); + return sheet; +} From 20d40461eb86b09d0e90452217575705bb015d8b Mon Sep 17 00:00:00 2001 From: Alexander Harding Date: Fri, 27 Jun 2025 21:16:07 -0500 Subject: [PATCH 2/3] lazy getter --- src/utils/shadow-root.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/utils/shadow-root.ts b/src/utils/shadow-root.ts index 85c00e087d4..938dc1de73f 100644 --- a/src/utils/shadow-root.ts +++ b/src/utils/shadow-root.ts @@ -10,8 +10,26 @@ import { createStyleSheetIfNeededAndSupported } from './style'; * * This singleton avoids the performance and memory hit of * creating a new CSSStyleSheet every time a shadow root is created. + * + * Lazy getter due to "ReferenceError: Cannot access 'globalStyles' + * before initialization" if setup is run at root level + * + * internal state: + * - undefined: not yet computed + * - null: computed and cached, but stylesheet not needed/supported + * - CSSStyleSheet: computed and cached + * + * @returns CSSStyleSheet | null */ -export const globalStyleSheet = createStyleSheetIfNeededAndSupported(globalStyles); +const getLazyGlobalStyleSheet = (() => { + let value: CSSStyleSheet | null | undefined; + + return () => { + if (value === undefined) value = createStyleSheetIfNeededAndSupported(globalStyles) ?? null; + + return value; + }; +})(); export function createShadowRoot(this: HTMLElement, cmpMeta: d.ComponentRuntimeMeta) { const shadowRoot = BUILD.shadowDelegatesFocus @@ -21,5 +39,6 @@ export function createShadowRoot(this: HTMLElement, cmpMeta: d.ComponentRuntimeM }) : this.attachShadow({ mode: 'open' }); + const globalStyleSheet = getLazyGlobalStyleSheet(); if (globalStyleSheet) shadowRoot.adoptedStyleSheets.push(globalStyleSheet); } From 1414c08e6fd1e523f50310b40f5eeae6f39367f0 Mon Sep 17 00:00:00 2001 From: Alexander Harding Date: Fri, 27 Jun 2025 22:06:59 -0500 Subject: [PATCH 3/3] simplify --- src/utils/shadow-root.ts | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/src/utils/shadow-root.ts b/src/utils/shadow-root.ts index 938dc1de73f..c8c69867063 100644 --- a/src/utils/shadow-root.ts +++ b/src/utils/shadow-root.ts @@ -5,31 +5,7 @@ import { CMP_FLAGS } from '@utils'; import type * as d from '../declarations'; import { createStyleSheetIfNeededAndSupported } from './style'; -/** - * Create a single, shared global stylesheet for all shadow roots. - * - * This singleton avoids the performance and memory hit of - * creating a new CSSStyleSheet every time a shadow root is created. - * - * Lazy getter due to "ReferenceError: Cannot access 'globalStyles' - * before initialization" if setup is run at root level - * - * internal state: - * - undefined: not yet computed - * - null: computed and cached, but stylesheet not needed/supported - * - CSSStyleSheet: computed and cached - * - * @returns CSSStyleSheet | null - */ -const getLazyGlobalStyleSheet = (() => { - let value: CSSStyleSheet | null | undefined; - - return () => { - if (value === undefined) value = createStyleSheetIfNeededAndSupported(globalStyles) ?? null; - - return value; - }; -})(); +let globalStyleSheet: CSSStyleSheet | null | undefined; export function createShadowRoot(this: HTMLElement, cmpMeta: d.ComponentRuntimeMeta) { const shadowRoot = BUILD.shadowDelegatesFocus @@ -39,6 +15,9 @@ export function createShadowRoot(this: HTMLElement, cmpMeta: d.ComponentRuntimeM }) : this.attachShadow({ mode: 'open' }); - const globalStyleSheet = getLazyGlobalStyleSheet(); + // Initialize if undefined, set to CSSStyleSheet or null + if (globalStyleSheet === undefined) globalStyleSheet = createStyleSheetIfNeededAndSupported(globalStyles) ?? null; + + // Use initialized global stylesheet if available if (globalStyleSheet) shadowRoot.adoptedStyleSheets.push(globalStyleSheet); }