Skip to content

Commit

Permalink
fix(hmr): allow changes to component decorators when using HMR (#5290)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
alicewriteswrongs committed Jan 24, 2024
1 parent 0041dc2 commit 656355f
Showing 1 changed file with 14 additions and 1 deletion.
15 changes: 14 additions & 1 deletion src/client/client-host-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<d.RuntimeRef, d.HostRef> = /*@__PURE__*/ new WeakMap();
const hostRefs: WeakMap<d.RuntimeRef, d.HostRef> = /*@__PURE__*/ BUILD.hotModuleReplacement
? ((window as any).__STENCIL_HOSTREFS__ ||= new WeakMap())
: new WeakMap();

/**
* Given a {@link d.RuntimeRef} retrieve the corresponding {@link d.HostRef}
Expand Down

0 comments on commit 656355f

Please sign in to comment.