From 656355fc753fe09128f6f20f33150123863839d8 Mon Sep 17 00:00:00 2001 From: Alice Pote Date: Wed, 24 Jan 2024 09:41:58 -0500 Subject: [PATCH] fix(hmr): allow changes to component decorators when using HMR (#5290) This fixes an issue where HMR updates which changed arguments to the `@Component` decorator would crash the application because a `HostRef` for the component could not be located. This occurred because HMR updates caused a new version of the component's lazy bundle to be fetched, which in turn would bundle the `getHostRef` function and the corresponding `hostRefs` variable, which is declared here: https://github.com/ionic-team/stencil/blob/0041dc27b9deb45cfd1434a3d82d563793e56056/src/client/client-host-ref.ts#L18 Because the newly fetched lazy entry point for the updated component did not have access to the `hostRefs` variable which was in-scope in the _old_ bundle, the `getHostRef` function would always return `undefined`, on the first render after the HMR update, causing issues in functions like `setValue` which make use of a `HostRef`. In order to fix the issue we can persist the `hostRefs` map across HMR updates by storing a reference to it on the `window` object. We can additionally constrain this persistence to only occur when we're building with HMR, so that we don't clutter up the `window` object of unsuspecting Stencil users in the context of a production build. STENCIL-973 --- src/client/client-host-ref.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/client/client-host-ref.ts b/src/client/client-host-ref.ts index 57bae6e71f8..eb7d169bb56 100644 --- a/src/client/client-host-ref.ts +++ b/src/client/client-host-ref.ts @@ -6,8 +6,21 @@ import type * as d from '../declarations'; /** * A WeakMap mapping runtime component references to their corresponding host reference * instances. + * + * **Note**: If we're in an HMR context we need to store a reference to this + * value on `window` in order to maintain the mapping of {@link d.RuntimeRef} + * to {@link d.HostRef} across HMR updates. + * + * This is necessary because when HMR updates for a component are processed by + * the browser-side dev server client the JS bundle for that component is + * re-fetched. Since the module containing {@link hostRefs} is included in + * that bundle, if we do not store a reference to it the new iteration of the + * component will not have access to the previous hostRef map, leading to a + * bug where the new version of the component cannot properly initialize. */ -const hostRefs: WeakMap = /*@__PURE__*/ new WeakMap(); +const hostRefs: WeakMap = /*@__PURE__*/ BUILD.hotModuleReplacement + ? ((window as any).__STENCIL_HOSTREFS__ ||= new WeakMap()) + : new WeakMap(); /** * Given a {@link d.RuntimeRef} retrieve the corresponding {@link d.HostRef}