How to Configure HMR in Cordis With Debounce, Root, and Ignore Options
Configure Cordis HMR by setting root directories to watch, debounce milliseconds to batch rapid changes, and ignored glob patterns to exclude files—options passed directly to the underlying chokidar watcher.
The @cordisjs/plugin-hmr package provides hot module replacement for Cordis applications, allowing plugins to reload automatically when source files change. These three configuration options control precisely which files trigger reloads and how quickly those reloads occur.
Understanding the Core HMR Configuration Options
The HMR plugin accepts a configuration object that extends standard chokidar watcher options. According to the source code in packages/hmr/src/index.ts, three fields directly impact developer experience:
| Option | Type | Default | Purpose |
|---|---|---|---|
root |
string[] |
['.'] |
Directories to monitor, relative to the base path |
debounce |
number |
100 |
Milliseconds to wait before triggering reload after last change |
ignored |
string[] |
['**/node_modules', '**/.*', 'cache', 'data'] |
Glob patterns excluded from watching |
These defaults are defined in the Hmr.Config interface at lines 8-13 of packages/hmr/src/index.ts.
How root Controls the Watch Scope
The root option determines where chokidar.watch() begins scanning. In packages/hmr/src/index.ts lines 8-13, the watcher initializes with:
const watcher = chokidar.watch(this.config.root, {
ignored: this.config.ignored,
// ...
})
Multiple directories can be specified as an array. Paths are resolved relative to the Cordis base directory.
How debounce Batches Rapid File Changes
The debounce value wraps the partialReload() method to prevent excessive reloads during rapid edits. At lines 25-26 of packages/hmr/src/index.ts:
this.debouncedReload = this.ctx.debounce(this.partialReload.bind(this), this.config.debounce)
The ctx.debounce utility itself is provided by @cordisjs/plugin-timer, implemented at lines 134-135 of packages/timer/src/index.ts. The test suite in packages/hmr/tests/index.spec.ts (lines 66-82) verifies that multiple changes within the debounce window trigger only a single reload.
How ignored Filters Unwanted Files
Files matching any pattern in ignored are excluded before the watcher emits events. The plugin uses picomatch for glob filtering as part of the chokidar initialization. Default patterns exclude node_modules, hidden files, and common data directories.
Configuring HMR in a Cordis YAML File
The most common approach uses a Cordis YAML configuration file. The plugin configuration is read during context initialization through ctx.loader.entries().
# cordis.yml
- id: timer
name: '@cordisjs/plugin-timer' # Required: provides ctx.debounce()
- id: hmr
name: '@cordisjs/plugin-hmr'
config:
# Watch only these directories
root:
- src
# Exclude from watching
ignored:
- '**/node_modules'
- '**/.git'
- '**/*.js' # Skip compiled output if using TypeScript
- 'dist/**'
# Batch changes within 50ms
debounce: 50
Configuring HMR Programmatically
For dynamic or environment-specific setups, pass the configuration object directly to ctx.plugin():
import { Context } from 'cordis';
import Loader from '@cordisjs/plugin-loader';
import Hmr from '@cordisjs/plugin-hmr';
import Timer from '@cordisjs/plugin-timer';
const ctx = new Context();
async function main() {
// Required dependency: provides debounce utility
await ctx.plugin(Timer);
// Provides plugin loading infrastructure
await ctx.plugin(Loader);
// Configure HMR with custom options
await ctx.plugin(Hmr, {
root: ['src', 'lib'], // Monitor multiple directories
ignored: [
'**/node_modules',
'**/.git',
'dist/**',
'**/*.test.ts' // Ignore test files
],
debounce: 150 // 150ms batching window
});
await ctx.start();
}
main();
Key Source Files and Implementation Details
| File | Lines | Purpose |
|---|---|---|
packages/hmr/src/index.ts |
8-13, 25-26 | Config interface, watcher creation, debounce wrapper |
packages/hmr/tests/index.spec.ts |
66-82 | Debounce behavior verification |
packages/timer/src/index.ts |
134-135 | ctx.debounce() implementation |
packages/hmr/tests/cordis.yml |
— | Example YAML configuration |
Performance and Behavior Considerations
- Lower
debouncevalues (50ms or less) feel more responsive but may trigger multiple reloads during rapid saves - Higher
debouncevalues (200ms+) reduce reload frequency but introduce noticeable delay - Restrictive
rootpaths improve startup performance by reducing initial scan scope - Comprehensive
ignoredpatterns prevent unnecessary reloads from build artifacts, lockfiles, and editor temporary files
The test suite at packages/hmr/tests/index.spec.ts demonstrates that proper configuration of these three options is essential for stable development workflows with large codebases.
Summary
rootspecifies which directories to watch; defaults to current working directorydebouncecontrols reload batching in milliseconds; defaults to 100msignoredfilters files using glob patterns; excludesnode_modulesand hidden files by default- All options are passed directly to
chokidar.watch()inpackages/hmr/src/index.ts - The
@cordisjs/plugin-timerdependency provides the underlyingctx.debounce()utility
Frequently Asked Questions
What happens if I don't install @cordisjs/plugin-timer?
The HMR plugin will fail to initialize because ctx.debounce() is undefined. The timer plugin is a required peer dependency that provides the debounce utility used at line 25 of packages/hmr/src/index.ts.
Can I use absolute paths in the root option?
Yes. The root array accepts any valid path that chokidar.watch() can resolve. Relative paths are resolved against the Cordis base directory; absolute paths are used as-is.
Why are my changes not triggering reloads despite correct root settings?
Check your ignored patterns. The default configuration excludes all dotfiles (**/.*). If your source files are in a hidden directory or match an ignored glob, they will not emit change events. Verify against the picomatch filtering in the watcher initialization.
Does debounce affect the initial load or only subsequent reloads?
The debounce option only affects hot reloads of already-loaded plugins. Initial plugin loading during context startup is not debounced. The debounced partialReload() wrapper created at lines 25-26 of packages/hmr/src/index.ts is invoked exclusively on file change events.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →