Rethinking Hot Module Reloading

I recently worked on the FuseBox module loader to make its Hot Reloading configurable. Before I jump into how that works here is a quick node / modules lesson.

Modules are just fancy globals

You might already know this as a seasoned developer but I’ve seen people of all levels being unaware of this fact too many times. Say you have ./foo.ts with the following

As soon as another file requires this one e.g. ./bar.ts:

Once you require (or `import` in ES6) the same module you get the same copy. Even though bar.ts requires foo.ts twice, you will see Executing foo only happen once. Essentially once a module is required … it is executed only once and then the module.exports object is cached and returned on each subsequent require. If any of this is still unclear, then I recommend reading a book on NodeJS (I wrote one as well) or even the official docs.

FuseBox default HMR

FuseBox has excellent docs. Checkout the ones on the LoaderAPI.

By default when a file is changed fusebox compiles it to a .js or .css and sends it down to any connected clients via a websocket as a SrcChangedEvent. The client then uses the loader api to:

  • Flushes all files from loaded cache using FuseBox.flush. What this means is that when a file is required again … it will be executed again and there will be a new module.exports.
  • Patches the contents of the file that is updated with the contents as passed in with the SrcChangedEvent.
  • Executes the mainPath for the current default package again. Since all modules are flushed everything essentially executes again as it gets required again from the mainPath.

As an outside reviewer this behaviour feels absolutely beautiful. If your application is always an output of some state, it *should be able to be re-rendered in its entirety* using this behaviour just fine.

But your application likely has some modules that you don’t want running again and being reused between HMR updates. Fortunately FuseBox HMR provides loader plugins that can completely change the behaviour to whatever you want!.

Things you don’t want running again

I call these stateful things. An example is a router that registers a hook on window.addEventListener(“hashchange”,/*something*/) is not something you want executing again:

Similarly for things that initialise state:

You can write a function that adds a loader plugin, which will change the hmr update behaviour to be:

  • if something not stateful changed:
    - flushes all files from loaded cache except stateful ones
    - patches the changed files on HMR update
    - runs the application entry point again
  • if something stateful changed
    - refresh the whole window

This way you don’t get duplicate global handlers and get to reuse state without needing to wrap your components in anything fancy.

Fortunately you don’t have to write this function as the FuseBox team was happy enough to ship it with the core! Just bring in fuse-hmr as shown below:

Here it is in action

Stateful HMR in action

Footnote: Why fusebox?

It’s insanely fast. It’s generally the first reaction

But the thing that got me was really the approachability of the source code. I’ve ranted a bit on the value of typescript and here is a recent post by someone else that’s very well written. You won’t find much code here for the sake of catching user errors that can caught at compile time (thanks to TypeScript). It’s all open source so go ahead and have a look, adding your 🌟 while you are there.

Its built with TypeScript and supports it out of the box. In fact your config even autocompletes if you write it in ts (tip: use ts-node to run it):

Also its API first instead of using config files. Means you can use it as powerfully as you want 🌹

If you liked this post / ideas don’t forget to hit the green heart 💚