HMR-serve is inspired by npm run dev
and by serve. It turns any folder into a hot- web app, because sometimes it's nice to preview HTML, Markdown, images and other files in the browser.
Point hmr-serve
at any folder and review it live in the browser: a VSCode-style file tree on the left, a content pane on the right and instant updates as files change on disk, powered by Vite's real dev server and HMR client. π₯
HMR (Hot Module Replacement) is the mechanism that pushes file changes into the browser over a WebSocket so the page updates without a full refresh. This project uses Vite's HMR rather than inventing its own.
Tip:Helpful for reviewing AI agent output (markdown/HTML docs) as it iterates.
npx hmr-serve ./some-folder
Or run it like ** serve** without the nav, which serves a directory listing or
index.html
if present, plus rendered Markdown, CSV/TSV, all with HMR:
npx hmr-serve ./some-folder --no-nav
For a permanent setup, install globally and use the short hmr
command:
npm install -g hmr-serve
hmr ./some-folder
hmr ./some-folder --no-nav
CLI:
hmr [dir] Folder to serve (default: current directory)
--port <port> Port to listen on (default: 5183)
--open Open the browser on start
--no-nav Like serve, without the tree UI
Default (nav) mode shows a VS Code-style file tree and content pane. Supported
types: Markdown (rendered), HTML (sandboxed iframe), code (.css
,
.js
, .ts
, .json
, β¦ syntax-highlighted), text, CSV/TSV (tables), images and video. A gear menu toggles which categories show in the tree, plus an Other files option. Choices are remembered per folder (localStorage).
** --no-nav** skips the tree UI: if the folder has
index.html
(or index.htm
),
that is the root page; otherwise you get a directory listing. Markdown, CSV and
TSV are still rendered to HTML, and edits hot-reload via Vite.No build step for your content. Runs in Vite dev-server mode only. The app shell (a tiny Vue UI) is Vite's root; your folder is mounted alongside it via middleware +server.fs.allow
. Your filesarethe content β nothing is compiled.Live search index. Built at startup withMiniSearch(no native deps) and kept current incrementally off file-watch events β no rebuild step.Plain HTTP + WebSocket, cross-browser. The browser only talks to the Node server over HTTP and Vite's HMR socket. No File System Access API, no Chromium-only APIs β works in Safari and Firefox.Extensible renderer registry..md
βmarkdown-it
;.html
β sandboxed iframe;.css
/.txt
β server-sidehighlight.js
;.csv
/.tsv
β HTML tables styled with GitHub markdown CSS. New types register insrc/server/renderers/
without touching routing.Lightweight CLI via:cac
<path>
,--port
,--open
,--no-nav
.
Live content updates ride Vite's HMR channel: the server pushes a custom
hmr-serve:update
event and the content pane re-fetches β so Vite's client handles transport and reconnection for us.
Links & URLs. Raw HTML is served at mirror paths (/__hmrserve/raw/<file>
),
so relative links and assets inside a framed doc (href="contact.html"
,
<img src="logo.png">
) resolve against the real folder layout. The selected file
is reflected in a clean, bookmarkable URL under the served folder's name β e.g.
serving ./aurora-notes
gives /aurora-notes/docs/intro.md
. These are real
History-API paths (no #
): shareable, reload-safe and back/forward works. Unknown paths 404 rather than rendering the app, so a stray absolute link can't nest the shell inside the content pane.
src/
cli/ arg parsing + entry
server/ Vite plugin, middleware, search index, watcher, renderers
client/ Vue app shell (tree + content pane)
shared/ types shared across server and client
fixtures/ versioned sample files used by tests
tests/ unit Β· component Β· e2e
See CONTRIBUTING.md for local setup and test expectations.