# Enabling HMR in Node.js

By default, the native Node.js module system caches module exports. The new --watch and --watch-paths CLI param allows reloading the entire runtime when the given paths change. This workflow is sufficient for simple scripts.

But for more complex programs, or when some portion of the program takes a while to load (e.g. shiki), or some operations are resource-expensive (e.g. loading files from disk) and to be done as few times as possible, we don't want to throw away the entire runtime when files change, but only reload files that need to be reloaded, and decache some modules.

Using immaculata, you can:

A simple example of enabling HMR in Node.js:

import * as immaculata from "immaculata"
import { registerHooks } from 'module'
import ts from 'typescript'

const tree = new immaculata.LiveTree('site', import.meta.url)
registerHooks(tree.enableImportsModuleHook())

const myModule = await import('site/myModule.js')
// site/myModule.js is executed

const myModule2 = await import('site/myModule.js')
// site/myModule.js is NOT executed

tree.watch({}, async () => {
  const myModule = await import('site/myModule.js')
  // site/myModule.js IS executed again if invalidated
})

Now any time you save site/myModule.js, or any module that it imports (recursively), the code in this file will be re-executed (via cache invalidation). This provides efficient HMR in Node.js using its native module system.

Note that it also works with require if the project is in ESM mode (via package.json's type key). But module.createRequire will not (yet) respect the cache invalidation feature due to node#57696.