Open 43081j opened 3 years ago
I think es module HMR is something to "standardize" on outside of this platform, basically what has been started at https://github.com/snowpackjs/esm-hmr.
I think the main point of interest here is to try to standardize on an update pattern for web components, such as a standard callback to call.
You are right, i've updated the post to focus a bit more on the client and the web component stuff.
Another reference from Salesforce from @diervo and @caridy: https://github.com/salesforce/lwc/pull/2071
Thanks @abdonrd for the heads up.
Given that I was implementing HMR recently and looking at other implementations, here are some observations:
In general I think many frameworks need the per module import.meta.hot
described above, primarily because they need to observe and potentially revert or change some side-effects and swap bindings of things that they reference or hold.
However I believe there are also frameworks where they might be simple enough to have a centralize module swapping. For example:
// centralized hmr example
import { updateModule } from 'lwc';
export function onModuleUpdate(oldUri, newUri) {
const oldModule = await import(oldUri);
const newModule = await import(newUri);
updateModule({
oldModule,
newModule,
});
}
On our LWC framework at Salesforce for example, we can swap any component js, css or html from a unique centralize place without issues with side-effects.
I know that this approach might not work for cases due to identity discontinuity or other odd side-effects, but I just wanted to put it here in case its worth exploring different angles and/or APIs.
While mostly its implementation framework-land details, if we could avoid for certain apps de need to prepend/append import.meta stuff will be interesting (it is observable nonetheless). However the proposed APIs seems a simple and complete way to go forward as well.
Swapping an element's HTML/CSS is simple enough, but what about adding/removing/changing class methods and/or changes in other extended elements or applied mixins?
I've started an approach for universal web component HMR at https://open-wc.org/docs/development/hot-module-replacement/
The idea is that the base class can implement a static and/or instance hotReplaceCallback
, callback. This decides how to do the actual replacement.
I'm going through different base libraries to see if this approach works for them. So far lit-element and fast element works, and I got haunted working with some code changes.
The plugin I linked is based on es module HMR, I wasn't able to figure out yet how to use LWC with regular es modules.
Currently it seems a fair amount of projects are working towards implementing HMR support.
A couple of existing implementations related to webcomponents/ESM:
These are only a couple, there will be more. However, there is no consistent API right now across these.
There are three parts to HMR as far as I can see:
Server-side
The server-side implementation should be as simple as a web socket service which emits messages of the following types:
update
- a message specifying that a particular module needs reloadingreload
- a message specifying that the page must reload as a wholeClient-side
An API should be made available at
import.meta.hot
which can have methods for the following:accept
message)The client-side implementation should primarily exist to handle the server-side messages, though it should also emit its own message:
accept
- a message specifying that the current module supports HMRHandling of the server-side messages could look like this:
update
- dynamically import the specified module and execute a user-supplied callback for dealing with the updatereload
- callwindow.location.reload
i supposeExample implementation
Within the modernweb repo I wrote the following message types:
Note that the message types are prefixed here because we already had a web socket open and didn't want to have a second just to specify the protocol. Though it could be argued a protocol is better here than a prefixed set of types.
Meanwhile, i used snowpack as inspiration to write a client API which looks like this:
However i'm not such a fan of it even though i did it. As confusion can quickly come about by weak naming.
I would suggest more like:
Framework/library specific
For example, the work being done to lit-element around HMR will produce an overridden
customElements.define
which then understands how to update an element when it is re-defined.Peter's work in the lit branch has this:
Which i agree with, though maybe named with a
Callback
suffix likeconnectedCallback
and such.The idea here being every hmr-compatible web component would have this standard static method which the library or user must implement.
Summary
I think the most important thing to get right here is the client API available at
import.meta.hot
and the framework/library specific interface.