Find out how to use the @roots/wordpress-hmr
library to build WordPress blocks and other block editor customizations with hot reload (HMR) support.
In this blog post it says “Moreover, the wordpress-hmr package can be used independently of bud.js or even webpack.”
I’m trying to achieve exactly that (we use vite & TS) but am struggling to get hot reloading working for our custom blocks. The documentation is a bit difficult to understand (even in the bud context). I checked the code to make sense of it and maybe derive from there how to get this working but without success.
I would be very grateful for some pointers in the right direction as hmr is something I’ve been wanting to get working in the block editor for ages. Thanks in advance.
The src/loader.ts
module is just sugar for theme/plugin developers. It replaces roots.register
with the actual call. Without the loader roots.register('./blocks')
is desugared to:
import * as blocks from '@roots/wordpress-hmr/blocks';
blocks.register(
() => import.meta.webpackContext(
'./',
{recursive: true, regExp: /.*\\.blocks\\..*$/}
),
(id, context) => {
if (import.meta.webpackHot) return import.meta.webpackHot.accept(id, context)
},
);
If you check out the source for the blocks
module it defines what should happen before a hot reload event and what should happen after. This is a curried function that is passed to the editor
module which executes the two callbacks (before
and after
). All of the entities recycle the editor
module in the way I just described.
The interface looks like this:
What you’re trying to do is replace the editor.load
function with a vite
specific implementation.
You should check out the vite docs for the vite HMR API and the vite handleHotUpdate
method from its plugin API:
Thanks a lot mate!!!
That helps a lot and gives me a good starting point for trying to figure this out. I’ll be back with either some questions, or with a finished solution to share with others.
cheers
I was able to make it work.
This is the code in the main file that is enqueued in the block editor
import { blocks } from '@roots/wordpress-hmr';
import createRequireContext from "./createRequireContext";
const modules = import.meta.glob('./**/*.block.*', { eager: true });
blocks.register(
() => createRequireContext(modules, import.meta.url),
(id, context) => {
if (import.meta.hot) {
return import.meta.hot.accept(id.toString(), context);
}
},
);
if (import.meta.hot) {
import.meta.hot.accept();
}
createRequireContext()
transforms our modules into the format expected by blocks.register()
. The latter is expecting __WebpackModuleApi.RequireContext
so we needed to add @types/webpack-env
to our devDependencies
so ts
knows what’s up with that.
import RequireContext = __WebpackModuleApi.RequireContext;
const createRequireContext = <T = any>(modules: Record<string, T>, moduleId: string): RequireContext => Object.assign(
(id: string) => modules[id],
{
keys: () => Object.keys(modules),
resolve: (id: string) => id,
id: moduleId
}
);
export default createRequireContext;
The only thing not working is the initial snackbar notice informing us that hot reload is enabled. It’s testing for import.meta.webpackHot
which is always false because in vite this would have to be changed to import.meta.hot
. Small inconvenience.
Thanks again for your input Kelly.