hyde/realtime-compiler
Hyde Realtime Compiler brings live rebuilds to HydePHP development: it watches your source files and automatically recompiles the static site whenever changes are saved, keeping previews current and eliminating manual build steps.
hyde/realtime-compiler is a dev-loop accelerator: it watches your Hyde source files, re-runs the static build, and pushes a browser reload so you stop hitting F5 while writing Markdown. It is not a production dependency and it does not replace hyde build.
Minimal adoption path:
composer require --dev hyde/realtime-compiler
Then look at these three things in the package before running anything:
composer.json → bin and extra.laravel.providers — this tells you whether you launch it as a standalone binary (vendor/bin/...) or as an Artisan command registered by a service provider.php artisan vendor:publish. It's where watched paths, debounce, port, and reload strategy live.First real use case — the inner loop for content authoring:
# terminal 1
php hyde serve # or: vendor/bin/realtime-compiler
# terminal 2 (optional) — CSS/JS HMR handled by Vite, not this package
npm run dev
Open the served URL, edit _posts/my-new-post.md, save, and confirm the page reloads without a manual refresh. If that works, you're done — everything else is tuning.
Two practical checks before you commit to it:
require-dev. Anything that watches the filesystem and opens a port has no business in a production composer install --no-dev."scripts": {
"dev": [
"Composer\\Config::disableProcessTimeout",
"hyde serve --realtime"
]
}
The disableProcessTimeout line matters — long-running watch processes get killed by Composer's default 300s process timeout otherwise.
Treat it as a browser-sync layer, not a build tool. The reliable mental model: your real build command is still php hyde build; the compiler just triggers it on a timer and injects a reload client. Keep hyde build working standalone so CI, deploys, and debugging never depend on the watcher.
Split responsibilities with your asset pipeline. Hyde sites typically pair a static build with a Tailwind/Vite step. Don't ask the compiler to watch resources/css — let Vite's own HMR own that, and let the compiler own _posts/, _pages/, _docs/, _media/, and resources/views/. Two watchers with one responsibility each beats one watcher with two.
Scope the watch list aggressively. On a content-heavy site, watching _media/ means every image drop triggers a full rebuild. Configure watched paths explicitly:
// config/realtime-compiler.php (name/keys vary — verify against the published file)
return [
'watch' => [
'_posts',
'_pages',
'_docs',
'resources/views',
'config/hyde.php',
],
'ignore' => ['_site', 'node_modules', 'vendor', '.git'],
'debounce' => 300, // ms
];
The ignore entry for _site is non-negotiable — see Gotchas.
Layout injection for the reload client. If the package doesn't auto-inject, add the snippet behind a local-only guard so it never leaks into production HTML:
{{-- resources/views/layouts/app.blade.php --}}
@env('local')
@include('realtime-reload')
@endenv
Docker / devcontainer pattern. Bind-mounted volume events frequently don't propagate into the container. Run the compiler inside the container with polling enabled, and publish the reload/websocket port alongside the HTTP port:
services:
app:
ports: ["8000:8000", "35729:35729"] # verify the actual reload port
environment:
CHOKIDAR_USEPOLLING: "true"
Keep CI honest. Never invoke the compiler in CI or a deploy script. Use it locally, use php hyde build everywhere else. If a contributor's branch only builds under the watcher, that's a bug in the build config, not a reason to run the watcher in CI.
Extension point worth knowing: because Hyde's source directories are declared in config/hyde.php, the cleanest customisation is usually changing what Hyde considers source rather than patching the watcher. If you add a custom collection, register it with Hyde first and the watcher will typically pick it up through the same config.
The rebuild loop. If the output directory (_site) is inside a watched path, every build writes files, which fires the watcher, which builds again. Symptom: CPU pinned, terminal scrolling continuously, browser reloading forever. Fix: explicitly ignore the build output directory. This is the single most common failure mode for any static-site file watcher.
Full rebuilds, not incremental. Hyde is a static site generator — a one-character Markdown edit likely rebuilds the whole site. On a few hundred pages that's tolerable; on a few thousand it's painful. Mitigations: raise the debounce to 500–800ms so a burst of saves coalesces into one build, and narrow the watch list. If you find yourself wanting incremental builds, that's out of scope for this package.
File-watching is platform-dependent. On Linux, inotify watch limits bite with large trees:
cat /proc/sys/fs/inotify/max_user_watches
# raise if needed
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
On WSL2, macOS with Docker Desktop, and network mounts (NFS/SMB), native events are unreliable-to-absent — switch to polling mode. Polling works everywhere but burns CPU; it's a dev-only trade, so accept it.
Stale output from Hyde's cache. If a page refuses to update even though the watcher logs a successful rebuild, the culprit is usually Hyde's own cache, not the compiler. Do a clean build once to confirm:
php hyde build --clean # or delete _site/ and rebuild
If the clean build fixes it, the compiler is fine — you have a cache invalidation issue in your source/config.
Failed builds fail silently in the browser. The reload only fires on a successful build. A Blade syntax error means your browser sits on the last good page and you assume the watcher is broken. Always keep the watcher's terminal visible — that's where the parse error is. Don't run it fully backgrounded in a tab you never look at.
Port collisions. The HTTP port and the reload/websocket port are two separate binds. If php artisan serve or another watcher already holds one, you'll get a half-working setup — page serves, reload never connects, or vice versa. Check both ports in the config and with lsof -i :PORT.
Symlinked content directories aren't always followed. If you symlink _posts from elsewhere (common in monorepos), the watcher may see the symlink but not changes inside the target. Test it explicitly before relying on it.
Pin the version. With a very small dependent count and low adoption, treat the release line as unstable. Commit the lockfile, pin exactly, and be prepared to read the source or vendor a fork if a breaking change lands. Budget an hour to read the whole thing once — it is small enough that this is realistic, and it de-risks the dependency entirely.
Debugging checklist when reload stops working, in order: (1) is the process still alive, (2) does the terminal show a successful build after a save, (3) does the browser console show the reload socket connected, (4) is the file you're editing actually inside a watched path, (5) is the build output being watched. That order resolves the overwhelming majority of issues.
How can I help you explore Laravel packages today?