Plugin basics
Add plugins to the plugins array in lunx.config.ts, or keep them in an existing vite.config.*, which lunx reads as it is. They run with Vite's lifecycle in dev and in the build: apply, enforce, config, configResolved, resolveId / load / transform (virtual modules included), transformIndexHtml, configureServer, renderChunk, generateBundle, writeBundle and closeBundle. Plugins that import viteget lunx's stand-in, so Vite does not need to be installed. Framework projects (SvelteKit, Nuxt, Astro, React Router…) run on lunx's Vite-compatible plugin host.
Checked in a browser on every release, in dev and in the build: vite-plugin-pwa, @tailwindcss/vite, unocss, vite-plugin-svgr, vite-tsconfig-paths, unplugin-auto-import, unplugin-icons, vite-plugin-html, vite-plugin-static-copy, rollup-plugin-visualizer, @mdx-js/rollup, vite-plugin-glsl, vite-imagetools, vite-plugin-wasm, vite-plugin-node-polyfills, vite-plugin-checker and vite-plugin-inspect. Not supported: @vitejs/plugin-legacy, whose legacy chunks need a SystemJS build that Rolldown does not write.
import { defineConfig } from 'lunx-dev' export default defineConfig({ plugins: [ { name: 'banner', transform(code, id) { if (id.endsWith('/src/main.ts')) return `console.log('built ${new Date().toISOString()}')\n${code}` }, }, ], })
Built in: no plugin needed
- Framework compilers instead of
@vitejs/plugin-react,@vitejs/plugin-vue,@sveltejs/vite-plugin-svelte,vite-plugin-solidand@preact/preset-vite(an existing config that lists them keeps working). - SVG as a component instead of
vite-plugin-svgr/vite-svg-loader:?react(React, Preact) or?component(React, Preact, Vue). The root<svg>'s attributes are props you can override. .wasm?init, as in Vite:const instance = await init(imports).%ENV%inindex.html, as in Vite:%VITE_APP_TITLE%,%MODE%, anyimport.meta.envvalue.- PWA instead of
vite-plugin-pwa:pwa: { manifest }in lunx.config writes the manifest and a service worker that precaches the build (see PWA). - Type errors in dev instead of
vite-plugin-checker:server: { typecheck: true }runs yourtsc(orvue-tsc) in watch mode and lists errors in the terminal and in the overlay's Type Errors tab. - tsconfig paths instead of
vite-tsconfig-paths. - Electron instead of
vite-plugin-electron(see the Electron page). lunx analyzeinstead ofrollup-plugin-visualizer.
import Logo from './logo.svg?react' import init from './add.wasm?init' const { exports } = await init() export default function App() { return <Logo className="logo" width={32} aria-label={`2 + 3 = ${exports.add(2, 3)}`} /> }
vite-plugin-svgr in your config, ?react imports go to it, with its options.TypeScript types for these (assets, ?raw, ?url, ?react, .wasm?init, CSS modules, import.meta.env, import.meta.hot, import.meta.glob) come with lunx:
/// <reference types="lunx-dev/client" />The TypeScript starters have this file already. Or add "types": ["lunx-dev/client"] to tsconfig.json.
Lunx plugin packages
The lunx repository also holds plugins written for lunx (packages/lunx-plugin-*). They are not published to npm yet.
@lunx/plugin-envSchema-validated environment variables; only LUNX_PUBLIC_ variables reach the browser.
@lunx/plugin-compressExtra compression settings (lunx build already writes .gz and .br copies).
@lunx/plugin-iconsOn-demand icons from Iconify.
@lunx/plugin-imageWebP conversion and responsive srcsets.
@lunx/plugin-mockMock API handlers in development.
@lunx/plugin-inspectModule graph inspector at /__lunx_inspect__.
@lunx/plugin-checkerTypeScript and ESLint diagnostics in the dev server overlay.
@lunx/plugin-legacyA legacy bundle for older browsers.
@lunx/plugin-auto-importAuto-imported APIs and components.
Plugin lifecycle hooks
| Hook | When it runs | Use case |
|---|---|---|
| resolveId() | Module resolution phase | Redirect imports to virtual modules or alternative paths |
| load() | Module loading phase | Load module content from a custom source (virtual modules) |
| transform() | Compilation phase | Modify source code — strip logs, inject metadata, transpile DSLs |
| generateBundle() | After bundling, pre-write | Modify or add output chunks and assets |
| closeBundle() | After files are written | Post-build tasks — upload assets, send notifications |
Plugin sandboxing
Lunx enforces a strict permission sandbox for all plugins. Third-party plugins must declare required permissions in their package.json. Undeclared access is blocked and logged.
{ "name": "my-lunx-plugin", "lunx": { "permissions": [ "fs:read", // read files from the project directory "fs:write:dist", // write files to the output directory only "network:fetch" // make outbound HTTP requests ] } }
# Audit permissions of all installed plugins lunx security plugins 🛡 Plugin audit... vite-plugin-svgr — permissions: none (safe) vite-plugin-checker — permissions: fs:read (expected) some-third-party-plugin — permissions: fs:write:/ (!) BLOCKED → Remove or review: some-third-party-plugin
lunx security plugins after installing any third-party plugin. A plugin with fs:write:/ access could overwrite arbitrary files outside your project directory.