Snowpack: Build `--watch` HMR Support

Created on 6 Sep 2020  路  8Comments  路  Source: snowpackjs/snowpack

Original Discussion: https://github.com/pikapkg/snowpack/pull/782#issuecomment-673719430, https://github.com/pikapkg/snowpack/issues/970#issuecomment-687644002
/cc @francislavoie @jeberly

Background

snowpack build --watch will run Snowpack's build pipeline through a file watcher, allowing you to do local development with an output directory that can be served by a custom frontend server (ex: Rails, PHP, etc). This feature was implemented somehwat recently in Snowpack v2.8 (iirc).

Currently there is no HMR support for this, since HMR is quite complicated and our fully-featured HMR engine needs to own both the server and client to get the required info about how an application loads and how HMR update events are bubbled.

Feature Request

This feature request comes in two parts/stages, which should be tackled separately & in order.

Part 1. Basic Live-Reload Support

When the user runs `build --watch --hmr:

  • Pass hmr: true to wrapHtmlResponse() in the build command when --hmr is passed. This will add the HMR client to the top-level HTML files, which is required for basic live-reload support.
  • Start up an HMR server alongside the build file wacher (you can borrow a lot of the same logic from the dev command.)
  • On "file changed" event, call hmrEngine.broadcastMessage({type: 'reload'});. This will tell the browser to reload on every file change.

Part 2. Advanced HMR Support

This is the much more complicated problem since HMR works by giving the server a complete understanding of how each file in your application is connected by imports/exports, so that update events are properly bubbled up to imported parents. You can learn more here: https://github.com/pikapkg/esm-hmr

In dev, this happens organically as files are served to the client. In build, this may require a full scan of your application on the initial build, to understand all imports. We can flesh this out in more detail once part 1 is completed, since this will build on that work.

All 8 comments

I would like to try this task and learn how to implement hmr.

Pass hmr: true to wrapHtmlResponse() in the build command when --hmr is passed. This will add the HMR client to the top-level HTML files, which is required for basic live-reload support.

Hopefully there will be a way to enable this for projects that don't use a public dir in the build - I have my entrypoint separate from my build. If there's a JS code snippet I would need to inject myself during development, I would like that approach if possible.

Thanks for tagging me!

Yes! Both would be supported, but you're right that we'll need to include instructions/documentation for what code to add to your HTML when Snowpack doesn't own your HTML. It's just a one-line code-snippet that you'd need to inject.

And, when we get to part 2 the snippet should no longer be necessary since Snowpack will automatically add it to all JS files when full HMR is enabled.

Adding a separate server brings in some complexity for scenarios like when developing on a server with --watch mode with files served through a reverse proxy as one would now need to proxy to the hmr server too which may not be possible in some environments when only a single port is reachable.

Maybe a solution like outputting (and updating) a .json file that can be polled in short intervals by clients-side code could be evaluated as well. It could contain a object of a file-to-content hash mapping based on which client-side code can decide what to reload, e.g.:

```json
{
"web_modules/index.js": "",
"web_modules/module.js": ""
}

@silverwind interesting, yea, I think there will be limits to how many unique use-cases we can support, at least to start. The JSON file polling is a smark solution but out of scope until this has had a chance for the main websocket workflow to settle and bake a bit.

This was one of the reasons we wanted to start as opt-in, so right now build --watch remains unchanged and build --watch --hmr (or devOptions.hmr: true) is the only way to enable it.

I'm curious, can you share more about why you can't open up another port for the HMR server?

Update: #1008 was just merged, completing "Part 1" outlined above.

I'm going to close this for now since Part 2 is lower priority, still a bit unknown, and probably shouldn't be tackled until the main "Part 1" workflow has had a chance to bake a bit.

Spun off some follow up work identified in the PR here: https://github.com/pikapkg/snowpack/issues/1041

I'm curious, can you share more about why you can't open up another port for the HMR server?

It'd be for environments that are strictly firewalled to allow only a single port (443). I'd have a backend application that listens on that port that serves the public directory statically (among other tasks).

If the HMR server could be set up to use a predefined URL it should still be possible to proxy traffic to it on a single port. E.g. the backend application could proxy path /hmr to localhost:hmrport. Maybe you could add an option to set the URL the devserver is reached on, ideally with some way to base it off the current origin, e.g. $origin/hmr.

I wonder if our current "proxy" config could already support this. Have you tried https://www.snowpack.dev/#dev-request-proxy

Was this page helpful?
0 / 5 - 0 ratings

Related issues

devongovett picture devongovett  路  4Comments

gr2m picture gr2m  路  6Comments

FredKSchott picture FredKSchott  路  6Comments

FredKSchott picture FredKSchott  路  5Comments

FredKSchott picture FredKSchott  路  6Comments