@zevanoo/hmr
v1.1.0
Published
**Lightweight, flexible, and production-ready Hot Module Replacement (HMR) for Node.js (ESM/CJS).**
Maintainers
Readme
@zevanoo/hmr
Lightweight, flexible, and production-ready Hot Module Replacement (HMR) for Node.js (ESM/CJS).
In modern Node.js development, especially with ECMAScript Modules (ESM), module caching is a persistent hurdle. Once a file is imported, Node.js caches it in memory for the lifetime of the process. @zevanoo/hmr solves this by providing a centralized registry to dynamically load, update, and remove modules without restarting your application.
This library is ideal for long-running processes that require rapid iteration or "live" logic updates, such as:
- Bot Platforms (WhatsApp, Discord, Telegram) for instant command updates.
- API Servers requiring endpoint updates without dropping active socket connections.
- Automation Systems where logic scripts change frequently at runtime.
- Worker Threads processing tasks based on dynamic user-provided scripts.
Key Features
- 🚀 Zero-Restart Runtime: Update functions, variables, or objects in real-time.
- 📂 Multi-Directory Support: Monitor multiple directories simultaneously with unique configurations.
- ⚡ Environment Aware: Full HMR in
developmentmode, optimized static one-time loading inproduction. - 📡 Event Driven: Built on
EventEmitterto handleadd,change, andunlinkevents. - 🛡️ Intelligent Cleanup: Automatically purges memory references (registry) when a file is deleted.
- 🎨 Flexible Logging: Supports both global and local (per-folder) custom loggers with timestamps.
- 🔒 Type Safe: Written in TypeScript with full Intellisense support.
Installation
npm install @zevanoo/hmrQuick Start
1. Initialization
Initialize the HMR instance in your application's entry point (e.g., index.js or main.ts).
import HMR from '@zevanoo/hmr';
const hmr = new HMR(
{
watchDir: './src/utils',
ignoreFiles: ['formatter.js']
},
{
watchDir: './src/libs'
},
// Final Argument: Global Logger (Optional)
(time, level, msg) => {
console.log(`[${time}] [HMR-SYSTEM] ${level.toUpperCase()}: ${msg}`);
}
);
export default hmr;2. Accessing Modules
You can access exported members via the .get() method or directly through the modules Proxy.
import hmr from './index.js';
// Option 1: Specific member access (Destructuring)
const { toNumber, isValid } = hmr.get(['toNumber', 'isValid']);
if (toNumber) console.log(toNumber("100"));
// Option 2: Direct access via Proxy (Recommended for concise code)
const myCommand = hmr.modules.pingCommand;
if (myCommand) myCommand.execute();Event Emitter
Since the library extends Node.js EventEmitter, you can attach listeners to trigger side effects when files change.
// Triggered when a new file is found
hmr.on('add', (filePath, module) => {
console.log(`New module registered: ${filePath}`);
});
// Triggered when an existing file is edited and reloaded
hmr.on('change', (filePath, module) => {
console.log(`Module updated: ${module.name}`);
});
// Triggered when a file is deleted from disk
hmr.on('unlink', (filePath) => {
console.log(`Module removed: ${filePath}`);
});Runtime Behavior
The library automatically adjusts its strategy based on process.env.NODE_ENV:
Development (NODE_ENV=development)
- Watcher: Active (powered by
chokidar). - Cache Busting: Appends a
?update=timestampquery string to every dynamic import to bypass the Node.js ESM cache. - Logs: Detailed synchronization logs.
Production (NODE_ENV=production)
- Watcher: Disabled. Modules are scanned and loaded recursively exactly once during startup.
- Performance: Uses standard Node.js internal caching for maximum RAM and CPU efficiency.
- Logs: Minimalist (only critical errors).
API Configuration
new HMR(...folderConfigs, globalLogger?)
Folder Configuration Object
| Property | Type | Description |
| :--- | :--- | :--- |
| watchDir | string | Required. Path to the folder to monitor. Supports recursive sub-folders. |
| ignoreFiles | string[] | List of filenames/suffixes to ignore (e.g., ['.test.js']). |
| logger | function | Local logger specific to this folder. If provided, it overrides the global logger. |
Logger Callback Function
(time, level, msg) => void
time: Current locale time string.level:'info' | 'warn' | 'error'.msg: The message content.
License
MIT
Important Note on Sub-Folders
HMR recursively watches sub-folders within your watchDir. Because this library uses a Flat Registry (all exports are stored in a single Map), please ensure your export names are unique across all watched files to avoid naming collisions.
