sveltejs / svelte-hmr

HMR commons for Svelte 3
ISC License
209 stars 19 forks source link

Feature: Expose `reload` function for ProxyComponent on `$$` allowing bespoke hot reloading support. #68

Closed typhonrt closed 1 year ago

typhonrt commented 1 year ago

Hi @rixo / @dominikg,

I'd like to find out if at all possible that the reload mechanism associated with ProxyComponent could be exposed on $$ / internal API; perhaps name it hmr_reload. A use case just presented itself for the platform that I deliver my Svelte UI framework on as things go. This is a TTRPG / VTT / Foundry VTT. They just enabled custom hot reloading for data files including localization / i18n. I can easily tap into the platforms hot reload callback and reload the mounted Svelte component with access to the reload function that currently is only used for import.meta.hot internally to svelte-hmr otherwise it is not accessible.

It should be noted that I simply expose the reload method on ProxyComponent for this proof of concept demo. Ideally it would be exposed on $$ though as internal API.

Thanks for your work on this package. I can see bespoke hot reloading made possible with this one adjustment and I know all the devs using my UI framework would like to have hot reloading for language / i18n data changes.

Here is a video demoing hot reload for i18n by modifying a language / translation JSON file on Foundry + my Svelte UI framework:

https://user-images.githubusercontent.com/311473/227505519-f87a42fd-7163-4795-82b3-987dbca33775.mov

typhonrt commented 1 year ago

I have created / tested a PR that exposes the ProxyComponent reload function on internal API at component.$$.hmr_reload. It is rather useful to be able to reload Svelte components in a bespoke manner beyond just HMR use cases provided by svelte-hmr. In this case I can utilize the PR changes to provide automatic reloading for a deployment platforms (FoundryVTT) i18n / localization changes that have an independent hook / event.

It would be great to discuss the use case for bespoke hot reloading beyond just HMR.

typhonrt commented 1 year ago

Given discussion on the Svelte Discord I got informed about the proxy.$$.hmr_cmp.$replace way of replacing / reloading a proxy instance. This issue is resolved and associated PR is unnecessary. Thanks to Rixo / Dominik for the help and willingness to engage!

For those that this might help as of Svelte v3 here is how to replace / reload a ProxyComponent instance when running the Vite dev server w/ svelte-hmr. This is my full / actual solution, so just take note of the appShell.$$.hmr_cmp and $replace section:

if (import.meta.hot)
{
   Hooks.on('hotReload', (data) =>
   {
      // Only handle JSON hot reload presumably specified in package manifest for language translation files.
      if (data?.extension === 'json')
      {
         // Postpone until next clock tick to allow Foundry to update localization first.
         setTimeout(() =>
         {
            for (const app of TJSAppIndex.values())
            {
               const appShell = app.svelte.applicationShell;

               // Retrieve the original `svelte-hmr` instrumented HMR component / not the proxy.
               const hmrComponent = appShell?.$$?.hmr_cmp;

               if (appShell && typeof hmrComponent?.$replace === 'function')
               {
                  const svelteData = app.svelte.dataByComponent(appShell);
                  if (svelteData)
                  {
                     try
                     {
                        // Replace with self; this will invoke `on_hmr` callback in associated SvelteApplication.
                        hmrComponent.$replace(hmrComponent.constructor, {
                           target: svelteData.config.target,
                           anchor: svelteData.config.anchor,
                           preserveLocalState: true,
                           conservative: true
                        });
                     }
                     catch (error)
                     {
                        const name = hmrComponent?.constructor?.name ?? 'Unknown';
                        console.error(`TyphonJS Runtime Library error; Could not hot reload component: '${name}'`);
                        console.error(error);
                     }
                  }
               }
            }
         }, 0);
      }

      return true;
   });
}