Skip to content

Instantly share code, notes, and snippets.

@KonnorRogers
Created October 20, 2025 04:57
Show Gist options
  • Save KonnorRogers/adf014a201af314940f96fd94d1ee071 to your computer and use it in GitHub Desktop.
Save KonnorRogers/adf014a201af314940f96fd94d1ee071 to your computer and use it in GitHub Desktop.
Hanami file watcher for esbuild
// config/assets.js
import * as path from "node:path"
import * as url from 'node:url';
import * as assets from "hanami-assets";
import chokidar from 'chokidar';
// ESM polyfill for __dirname
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
// To provide additional esbuild (https://esbuild.github.io) options, use the following:
//
// Read more at: https://guides.hanamirb.org/assets/customization/
//
let watchMode = false
const context = await assets.run({
esbuildOptionsFn: (args, esbuildOptions) => {
// Add to esbuildOptions here. Use `args.watch` as a condition for different options for
// compile vs watch.
if (args.watch) {
watchMode = true
}
return esbuildOptions;
}
});
if (watchMode) {
function debounce(func, delay) {
let timeoutId; // This will store the ID of the timeout
return function(...args) { // Returns a new function that will be called
const context = this; // Preserves the 'this' context
clearTimeout(timeoutId); // Clear any existing timeout
timeoutId = setTimeout(() => { // Set a new timeout
func.apply(context, args); // Execute the original function after the delay
}, delay);
};
}
const watchPath = path.resolve(path.join(__dirname, ".."))
const watcher = chokidar.watch(watchPath, {
persistent: true,
ignored: (path, stats) => {
// Careful...this watcher is watching *everything*, including slices, and any compiled files...you may want to scope this down...or scope down the watch path..
// Only watch `.rb` files.
return stats?.isFile() && !path.endsWith('.rb')
},
ignoreInitial: true,
// This is usually only necessary for Docker.
usePolling: true,
});
async function handleFile(evt, path, ...args) {
if (evt === "add") {
console.log(`File ${path} has been added`)
} else if (evt === "change") {
console.log(`File ${path} has been changed`)
} else if (evt === "unlink") {
console.log(`File ${path} has been removed`)
}
console.log("Rebuilding...")
await context.rebuild()
console.log("Finished rebuilding.")
}
const debouncedFileHandler = debounce(handleFile, 100)
const events = ["add", "change", "unlink"]
events.forEach((evt) => {
watcher.on(evt, async (path, ...args) => {
debouncedFileHandler(evt, path, ...args)
})
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment