Skip to content

feat: replace npm live-server with pure-Lua HTTP server#29

Merged
barrettruth merged 23 commits intomainfrom
feat/pure-lua-server
Mar 3, 2026
Merged

feat: replace npm live-server with pure-Lua HTTP server#29
barrettruth merged 23 commits intomainfrom
feat/pure-lua-server

Conversation

@barrettruth
Copy link
Owner

@barrettruth barrettruth commented Feb 23, 2026

Problem

The plugin requires users to install Node.js and the live-server npm
package globally. This is a heavyweight external dependency for what
amounts to a simple local dev-server workflow, and it creates friction
for users who don't otherwise need Node.js.

Solution

Replace the npm shell-out with a pure-Lua HTTP server built on vim.uv
(libuv bindings), eliminating all external dependencies. The new server
supports static file serving, SSE-based live reload, CSS hot-swap
without full page reload, directory listings, and recursive file
watching with configurable debounce.

Minimum Neovim version is bumped to 0.10 for vim.uv and vim.ui.open.
The old args-based config is automatically migrated with a deprecation
warning.

Closes #28.

Problem: the plugin required users to install Node.js and the
live-server npm package globally, adding a heavyweight external
dependency for a simple dev-server workflow.

Solution: implement an HTTP server entirely in Lua using vim.uv (libuv
bindings). The new server handles static file serving, SSE-based live
reload, CSS hot-swap, directory listings, and file watching with
debounce — all with zero external dependencies. Minimum Neovim version
bumped to 0.10. Old args-based config is migrated with a deprecation
warning.
Problem: only --port and --no-browser were migrated from the old args
config. Users with --wait, --ignore, or --no-css-inject would lose
their settings silently. Unsupported flags were also silently dropped.

Solution: migrate --wait to debounce, --ignore to ignore (split on
comma), and --no-css-inject to css_inject. Warn on unsupported flags
(--host, --cors, --spa, etc). Add css_inject config field that
controls whether CSS changes hot-swap or trigger a full reload.
Format all deprecation and warning messages with backticks.
Problem: the pure-Lua server uses vim.uv and vim.ui.open which don't
exist before Neovim 0.10. Loading the plugin on older versions produces
a cryptic Lua traceback.

Solution: check nvim-0.10 at the top of the plugin file and bail with
a clear ErrorMsg directing users to pin v0.1.6 or upgrade. No commands
are registered and no modules are required, preventing any crash.
Problem: hot-reload failures are silent — there's no way to tell which
step in the fs_event → debounce → reload → SSE broadcast chain is
breaking without manually adding print statements.

Solution: add a `debug` boolean to the config (default false). When
enabled, a `dbg` helper logs each critical step via vim.notify at
DEBUG level: fs_event firing, ignore checks, CSS detection, debounce
callback, reload dispatch, SSE broadcast, and client connect/disconnect.
@barrettruth barrettruth mentioned this pull request Feb 23, 2026
Problem: selene's deprecated TOML std format doesn't support the
lua_versions field, so the parser defaults to Lua 5.1 and rejects
\z string escapes used in server.lua HTTP response builders.

Solution: convert vim.toml to vim.yaml with lua_versions: [luajit],
enabling LuaJIT parser mode. Add bad_string_escape = "allow" to
selene.toml to suppress a known false positive where the lint only
whitelists \z for roblox mode, not the actual lua version.
Problem: lua-typecheck-action fails with undefined-global jit (guarded
behind a LuaJIT preprocessor flag in lua-ls), undefined-doc-name for
uv_tcp_t/uv_timer_t/uv_fs_event_t, and cascading undefined-field
errors for close/is_closing. The type annotations used unprefixed
names but LuaCATS/luv defines them as uv.uv_tcp_t etc.

Solution: set runtime.version to LuaJIT in .luarc.json to enable the
jit builtin, and update all type annotations in server.lua to use the
uv.-prefixed class names matching the LuaCATS/luv definitions.
Problem: lua-language-server is not available in the dev shell, making
it impossible to run local type checks.

Solution: add lua-language-server to the devShell packages.
Problem: no way to verify plugin requirements or detect deprecated
config without hitting runtime errors.

Solution: add lua/live-server/health.lua for :checkhealth, reporting
on Neovim version, vim.uv availability, deprecated args config, and
presence of the now-unnecessary npm live-server package.
Problem: the runtime notification about missing recursive file watching
on Linux fires every session, annoying users about something they
cannot change.

Solution: remove the runtime vim.notify and surface the limitation
through :checkhealth instead, where users discover it on their terms.
@barrettruth barrettruth merged commit f42f958 into main Mar 3, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: remove npm pkg deps in favor of libuv

1 participant