Using Plugins
Extend Lunx with official and community plugins. Learn the plugin lifecycle, write custom build actions, and compose multiple plugins safely.
Installing a plugin
Install the plugin package, then add it to the plugins array in lunx.config.ts. Plugin order matters — plugins run in array order during the build.
npm install --save-dev @lunx/plugin-compress @lunx/plugin-pwaimport { defineConfig } from 'lunx' import compress from '@lunx/plugin-compress' import pwa from '@lunx/plugin-pwa' export default defineConfig({ framework: 'react', plugins: [ // Plugins run in order — compress before pwa compress({ algorithm: 'brotli', threshold: 1024 }), pwa({ name: 'My Lunx App', shortName: 'LunxApp', themeColor: '#2563eb', icons: [ { src: '/icons/192.png', sizes: '192x192' }, { src: '/icons/512.png', sizes: '512x512' }, ], }), ], })
Plugin lifecycle hooks
Lunx plugins are plain objects with a name string and optional hook functions. Hooks are called by the Rust compiler orchestrator at the appropriate build stage.
resolveId()Intercepts module path resolution. Return a custom ID to redirect imports to a virtual module or an alternative path.
load()Loads module content from a custom source. Return source code as a string to bypass the file system entirely.
transform()Transform module source code. Receives the source and module ID. Return modified code and optional source map.
generateBundle()Called after all chunks are generated, before files are written. Modify or add output files programmatically.
closeBundle()Called after the bundle has been written. Use for post-build tasks like notifications or upload scripts.
Writing a custom plugin
Custom plugins are declared inline in lunx.config.ts or in a separate file. The TypeScript type LunxPlugin provides full autocomplete.
import { defineConfig, type LunxPlugin } from 'lunx' // Inline plugin — injects a build banner into every JS chunk function bannerPlugin(banner: string): LunxPlugin { return { name: 'banner-plugin', generateBundle(options, bundle) { for (const [fileName, chunk] of Object.entries(bundle)) { if (chunk.type === 'chunk') { chunk.code = `/* ${banner} */\n${chunk.code}` } } } } } // Virtual module plugin — exposes build metadata to the app function buildInfoPlugin(): LunxPlugin { const virtualId = 'virtual:build-info' const resolvedId = '\0' + virtualId return { name: 'build-info', resolveId(id) { if (id === virtualId) return resolvedId }, load(id) { if (id === resolvedId) { return `export const buildTime = '${new Date().toISOString()}'; export const version = '${process.env.npm_package_version}';` } } } } export default defineConfig({ framework: 'react', plugins: [ bannerPlugin('Built with Lunx v1.0.0'), buildInfoPlugin(), ], })
Using the virtual module in your app
// src/components/Footer.tsx import { buildTime, version } from 'virtual:build-info' export function Footer() { return ( <footer className="text-xs text-gray-500 p-4 text-center"> v{version} — built {new Date(buildTime).toLocaleDateString()} </footer> ) }
Transform plugin example
Use the transform hook to modify source code at compile time. The example below strips all console.log calls from production builds.
function stripConsolePlugin(): LunxPlugin { return { name: 'strip-console', transform(code, id) { // Only run in production, skip node_modules if (process.env.NODE_ENV !== 'production') return if (id.includes('node_modules')) return const cleaned = code.replace( /console\.log\([^)]*\);?/g, '/* console.log removed */' ) return { code: cleaned, map: null } } } }
package.json manifest. Undeclared permissions are blocked and logged as a fatal error. Run lunx security plugins to audit installed plugins.