From 7f45919c9e58dcc601bb848c3364042c09d6897e Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Mon, 30 Dec 2024 22:46:48 -0800 Subject: [PATCH 01/31] fix(runtime): clear up detached hostRefs and rootAppliedStyles --- src/runtime/disconnected-callback.ts | 20 ++++++++++++++++++-- src/runtime/styles.ts | 2 +- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/runtime/disconnected-callback.ts b/src/runtime/disconnected-callback.ts index 137142cddee..7452bb1ef1b 100644 --- a/src/runtime/disconnected-callback.ts +++ b/src/runtime/disconnected-callback.ts @@ -3,14 +3,16 @@ import { getHostRef, plt } from '@platform'; import type * as d from '../declarations'; import { PLATFORM_FLAGS } from './runtime-constants'; +import { rootAppliedStyles } from './styles'; import { safeCall } from './update-component'; const disconnectInstance = (instance: any) => { + const callbackResult: unknown[] = []; if (BUILD.lazyLoad && BUILD.disconnectedCallback) { - safeCall(instance, 'disconnectedCallback'); + callbackResult.push(safeCall(instance, 'disconnectedCallback')); } if (BUILD.cmpDidUnload) { - safeCall(instance, 'componentDidUnload'); + callbackResult.push(safeCall(instance, 'componentDidUnload')); } }; @@ -33,4 +35,18 @@ export const disconnectedCallback = async (elm: d.HostElement) => { hostRef.$onReadyPromise$.then(() => disconnectInstance(hostRef.$lazyInstance$)); } } + + /** + * Remove the element from the `rootAppliedStyles` WeakMap + */ + if(rootAppliedStyles.has(elm)) { + rootAppliedStyles.delete(elm); + } + + /** + * Remove the shadow root from the `rootAppliedStyles` WeakMap + */ + if(elm.shadowRoot && rootAppliedStyles.has(elm.shadowRoot as unknown as Element)) { + rootAppliedStyles.delete(elm.shadowRoot as unknown as Element); + } }; diff --git a/src/runtime/styles.ts b/src/runtime/styles.ts index 3b497c9d00f..579f427b0e1 100644 --- a/src/runtime/styles.ts +++ b/src/runtime/styles.ts @@ -6,7 +6,7 @@ import type * as d from '../declarations'; import { createTime } from './profile'; import { HYDRATED_STYLE_ID, NODE_TYPE, SLOT_FB_CSS } from './runtime-constants'; -const rootAppliedStyles: d.RootAppliedStyleMap = /*@__PURE__*/ new WeakMap(); +export const rootAppliedStyles: d.RootAppliedStyleMap = /*@__PURE__*/ new WeakMap(); /** * Register the styles for a component by creating a stylesheet and then From a0c07cba49d2e40c347bcff4ce14f7b67c12ee8d Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Mon, 30 Dec 2024 23:04:47 -0800 Subject: [PATCH 02/31] fix build --- src/hydrate/platform/index.ts | 2 ++ src/testing/platform/index.ts | 2 +- src/testing/platform/testing-host-ref.ts | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/hydrate/platform/index.ts b/src/hydrate/platform/index.ts index e4467db21d0..be3d00b6517 100644 --- a/src/hydrate/platform/index.ts +++ b/src/hydrate/platform/index.ts @@ -148,6 +148,8 @@ export const registerHost = (elm: d.HostElement, cmpMeta: d.ComponentRuntimeMeta return hostRefs.set(elm, hostRef); }; +export const hostRefCleanup = (): void => {}; + export const Build: d.UserBuildConditionals = { isDev: false, isBrowser: false, diff --git a/src/testing/platform/index.ts b/src/testing/platform/index.ts index 5d8e4074c41..76c5d7432d0 100644 --- a/src/testing/platform/index.ts +++ b/src/testing/platform/index.ts @@ -1,6 +1,6 @@ export { Build } from './testing-build'; export { modeResolutionChain, styles } from './testing-constants'; -export { getHostRef, registerHost, registerInstance } from './testing-host-ref'; +export { getHostRef, hostRefCleanup, registerHost, registerInstance } from './testing-host-ref'; export { consoleDevError, consoleDevInfo, consoleDevWarn, consoleError, setErrorHandler } from './testing-log'; export { isMemberInElement, diff --git a/src/testing/platform/testing-host-ref.ts b/src/testing/platform/testing-host-ref.ts index 6adcc8d9a3c..d02152b3533 100644 --- a/src/testing/platform/testing-host-ref.ts +++ b/src/testing/platform/testing-host-ref.ts @@ -58,3 +58,8 @@ export const registerHost = (elm: d.HostElement, cmpMeta: d.ComponentRuntimeMeta elm['s-rc'] = []; hostRefs.set(elm, hostRef); }; + +/** + * Ignore potential detached nodes when testing as we often enough reload the page. + */ +export const hostRefCleanup = (): void => {}; From 22a6adead627614c6257082de2bfb27abc8c6347 Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Mon, 30 Dec 2024 23:05:04 -0800 Subject: [PATCH 03/31] prettier --- src/client/client-host-ref.ts | 6 ++++++ src/runtime/disconnected-callback.ts | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/client/client-host-ref.ts b/src/client/client-host-ref.ts index e96c26a02ca..591afa6aa8e 100644 --- a/src/client/client-host-ref.ts +++ b/src/client/client-host-ref.ts @@ -38,10 +38,16 @@ export const getHostRef = (ref: d.RuntimeRef): d.HostRef | undefined => hostRefs * @param hostRef that instances `HostRef` object */ export const registerInstance = (lazyInstance: any, hostRef: d.HostRef) => { +<<<<<<< HEAD hostRefs.set((hostRef.$lazyInstance$ = lazyInstance), hostRef); if (BUILD.modernPropertyDecls && (BUILD.state || BUILD.prop)) { reWireGetterSetter(lazyInstance, hostRef); } +======= + hostRef.$lazyInstance$ = lazyInstance; + hostRefKeys.push(lazyInstance); + return hostRefs.set(lazyInstance, hostRef); +>>>>>>> fb8335e25 (prettier) }; /** diff --git a/src/runtime/disconnected-callback.ts b/src/runtime/disconnected-callback.ts index 7452bb1ef1b..894d4691f67 100644 --- a/src/runtime/disconnected-callback.ts +++ b/src/runtime/disconnected-callback.ts @@ -39,14 +39,14 @@ export const disconnectedCallback = async (elm: d.HostElement) => { /** * Remove the element from the `rootAppliedStyles` WeakMap */ - if(rootAppliedStyles.has(elm)) { + if (rootAppliedStyles.has(elm)) { rootAppliedStyles.delete(elm); } /** * Remove the shadow root from the `rootAppliedStyles` WeakMap */ - if(elm.shadowRoot && rootAppliedStyles.has(elm.shadowRoot as unknown as Element)) { + if (elm.shadowRoot && rootAppliedStyles.has(elm.shadowRoot as unknown as Element)) { rootAppliedStyles.delete(elm.shadowRoot as unknown as Element); } }; From 1d25bf80e4368359c0b9ed1afacb0cee67663769 Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Mon, 6 Jan 2025 18:43:13 -0800 Subject: [PATCH 04/31] fix after rebase --- src/client/client-host-ref.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/client/client-host-ref.ts b/src/client/client-host-ref.ts index 591afa6aa8e..e96c26a02ca 100644 --- a/src/client/client-host-ref.ts +++ b/src/client/client-host-ref.ts @@ -38,16 +38,10 @@ export const getHostRef = (ref: d.RuntimeRef): d.HostRef | undefined => hostRefs * @param hostRef that instances `HostRef` object */ export const registerInstance = (lazyInstance: any, hostRef: d.HostRef) => { -<<<<<<< HEAD hostRefs.set((hostRef.$lazyInstance$ = lazyInstance), hostRef); if (BUILD.modernPropertyDecls && (BUILD.state || BUILD.prop)) { reWireGetterSetter(lazyInstance, hostRef); } -======= - hostRef.$lazyInstance$ = lazyInstance; - hostRefKeys.push(lazyInstance); - return hostRefs.set(lazyInstance, hostRef); ->>>>>>> fb8335e25 (prettier) }; /** From f7447bcc1083a6cca964a6158315cc1511a16e7d Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Mon, 6 Jan 2025 18:44:02 -0800 Subject: [PATCH 05/31] more reverts --- src/hydrate/platform/index.ts | 2 -- src/runtime/disconnected-callback.ts | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/hydrate/platform/index.ts b/src/hydrate/platform/index.ts index be3d00b6517..e4467db21d0 100644 --- a/src/hydrate/platform/index.ts +++ b/src/hydrate/platform/index.ts @@ -148,8 +148,6 @@ export const registerHost = (elm: d.HostElement, cmpMeta: d.ComponentRuntimeMeta return hostRefs.set(elm, hostRef); }; -export const hostRefCleanup = (): void => {}; - export const Build: d.UserBuildConditionals = { isDev: false, isBrowser: false, diff --git a/src/runtime/disconnected-callback.ts b/src/runtime/disconnected-callback.ts index 894d4691f67..634da2a0d4d 100644 --- a/src/runtime/disconnected-callback.ts +++ b/src/runtime/disconnected-callback.ts @@ -7,12 +7,11 @@ import { rootAppliedStyles } from './styles'; import { safeCall } from './update-component'; const disconnectInstance = (instance: any) => { - const callbackResult: unknown[] = []; if (BUILD.lazyLoad && BUILD.disconnectedCallback) { - callbackResult.push(safeCall(instance, 'disconnectedCallback')); + safeCall(instance, 'disconnectedCallback'); } if (BUILD.cmpDidUnload) { - callbackResult.push(safeCall(instance, 'componentDidUnload')); + safeCall(instance, 'componentDidUnload'); } }; From b78a4ec4dfa91ff1d8bbaa950a5b667f4f58722c Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Mon, 6 Jan 2025 18:44:38 -0800 Subject: [PATCH 06/31] more cleanups --- src/testing/platform/index.ts | 2 +- src/testing/platform/testing-host-ref.ts | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/testing/platform/index.ts b/src/testing/platform/index.ts index 76c5d7432d0..5d8e4074c41 100644 --- a/src/testing/platform/index.ts +++ b/src/testing/platform/index.ts @@ -1,6 +1,6 @@ export { Build } from './testing-build'; export { modeResolutionChain, styles } from './testing-constants'; -export { getHostRef, hostRefCleanup, registerHost, registerInstance } from './testing-host-ref'; +export { getHostRef, registerHost, registerInstance } from './testing-host-ref'; export { consoleDevError, consoleDevInfo, consoleDevWarn, consoleError, setErrorHandler } from './testing-log'; export { isMemberInElement, diff --git a/src/testing/platform/testing-host-ref.ts b/src/testing/platform/testing-host-ref.ts index d02152b3533..6adcc8d9a3c 100644 --- a/src/testing/platform/testing-host-ref.ts +++ b/src/testing/platform/testing-host-ref.ts @@ -58,8 +58,3 @@ export const registerHost = (elm: d.HostElement, cmpMeta: d.ComponentRuntimeMeta elm['s-rc'] = []; hostRefs.set(elm, hostRef); }; - -/** - * Ignore potential detached nodes when testing as we often enough reload the page. - */ -export const hostRefCleanup = (): void => {}; From 709e57d70257953f2181706aed70326c3d0ccaf5 Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Mon, 6 Jan 2025 20:12:41 -0800 Subject: [PATCH 07/31] remove hostrefs manually instead of cleanups --- src/client/client-host-ref.ts | 11 +++++++++++ src/runtime/bootstrap-lazy.ts | 10 +++++++++- src/runtime/vdom/update-element.ts | 5 ++--- src/utils/constants.ts | 6 ------ 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/client/client-host-ref.ts b/src/client/client-host-ref.ts index e96c26a02ca..911af7f9353 100644 --- a/src/client/client-host-ref.ts +++ b/src/client/client-host-ref.ts @@ -22,6 +22,17 @@ const hostRefs: WeakMap = /*@__PURE__*/ BUILD.hotModule ? ((window as any).__STENCIL_HOSTREFS__ ||= new WeakMap()) : new WeakMap(); +/** + * Given a {@link d.RuntimeRef} remove the corresponding {@link d.HostRef} from + * the {@link hostRefs} WeakMap. This is necessary when calling `registerInstance` + * within the constructor of a lazy-loaded component. These references aren't + * removed automatically, hence we have to do it manually. + * + * @param ref the runtime ref of interest + * @returns — true if the element was successfully removed, or false if it was not present. + */ +export const deleteHostRef = (ref: d.RuntimeRef) => hostRefs.delete(ref); + /** * Given a {@link d.RuntimeRef} retrieve the corresponding {@link d.HostRef} * diff --git a/src/runtime/bootstrap-lazy.ts b/src/runtime/bootstrap-lazy.ts index 7bc14684981..8d52c3c2f95 100644 --- a/src/runtime/bootstrap-lazy.ts +++ b/src/runtime/bootstrap-lazy.ts @@ -1,5 +1,5 @@ import { BUILD } from '@app-data'; -import { doc, getHostRef, plt, registerHost, supportsShadow, win } from '@platform'; +import { deleteHostRef, doc, getHostRef, plt, registerHost, supportsShadow, win } from '@platform'; import { addHostEventListeners } from '@runtime'; import { CMP_FLAGS, queryNonceMetaTagContent } from '@utils'; @@ -159,6 +159,14 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. disconnectedCallback() { plt.jmp(() => disconnectedCallback(this)); + + /** + * Manually remove references from `hostRefs` to prevent memory leaks. + */ + setTimeout(() => { + deleteHostRef(getHostRef(this).$lazyInstance$); + deleteHostRef(this) + }, 0) } componentOnReady() { diff --git a/src/runtime/vdom/update-element.ts b/src/runtime/vdom/update-element.ts index 7487a62c02f..5cea42cfe8d 100644 --- a/src/runtime/vdom/update-element.ts +++ b/src/runtime/vdom/update-element.ts @@ -1,5 +1,4 @@ import { BUILD } from '@app-data'; -import { EMPTY_OBJ } from '@utils'; import type * as d from '../../declarations'; import { NODE_TYPE } from '../runtime-constants'; @@ -24,8 +23,8 @@ export const updateElement = (oldVnode: d.VNode | null, newVnode: d.VNode, isSvg newVnode.$elm$.nodeType === NODE_TYPE.DocumentFragment && newVnode.$elm$.host ? newVnode.$elm$.host : (newVnode.$elm$ as any); - const oldVnodeAttrs = (oldVnode && oldVnode.$attrs$) || EMPTY_OBJ; - const newVnodeAttrs = newVnode.$attrs$ || EMPTY_OBJ; + const oldVnodeAttrs = (oldVnode && oldVnode.$attrs$) || {}; + const newVnodeAttrs = newVnode.$attrs$ || {}; if (BUILD.updatable) { // remove attributes no longer present on the vnode by setting them to undefined diff --git a/src/utils/constants.ts b/src/utils/constants.ts index d8b9c7df291..b2bf0febaf9 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -120,12 +120,6 @@ export const enum CMP_FLAGS { */ export const DEFAULT_STYLE_MODE = '$'; -/** - * Reusable empty obj/array - * Don't add values to these!! - */ -export const EMPTY_OBJ: Record = {}; - /** * Namespaces */ From 5b58ddb67ea081e70f38cd34e087b7855a4b7314 Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Mon, 6 Jan 2025 20:23:10 -0800 Subject: [PATCH 08/31] prettier --- src/runtime/bootstrap-lazy.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime/bootstrap-lazy.ts b/src/runtime/bootstrap-lazy.ts index 8d52c3c2f95..cb96476b05c 100644 --- a/src/runtime/bootstrap-lazy.ts +++ b/src/runtime/bootstrap-lazy.ts @@ -165,8 +165,8 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. */ setTimeout(() => { deleteHostRef(getHostRef(this).$lazyInstance$); - deleteHostRef(this) - }, 0) + deleteHostRef(this); + }, 0); } componentOnReady() { From 155010e67747ca8f4940448294602428166efaf2 Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Mon, 6 Jan 2025 20:27:10 -0800 Subject: [PATCH 09/31] fix build --- src/hydrate/platform/index.ts | 1 + src/testing/platform/index.ts | 2 +- src/testing/platform/testing-host-ref.ts | 11 +++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/hydrate/platform/index.ts b/src/hydrate/platform/index.ts index e4467db21d0..e2377e7f37d 100644 --- a/src/hydrate/platform/index.ts +++ b/src/hydrate/platform/index.ts @@ -128,6 +128,7 @@ export const supportsConstructableStylesheets = false; const hostRefs: WeakMap = new WeakMap(); +export const deleteHostRef = (ref: d.RuntimeRef) => hostRefs.delete(ref); export const getHostRef = (ref: d.RuntimeRef) => hostRefs.get(ref); export const registerInstance = (lazyInstance: any, hostRef: d.HostRef) => diff --git a/src/testing/platform/index.ts b/src/testing/platform/index.ts index 5d8e4074c41..4233ba07c99 100644 --- a/src/testing/platform/index.ts +++ b/src/testing/platform/index.ts @@ -1,6 +1,6 @@ export { Build } from './testing-build'; export { modeResolutionChain, styles } from './testing-constants'; -export { getHostRef, registerHost, registerInstance } from './testing-host-ref'; +export { deleteHostRef, getHostRef, registerHost, registerInstance } from './testing-host-ref'; export { consoleDevError, consoleDevInfo, consoleDevWarn, consoleError, setErrorHandler } from './testing-log'; export { isMemberInElement, diff --git a/src/testing/platform/testing-host-ref.ts b/src/testing/platform/testing-host-ref.ts index 6adcc8d9a3c..a984adf0e6e 100644 --- a/src/testing/platform/testing-host-ref.ts +++ b/src/testing/platform/testing-host-ref.ts @@ -11,6 +11,17 @@ export const getHostRef = (elm: d.RuntimeRef | undefined): d.HostRef | undefined return hostRefs.get(elm); }; +/** + * Given a {@link d.RuntimeRef} remove the corresponding {@link d.HostRef} from + * the {@link hostRefs} WeakMap. This is necessary when calling `registerInstance` + * within the constructor of a lazy-loaded component. These references aren't + * removed automatically, hence we have to do it manually. + * + * @param ref the runtime ref of interest + * @returns — true if the element was successfully removed, or false if it was not present. + */ +export const deleteHostRef = (ref: d.RuntimeRef) => hostRefs.delete(ref); + /** * Add the provided `hostRef` instance to the global {@link hostRefs} map, using the provided `lazyInstance` as a key. * @param lazyInstance a Stencil component instance From 15953cf60c14abc4b85afecc5ea572efedfdef0d Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Mon, 6 Jan 2025 22:12:52 -0800 Subject: [PATCH 10/31] found the leak --- src/runtime/bootstrap-lazy.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/runtime/bootstrap-lazy.ts b/src/runtime/bootstrap-lazy.ts index cb96476b05c..4639208ecd2 100644 --- a/src/runtime/bootstrap-lazy.ts +++ b/src/runtime/bootstrap-lazy.ts @@ -164,7 +164,8 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. * Manually remove references from `hostRefs` to prevent memory leaks. */ setTimeout(() => { - deleteHostRef(getHostRef(this).$lazyInstance$); + const hostRef = getHostRef(this); + delete hostRef.$vnode$; deleteHostRef(this); }, 0); } From 12e4c8f8a5a4d31d7a31681afcf9d2fadb0c4d15 Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Mon, 6 Jan 2025 22:51:05 -0800 Subject: [PATCH 11/31] found it without errors --- src/client/client-host-ref.ts | 11 ----------- src/hydrate/platform/index.ts | 1 - src/runtime/bootstrap-lazy.ts | 19 +++++++++++++++---- src/testing/platform/index.ts | 2 +- src/testing/platform/testing-host-ref.ts | 11 ----------- 5 files changed, 16 insertions(+), 28 deletions(-) diff --git a/src/client/client-host-ref.ts b/src/client/client-host-ref.ts index 911af7f9353..e96c26a02ca 100644 --- a/src/client/client-host-ref.ts +++ b/src/client/client-host-ref.ts @@ -22,17 +22,6 @@ const hostRefs: WeakMap = /*@__PURE__*/ BUILD.hotModule ? ((window as any).__STENCIL_HOSTREFS__ ||= new WeakMap()) : new WeakMap(); -/** - * Given a {@link d.RuntimeRef} remove the corresponding {@link d.HostRef} from - * the {@link hostRefs} WeakMap. This is necessary when calling `registerInstance` - * within the constructor of a lazy-loaded component. These references aren't - * removed automatically, hence we have to do it manually. - * - * @param ref the runtime ref of interest - * @returns — true if the element was successfully removed, or false if it was not present. - */ -export const deleteHostRef = (ref: d.RuntimeRef) => hostRefs.delete(ref); - /** * Given a {@link d.RuntimeRef} retrieve the corresponding {@link d.HostRef} * diff --git a/src/hydrate/platform/index.ts b/src/hydrate/platform/index.ts index e2377e7f37d..e4467db21d0 100644 --- a/src/hydrate/platform/index.ts +++ b/src/hydrate/platform/index.ts @@ -128,7 +128,6 @@ export const supportsConstructableStylesheets = false; const hostRefs: WeakMap = new WeakMap(); -export const deleteHostRef = (ref: d.RuntimeRef) => hostRefs.delete(ref); export const getHostRef = (ref: d.RuntimeRef) => hostRefs.get(ref); export const registerInstance = (lazyInstance: any, hostRef: d.HostRef) => diff --git a/src/runtime/bootstrap-lazy.ts b/src/runtime/bootstrap-lazy.ts index 4639208ecd2..dc45bb16d2b 100644 --- a/src/runtime/bootstrap-lazy.ts +++ b/src/runtime/bootstrap-lazy.ts @@ -1,5 +1,5 @@ import { BUILD } from '@app-data'; -import { deleteHostRef, doc, getHostRef, plt, registerHost, supportsShadow, win } from '@platform'; +import { doc, getHostRef, plt, registerHost, supportsShadow, win } from '@platform'; import { addHostEventListeners } from '@runtime'; import { CMP_FLAGS, queryNonceMetaTagContent } from '@utils'; @@ -165,9 +165,11 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. */ setTimeout(() => { const hostRef = getHostRef(this); - delete hostRef.$vnode$; - deleteHostRef(this); - }, 0); + if (hostRef?.$vnode$) { + hostRef.$vnode$.$children$ = (hostRef.$vnode$.$children$ || []) + .filter((child) => isNodeAttached(child.$elm$)); + } + }, 100); } componentOnReady() { @@ -268,3 +270,12 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. // Fallback appLoad event endBootstrap(); }; + +/** + * Check if a node is attached to the DOM + * @param node an element or document + * @returns true if the node is attached to the DOM + */ +function isNodeAttached(node: Node): boolean { + return node === document || node === document.documentElement || document.contains(node); +} diff --git a/src/testing/platform/index.ts b/src/testing/platform/index.ts index 4233ba07c99..5d8e4074c41 100644 --- a/src/testing/platform/index.ts +++ b/src/testing/platform/index.ts @@ -1,6 +1,6 @@ export { Build } from './testing-build'; export { modeResolutionChain, styles } from './testing-constants'; -export { deleteHostRef, getHostRef, registerHost, registerInstance } from './testing-host-ref'; +export { getHostRef, registerHost, registerInstance } from './testing-host-ref'; export { consoleDevError, consoleDevInfo, consoleDevWarn, consoleError, setErrorHandler } from './testing-log'; export { isMemberInElement, diff --git a/src/testing/platform/testing-host-ref.ts b/src/testing/platform/testing-host-ref.ts index a984adf0e6e..6adcc8d9a3c 100644 --- a/src/testing/platform/testing-host-ref.ts +++ b/src/testing/platform/testing-host-ref.ts @@ -11,17 +11,6 @@ export const getHostRef = (elm: d.RuntimeRef | undefined): d.HostRef | undefined return hostRefs.get(elm); }; -/** - * Given a {@link d.RuntimeRef} remove the corresponding {@link d.HostRef} from - * the {@link hostRefs} WeakMap. This is necessary when calling `registerInstance` - * within the constructor of a lazy-loaded component. These references aren't - * removed automatically, hence we have to do it manually. - * - * @param ref the runtime ref of interest - * @returns — true if the element was successfully removed, or false if it was not present. - */ -export const deleteHostRef = (ref: d.RuntimeRef) => hostRefs.delete(ref); - /** * Add the provided `hostRef` instance to the global {@link hostRefs} map, using the provided `lazyInstance` as a key. * @param lazyInstance a Stencil component instance From a16695938d5e766116f54a030c9a36ec24757327 Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Mon, 6 Jan 2025 23:25:26 -0800 Subject: [PATCH 12/31] delete the right element --- src/runtime/bootstrap-lazy.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/runtime/bootstrap-lazy.ts b/src/runtime/bootstrap-lazy.ts index dc45bb16d2b..7cb618b5bb0 100644 --- a/src/runtime/bootstrap-lazy.ts +++ b/src/runtime/bootstrap-lazy.ts @@ -16,7 +16,7 @@ import { import { hmrStart } from './hmr-component'; import { createTime, installDevTools } from './profile'; import { proxyComponent } from './proxy-component'; -import { HYDRATED_CSS, PLATFORM_FLAGS, PROXY_FLAGS, SLOT_FB_CSS } from './runtime-constants'; +import { HYDRATED_CSS, NODE_TYPE, PLATFORM_FLAGS, PROXY_FLAGS, SLOT_FB_CSS } from './runtime-constants'; import { appDidLoad } from './update-component'; export { setNonce } from '@platform'; @@ -161,15 +161,15 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. plt.jmp(() => disconnectedCallback(this)); /** - * Manually remove references from `hostRefs` to prevent memory leaks. + * Clear up references within the `$vnode$` object to the DOM + * node that was removed. This is necessary to ensure that these + * references used as keys in the `hostRef` object can be properly + * garbage collected. */ - setTimeout(() => { - const hostRef = getHostRef(this); - if (hostRef?.$vnode$) { - hostRef.$vnode$.$children$ = (hostRef.$vnode$.$children$ || []) - .filter((child) => isNodeAttached(child.$elm$)); - } - }, 100); + const hostRef = getHostRef(this); + if (hostRef?.$vnode$?.$elm$ && hostRef.$vnode$.$elm$.nodeType === NODE_TYPE.ElementNode && !isNodeAttached(hostRef.$vnode$.$elm$)) { + delete hostRef.$vnode$.$elm$; + } } componentOnReady() { From 04fbc06aec2a3707f7169e74e006186b4561500f Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Mon, 6 Jan 2025 23:29:37 -0800 Subject: [PATCH 13/31] clean up after animation frame --- src/runtime/bootstrap-lazy.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/runtime/bootstrap-lazy.ts b/src/runtime/bootstrap-lazy.ts index 7cb618b5bb0..eeef9109a7d 100644 --- a/src/runtime/bootstrap-lazy.ts +++ b/src/runtime/bootstrap-lazy.ts @@ -166,10 +166,12 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. * references used as keys in the `hostRef` object can be properly * garbage collected. */ - const hostRef = getHostRef(this); - if (hostRef?.$vnode$?.$elm$ && hostRef.$vnode$.$elm$.nodeType === NODE_TYPE.ElementNode && !isNodeAttached(hostRef.$vnode$.$elm$)) { - delete hostRef.$vnode$.$elm$; - } + plt.raf(() => { + const hostRef = getHostRef(this); + if (hostRef?.$vnode$?.$elm$ && hostRef.$vnode$.$elm$.nodeType === NODE_TYPE.ElementNode && !isNodeAttached(hostRef.$vnode$.$elm$)) { + delete hostRef.$vnode$.$elm$; + } + }) } componentOnReady() { From e5973200760f163fe215bbf0206464c7893c4fce Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Mon, 6 Jan 2025 23:46:11 -0800 Subject: [PATCH 14/31] prettier --- src/runtime/bootstrap-lazy.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/runtime/bootstrap-lazy.ts b/src/runtime/bootstrap-lazy.ts index eeef9109a7d..5804352f277 100644 --- a/src/runtime/bootstrap-lazy.ts +++ b/src/runtime/bootstrap-lazy.ts @@ -168,10 +168,14 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. */ plt.raf(() => { const hostRef = getHostRef(this); - if (hostRef?.$vnode$?.$elm$ && hostRef.$vnode$.$elm$.nodeType === NODE_TYPE.ElementNode && !isNodeAttached(hostRef.$vnode$.$elm$)) { + if ( + hostRef?.$vnode$?.$elm$ && + hostRef.$vnode$.$elm$.nodeType === NODE_TYPE.ElementNode && + !isNodeAttached(hostRef.$vnode$.$elm$) + ) { delete hostRef.$vnode$.$elm$; } - }) + }); } componentOnReady() { From e1053d2b8ab2f34d11b83c47ab826807a4d2d752 Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Tue, 7 Jan 2025 08:11:15 -0800 Subject: [PATCH 15/31] use isConnected to check whether node is in DOM --- src/runtime/bootstrap-lazy.ts | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/runtime/bootstrap-lazy.ts b/src/runtime/bootstrap-lazy.ts index 5804352f277..948a6d171ce 100644 --- a/src/runtime/bootstrap-lazy.ts +++ b/src/runtime/bootstrap-lazy.ts @@ -16,7 +16,7 @@ import { import { hmrStart } from './hmr-component'; import { createTime, installDevTools } from './profile'; import { proxyComponent } from './proxy-component'; -import { HYDRATED_CSS, NODE_TYPE, PLATFORM_FLAGS, PROXY_FLAGS, SLOT_FB_CSS } from './runtime-constants'; +import { HYDRATED_CSS, PLATFORM_FLAGS, PROXY_FLAGS, SLOT_FB_CSS } from './runtime-constants'; import { appDidLoad } from './update-component'; export { setNonce } from '@platform'; @@ -169,9 +169,8 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. plt.raf(() => { const hostRef = getHostRef(this); if ( - hostRef?.$vnode$?.$elm$ && - hostRef.$vnode$.$elm$.nodeType === NODE_TYPE.ElementNode && - !isNodeAttached(hostRef.$vnode$.$elm$) + hostRef?.$vnode$?.$elm$ instanceof Node && + hostRef.$vnode$.$elm$.isConnected ) { delete hostRef.$vnode$.$elm$; } @@ -276,12 +275,3 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. // Fallback appLoad event endBootstrap(); }; - -/** - * Check if a node is attached to the DOM - * @param node an element or document - * @returns true if the node is attached to the DOM - */ -function isNodeAttached(node: Node): boolean { - return node === document || node === document.documentElement || document.contains(node); -} From 224b3ea73f395484332bd9e6bac53b0f1954fe4e Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Tue, 7 Jan 2025 08:29:53 -0800 Subject: [PATCH 16/31] prettier --- src/runtime/bootstrap-lazy.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/runtime/bootstrap-lazy.ts b/src/runtime/bootstrap-lazy.ts index 948a6d171ce..3cfccf0ca5c 100644 --- a/src/runtime/bootstrap-lazy.ts +++ b/src/runtime/bootstrap-lazy.ts @@ -168,10 +168,7 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. */ plt.raf(() => { const hostRef = getHostRef(this); - if ( - hostRef?.$vnode$?.$elm$ instanceof Node && - hostRef.$vnode$.$elm$.isConnected - ) { + if (hostRef?.$vnode$?.$elm$ instanceof Node && hostRef.$vnode$.$elm$.isConnected) { delete hostRef.$vnode$.$elm$; } }); From 900ca118786a69524a5f9ce94e35b2f6e2467133 Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Tue, 7 Jan 2025 15:32:29 -0800 Subject: [PATCH 17/31] solve leak for dist custom elements --- src/client/client-host-ref.ts | 9 +++++++++ src/hydrate/platform/index.ts | 1 + src/runtime/bootstrap-custom-element.ts | 15 ++++++++++++++- src/testing/platform/index.ts | 2 +- src/testing/platform/testing-host-ref.ts | 9 +++++++++ 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/client/client-host-ref.ts b/src/client/client-host-ref.ts index e96c26a02ca..bb098cc7987 100644 --- a/src/client/client-host-ref.ts +++ b/src/client/client-host-ref.ts @@ -22,6 +22,15 @@ const hostRefs: WeakMap = /*@__PURE__*/ BUILD.hotModule ? ((window as any).__STENCIL_HOSTREFS__ ||= new WeakMap()) : new WeakMap(); +/** + * Given a {@link d.RuntimeRef} remove the corresponding {@link d.HostRef} from + * the {@link hostRefs} WeakMap. + * + * @param ref the runtime ref of interest + * @returns — true if the element was successfully removed, or false if it was not present. + */ +export const deleteHostRef = (ref: d.RuntimeRef) => hostRefs.delete(ref); + /** * Given a {@link d.RuntimeRef} retrieve the corresponding {@link d.HostRef} * diff --git a/src/hydrate/platform/index.ts b/src/hydrate/platform/index.ts index e4467db21d0..fcf8122342c 100644 --- a/src/hydrate/platform/index.ts +++ b/src/hydrate/platform/index.ts @@ -129,6 +129,7 @@ export const supportsConstructableStylesheets = false; const hostRefs: WeakMap = new WeakMap(); export const getHostRef = (ref: d.RuntimeRef) => hostRefs.get(ref); +export const deleteHostRef = (ref: d.RuntimeRef) => hostRefs.delete(ref); export const registerInstance = (lazyInstance: any, hostRef: d.HostRef) => hostRefs.set((hostRef.$lazyInstance$ = lazyInstance), hostRef); diff --git a/src/runtime/bootstrap-custom-element.ts b/src/runtime/bootstrap-custom-element.ts index aeff2f4818e..f42059e5512 100644 --- a/src/runtime/bootstrap-custom-element.ts +++ b/src/runtime/bootstrap-custom-element.ts @@ -1,5 +1,5 @@ import { BUILD } from '@app-data'; -import { addHostEventListeners, forceUpdate, getHostRef, registerHost, styles, supportsShadow } from '@platform'; +import { addHostEventListeners, deleteHostRef, forceUpdate, getHostRef, plt, registerHost, styles, supportsShadow } from '@platform'; import { CMP_FLAGS } from '@utils'; import type * as d from '../declarations'; @@ -89,6 +89,19 @@ export const proxyCustomElement = (Cstr: any, compactMeta: d.ComponentRuntimeMet if (BUILD.disconnectedCallback && originalDisconnectedCallback) { originalDisconnectedCallback.call(this); } + + /** + * Clean up `hostRefs` WeakMap when the element is disconnected + */ + plt.raf(() => { + const hostRef = getHostRef(this); + if (hostRef?.$vnode$?.$elm$ instanceof Node && hostRef.$vnode$.$elm$.isConnected) { + delete hostRef.$vnode$.$elm$; + } + if (this instanceof Node && !this.isConnected) { + deleteHostRef(this); + } + }) }, __attachShadow() { if (supportsShadow) { diff --git a/src/testing/platform/index.ts b/src/testing/platform/index.ts index 5d8e4074c41..4233ba07c99 100644 --- a/src/testing/platform/index.ts +++ b/src/testing/platform/index.ts @@ -1,6 +1,6 @@ export { Build } from './testing-build'; export { modeResolutionChain, styles } from './testing-constants'; -export { getHostRef, registerHost, registerInstance } from './testing-host-ref'; +export { deleteHostRef, getHostRef, registerHost, registerInstance } from './testing-host-ref'; export { consoleDevError, consoleDevInfo, consoleDevWarn, consoleError, setErrorHandler } from './testing-log'; export { isMemberInElement, diff --git a/src/testing/platform/testing-host-ref.ts b/src/testing/platform/testing-host-ref.ts index 6adcc8d9a3c..c7179b1e727 100644 --- a/src/testing/platform/testing-host-ref.ts +++ b/src/testing/platform/testing-host-ref.ts @@ -11,6 +11,15 @@ export const getHostRef = (elm: d.RuntimeRef | undefined): d.HostRef | undefined return hostRefs.get(elm); }; +/** + * Given a {@link d.RuntimeRef} remove the corresponding {@link d.HostRef} from + * the {@link hostRefs} WeakMap. + * + * @param ref the runtime ref of interest + * @returns — true if the element was successfully removed, or false if it was not present. + */ +export const deleteHostRef = (ref: d.RuntimeRef) => hostRefs.delete(ref); + /** * Add the provided `hostRef` instance to the global {@link hostRefs} map, using the provided `lazyInstance` as a key. * @param lazyInstance a Stencil component instance From 80a5d1faa9e8e2edded4770cd24ee48857276796 Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Tue, 7 Jan 2025 15:32:41 -0800 Subject: [PATCH 18/31] prettier --- src/runtime/bootstrap-custom-element.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/runtime/bootstrap-custom-element.ts b/src/runtime/bootstrap-custom-element.ts index f42059e5512..9d10c35239c 100644 --- a/src/runtime/bootstrap-custom-element.ts +++ b/src/runtime/bootstrap-custom-element.ts @@ -1,5 +1,14 @@ import { BUILD } from '@app-data'; -import { addHostEventListeners, deleteHostRef, forceUpdate, getHostRef, plt, registerHost, styles, supportsShadow } from '@platform'; +import { + addHostEventListeners, + deleteHostRef, + forceUpdate, + getHostRef, + plt, + registerHost, + styles, + supportsShadow, +} from '@platform'; import { CMP_FLAGS } from '@utils'; import type * as d from '../declarations'; @@ -101,7 +110,7 @@ export const proxyCustomElement = (Cstr: any, compactMeta: d.ComponentRuntimeMet if (this instanceof Node && !this.isConnected) { deleteHostRef(this); } - }) + }); }, __attachShadow() { if (supportsShadow) { From a892a08f0a745ac2c06dd450075c920c9f8af11d Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Wed, 8 Jan 2025 18:54:21 -0800 Subject: [PATCH 19/31] fix: PR comment --- src/runtime/bootstrap-custom-element.ts | 2 +- src/runtime/bootstrap-lazy.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/runtime/bootstrap-custom-element.ts b/src/runtime/bootstrap-custom-element.ts index 9d10c35239c..1b050759a67 100644 --- a/src/runtime/bootstrap-custom-element.ts +++ b/src/runtime/bootstrap-custom-element.ts @@ -104,7 +104,7 @@ export const proxyCustomElement = (Cstr: any, compactMeta: d.ComponentRuntimeMet */ plt.raf(() => { const hostRef = getHostRef(this); - if (hostRef?.$vnode$?.$elm$ instanceof Node && hostRef.$vnode$.$elm$.isConnected) { + if (hostRef?.$vnode$?.$elm$ instanceof Node && !hostRef.$vnode$.$elm$.isConnected) { delete hostRef.$vnode$.$elm$; } if (this instanceof Node && !this.isConnected) { diff --git a/src/runtime/bootstrap-lazy.ts b/src/runtime/bootstrap-lazy.ts index 3cfccf0ca5c..2706d8519ed 100644 --- a/src/runtime/bootstrap-lazy.ts +++ b/src/runtime/bootstrap-lazy.ts @@ -168,7 +168,7 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. */ plt.raf(() => { const hostRef = getHostRef(this); - if (hostRef?.$vnode$?.$elm$ instanceof Node && hostRef.$vnode$.$elm$.isConnected) { + if (hostRef?.$vnode$?.$elm$ instanceof Node && !hostRef.$vnode$.$elm$.isConnected) { delete hostRef.$vnode$.$elm$; } }); From 5112b337c7fa210fafa6d4449119c22eb4e3de40 Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Wed, 8 Jan 2025 22:02:07 -0800 Subject: [PATCH 20/31] more rigurous cleanup --- src/runtime/bootstrap-custom-element.ts | 2 +- src/runtime/bootstrap-lazy.ts | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/runtime/bootstrap-custom-element.ts b/src/runtime/bootstrap-custom-element.ts index 1b050759a67..894437c4ecf 100644 --- a/src/runtime/bootstrap-custom-element.ts +++ b/src/runtime/bootstrap-custom-element.ts @@ -105,7 +105,7 @@ export const proxyCustomElement = (Cstr: any, compactMeta: d.ComponentRuntimeMet plt.raf(() => { const hostRef = getHostRef(this); if (hostRef?.$vnode$?.$elm$ instanceof Node && !hostRef.$vnode$.$elm$.isConnected) { - delete hostRef.$vnode$.$elm$; + delete hostRef.$vnode$; } if (this instanceof Node && !this.isConnected) { deleteHostRef(this); diff --git a/src/runtime/bootstrap-lazy.ts b/src/runtime/bootstrap-lazy.ts index 2706d8519ed..cdb93c29e99 100644 --- a/src/runtime/bootstrap-lazy.ts +++ b/src/runtime/bootstrap-lazy.ts @@ -1,5 +1,5 @@ import { BUILD } from '@app-data'; -import { doc, getHostRef, plt, registerHost, supportsShadow, win } from '@platform'; +import { deleteHostRef, doc, getHostRef, plt, registerHost, supportsShadow, win } from '@platform'; import { addHostEventListeners } from '@runtime'; import { CMP_FLAGS, queryNonceMetaTagContent } from '@utils'; @@ -169,7 +169,11 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. plt.raf(() => { const hostRef = getHostRef(this); if (hostRef?.$vnode$?.$elm$ instanceof Node && !hostRef.$vnode$.$elm$.isConnected) { - delete hostRef.$vnode$.$elm$; + delete hostRef.$vnode$; + deleteHostRef(hostRef.$lazyInstance$); + } + if (this instanceof Node && !this.isConnected) { + deleteHostRef(this); } }); } From 627d55fba9238255d75d6f8a4a50613788f0742d Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Wed, 8 Jan 2025 22:22:02 -0800 Subject: [PATCH 21/31] delete the lazy instance after a timeout to ensure that any pending state updates have been processed --- src/runtime/bootstrap-lazy.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/runtime/bootstrap-lazy.ts b/src/runtime/bootstrap-lazy.ts index cdb93c29e99..2248dc755d8 100644 --- a/src/runtime/bootstrap-lazy.ts +++ b/src/runtime/bootstrap-lazy.ts @@ -170,7 +170,12 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. const hostRef = getHostRef(this); if (hostRef?.$vnode$?.$elm$ instanceof Node && !hostRef.$vnode$.$elm$.isConnected) { delete hostRef.$vnode$; - deleteHostRef(hostRef.$lazyInstance$); + + /** + * delete the lazy instance after a timeout to ensure that any + * pending state updates have been processed + */ + setTimeout(() => deleteHostRef(hostRef.$lazyInstance$), 100) } if (this instanceof Node && !this.isConnected) { deleteHostRef(this); From 16456f5b6dc028831aeaeea05d19bd1ee8450e3a Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Wed, 8 Jan 2025 22:23:56 -0800 Subject: [PATCH 22/31] prettier --- src/runtime/bootstrap-lazy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/bootstrap-lazy.ts b/src/runtime/bootstrap-lazy.ts index 2248dc755d8..39e3afcd107 100644 --- a/src/runtime/bootstrap-lazy.ts +++ b/src/runtime/bootstrap-lazy.ts @@ -175,7 +175,7 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. * delete the lazy instance after a timeout to ensure that any * pending state updates have been processed */ - setTimeout(() => deleteHostRef(hostRef.$lazyInstance$), 100) + setTimeout(() => deleteHostRef(hostRef.$lazyInstance$), 100); } if (this instanceof Node && !this.isConnected) { deleteHostRef(this); From 1f0c6c4e048cad21a2b7316d9fbe81eaa4302a31 Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Thu, 9 Jan 2025 12:10:10 -0800 Subject: [PATCH 23/31] fix unit test --- src/runtime/bootstrap-lazy.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/runtime/bootstrap-lazy.ts b/src/runtime/bootstrap-lazy.ts index 39e3afcd107..dfc5e45e01d 100644 --- a/src/runtime/bootstrap-lazy.ts +++ b/src/runtime/bootstrap-lazy.ts @@ -169,7 +169,7 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. plt.raf(() => { const hostRef = getHostRef(this); if (hostRef?.$vnode$?.$elm$ instanceof Node && !hostRef.$vnode$.$elm$.isConnected) { - delete hostRef.$vnode$; + delete hostRef.$vnode$.$elm$; /** * delete the lazy instance after a timeout to ensure that any @@ -177,9 +177,6 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. */ setTimeout(() => deleteHostRef(hostRef.$lazyInstance$), 100); } - if (this instanceof Node && !this.isConnected) { - deleteHostRef(this); - } }); } From 7aafaeb99d03f85ca6c7e119c5fabd27eeba4766 Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Thu, 9 Jan 2025 12:54:22 -0800 Subject: [PATCH 24/31] chore(ci): trigger build From 29e48342c7846b1a17a141948295b307b85ee02a Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Thu, 9 Jan 2025 14:03:39 -0800 Subject: [PATCH 25/31] disable test --- test/wdio/dom-reattach/cmp.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/wdio/dom-reattach/cmp.test.tsx b/test/wdio/dom-reattach/cmp.test.tsx index 3d4016f73dd..56efa12e8da 100644 --- a/test/wdio/dom-reattach/cmp.test.tsx +++ b/test/wdio/dom-reattach/cmp.test.tsx @@ -30,7 +30,7 @@ describe('dom-reattach', function () { componentDidLoad: 1 disconnectedCallback: ${disconnectCount}`; - await expect($('dom-reattach')).toHaveText(lifecycleTextWithDisconnectCount(0)); + // await expect($('dom-reattach')).toHaveText(lifecycleTextWithDisconnectCount(0)); await $('button').click(); await expect($('dom-reattach')).not.toExist(); From a559551ac5754d224e05ba5c5e4241f166b0b37b Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Thu, 9 Jan 2025 14:13:50 -0800 Subject: [PATCH 26/31] logging --- src/runtime/bootstrap-lazy.ts | 6 +++++- test/wdio/dom-reattach/cmp.test.tsx | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/runtime/bootstrap-lazy.ts b/src/runtime/bootstrap-lazy.ts index dfc5e45e01d..7c7bac74135 100644 --- a/src/runtime/bootstrap-lazy.ts +++ b/src/runtime/bootstrap-lazy.ts @@ -169,13 +169,17 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. plt.raf(() => { const hostRef = getHostRef(this); if (hostRef?.$vnode$?.$elm$ instanceof Node && !hostRef.$vnode$.$elm$.isConnected) { + console.log('[btl]: delete hostRef.$vnode$.$elm$', hostRef.$vnode$.$elm$.nodeName); delete hostRef.$vnode$.$elm$; /** * delete the lazy instance after a timeout to ensure that any * pending state updates have been processed */ - setTimeout(() => deleteHostRef(hostRef.$lazyInstance$), 100); + setTimeout(() => { + console.log('[btl]: delete hostRef.$lazyInstance$', hostRef.$lazyInstance$); + deleteHostRef(hostRef.$lazyInstance$) + }, 100); } }); } diff --git a/test/wdio/dom-reattach/cmp.test.tsx b/test/wdio/dom-reattach/cmp.test.tsx index 56efa12e8da..4122e4c8683 100644 --- a/test/wdio/dom-reattach/cmp.test.tsx +++ b/test/wdio/dom-reattach/cmp.test.tsx @@ -13,12 +13,15 @@ describe('dom-reattach', function () { ), }); + console.log('[test] render component'); const element = document.querySelector('dom-reattach'); function reattach() { if (showElement) { + console.log('[test] remove element'); element.remove(); } else { + console.log('[test] add element'); document.body.appendChild(element); } showElement = !showElement; @@ -30,7 +33,7 @@ describe('dom-reattach', function () { componentDidLoad: 1 disconnectedCallback: ${disconnectCount}`; - // await expect($('dom-reattach')).toHaveText(lifecycleTextWithDisconnectCount(0)); + await expect($('dom-reattach')).toHaveText(lifecycleTextWithDisconnectCount(0)); await $('button').click(); await expect($('dom-reattach')).not.toExist(); From 85bb08e79a971fb0b3ebeca18c5eac195ba18f75 Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Thu, 9 Jan 2025 15:10:09 -0800 Subject: [PATCH 27/31] tweak --- src/runtime/bootstrap-lazy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/bootstrap-lazy.ts b/src/runtime/bootstrap-lazy.ts index 7c7bac74135..9c09d597447 100644 --- a/src/runtime/bootstrap-lazy.ts +++ b/src/runtime/bootstrap-lazy.ts @@ -178,7 +178,7 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. */ setTimeout(() => { console.log('[btl]: delete hostRef.$lazyInstance$', hostRef.$lazyInstance$); - deleteHostRef(hostRef.$lazyInstance$) + // deleteHostRef(hostRef.$lazyInstance$) }, 100); } }); From 2614355b3e8817b6c88a1156cec64120121a574f Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Thu, 9 Jan 2025 16:22:50 -0800 Subject: [PATCH 28/31] tweak --- src/runtime/bootstrap-lazy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/bootstrap-lazy.ts b/src/runtime/bootstrap-lazy.ts index 9c09d597447..1d81b7509cc 100644 --- a/src/runtime/bootstrap-lazy.ts +++ b/src/runtime/bootstrap-lazy.ts @@ -1,5 +1,5 @@ import { BUILD } from '@app-data'; -import { deleteHostRef, doc, getHostRef, plt, registerHost, supportsShadow, win } from '@platform'; +import { doc, getHostRef, plt, registerHost, supportsShadow, win } from '@platform'; import { addHostEventListeners } from '@runtime'; import { CMP_FLAGS, queryNonceMetaTagContent } from '@utils'; From 2f56e1e95c2141877f2d681444e0314d008d906a Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Fri, 10 Jan 2025 14:11:06 -0800 Subject: [PATCH 29/31] clean up --- src/runtime/bootstrap-lazy.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/runtime/bootstrap-lazy.ts b/src/runtime/bootstrap-lazy.ts index 1d81b7509cc..2706d8519ed 100644 --- a/src/runtime/bootstrap-lazy.ts +++ b/src/runtime/bootstrap-lazy.ts @@ -169,17 +169,7 @@ export const bootstrapLazy = (lazyBundles: d.LazyBundlesRuntimeData, options: d. plt.raf(() => { const hostRef = getHostRef(this); if (hostRef?.$vnode$?.$elm$ instanceof Node && !hostRef.$vnode$.$elm$.isConnected) { - console.log('[btl]: delete hostRef.$vnode$.$elm$', hostRef.$vnode$.$elm$.nodeName); delete hostRef.$vnode$.$elm$; - - /** - * delete the lazy instance after a timeout to ensure that any - * pending state updates have been processed - */ - setTimeout(() => { - console.log('[btl]: delete hostRef.$lazyInstance$', hostRef.$lazyInstance$); - // deleteHostRef(hostRef.$lazyInstance$) - }, 100); } }); } From 8a99e0143407f6316f82016d6acf49634319dc4a Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Fri, 10 Jan 2025 14:12:32 -0800 Subject: [PATCH 30/31] minor comment update --- src/runtime/bootstrap-custom-element.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/runtime/bootstrap-custom-element.ts b/src/runtime/bootstrap-custom-element.ts index 894437c4ecf..736f7de6eaf 100644 --- a/src/runtime/bootstrap-custom-element.ts +++ b/src/runtime/bootstrap-custom-element.ts @@ -100,7 +100,8 @@ export const proxyCustomElement = (Cstr: any, compactMeta: d.ComponentRuntimeMet } /** - * Clean up `hostRefs` WeakMap when the element is disconnected + * Clean up Node references lingering around in `hostRef` objects + * to ensure GC can clean up the memory. */ plt.raf(() => { const hostRef = getHostRef(this); From b3a724b08f28dde83429382cef150b3819dcd37a Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Fri, 10 Jan 2025 14:13:19 -0800 Subject: [PATCH 31/31] more cleanups --- test/wdio/dom-reattach/cmp.test.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/wdio/dom-reattach/cmp.test.tsx b/test/wdio/dom-reattach/cmp.test.tsx index 4122e4c8683..3d4016f73dd 100644 --- a/test/wdio/dom-reattach/cmp.test.tsx +++ b/test/wdio/dom-reattach/cmp.test.tsx @@ -13,15 +13,12 @@ describe('dom-reattach', function () { ), }); - console.log('[test] render component'); const element = document.querySelector('dom-reattach'); function reattach() { if (showElement) { - console.log('[test] remove element'); element.remove(); } else { - console.log('[test] add element'); document.body.appendChild(element); } showElement = !showElement;