-
Couldn't load subscription status.
- Fork 16
Description
Currently it seems a fair amount of projects are working towards implementing HMR support.
A couple of existing implementations related to webcomponents/ESM:
- Initial hot elements implementation. [Not ready to merge] lit/lit-element#802 (@rictic)
- WIP: add dev-server-hmr modernweb-dev/web#685
- feat: add HMR functionality salesforce/lwc#2071 (@diervo, @caridy)
- vite
- snowpack (@FredKSchott)
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 (basically a file watcher which notifies the client when a module changes)
- Client-side (an API to communicate with these server updates)
- Framework/library specific (an integration of the client-side API into a specific ecosystem like lit-element)
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 whole
Client-side
An API should be made available at import.meta.hot which can have methods for the following:
- Accept updates (notify the server this module can handle updates, via an
acceptmessage) - Refuse updates (notify the server this module cannot handle updates)
- Invalidate the current module (if something went wrong, force a full reload)
- Disposer (handle teardown of the module before a new version is loaded)
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 HMR
Handling 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.reloadi suppose
Example implementation
Within the modernweb repo I wrote the following message types:
// emitted by the server
export interface HmrReloadMessage {
type: 'hmr:reload';
}
// emitted by the server
export interface HmrUpdateMessage {
type: 'hmr:update';
url: string;
}
// emitted by the client
export interface HmrAcceptMessage {
type: 'hmr:accept';
id: string;
}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:
// at import.meta.hot
{
accept(callback);
accept(deps[], callback);
dispose(callback);
decline();
invalidate();
}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:
{
acceptCallback(callback);
acceptCallback(deps[], callback);
disposeCallback(callback);
decline();
invalidate();
}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:
static notifyOnHotModuleReload(tag, newClass)Which i agree with, though maybe named with a Callback suffix like connectedCallback 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.