@fps-games/vite-plugins
v0.1.2
Published
Vite plugin collection for playable ad / HTML5 game builds — asset versioning, GLB compression, PNG optimization, locale fallback, dependency allowlist, Babylon.js tree-shaking & inspector.
Readme
@fps-games/vite-plugins
Vite plugin collection for playable ad / HTML5 game builds.
Covers the full build pipeline: dev-time asset caching, locale fallback, binary compression, image optimization, dependency guardrails, and Babylon.js-specific tree-shaking.
Install
npm install @fps-games/vite-plugins -DQuick Start
// vite.config.ts
import { defineConfig } from 'vite';
import {
modelCachePlugin,
binaryCompressPlugin,
imageOptimizePlugin,
depWhitelistPlugin,
localeAssetsPlugin,
scriptInjectPlugin,
inspectorBridgePlugin,
} from '@fps-games/vite-plugins';
// Babylon.js plugins (optional)
import {
stripBabylonPlugin,
babylonInspectorPlugin,
} from '@fps-games/vite-plugins/babylon';
export default defineConfig({
plugins: [
// Dev plugins
modelCachePlugin({
extensions: ['.glb', '.png', '.jpg'],
roots: ['src/assets', 'src/shared'],
}),
localeAssetsPlugin({ locale: 'zh', htmlLang: 'zh-CN' }),
babylonInspectorPlugin(),
// Inspector Bridge — 独立的 inspector 命令监听器(与 bridge.js 解耦)
inspectorBridgePlugin(),
scriptInjectPlugin({
src: 'http://localhost:8080/script/bridge.js',
delay: 2000,
}),
// Build plugins
binaryCompressPlugin({ extensions: ['.glb'] }),
imageOptimizePlugin({ optipngLevel: 2, webpQuality: 80 }),
stripBabylonPlugin(),
depWhitelistPlugin({
allowedPackages: ['@babylonjs/core', '@babylonjs/loaders'],
}),
],
});Plugins
Universal (any Vite project)
| Plugin | Mode | Description |
|--------|------|-------------|
| modelCachePlugin | dev | Appends ?v=<mtime> to new URL() asset imports for cache-busting. Serves versioned assets with immutable cache headers. |
| localeAssetsPlugin | both | Resolves @locale-img/file.png to locale-specific directory with fallback. Injects lang and dir attributes on <html>. |
| binaryCompressPlugin | build | Brotli-compresses binary assets (GLB, etc.) and inlines as base64 data URL. ~16% smaller than gzip. |
| imageOptimizePlugin | build | Post-build PNG optimization via optipng (lossless) and cwebp (lossy). Picks whichever is smallest. |
| depWhitelistPlugin | build | Throws build error if project code imports an npm package not in the allowlist. Prevents accidental bundle bloat. |
| scriptInjectPlugin | dev | Injects a <script> tag into index.html with configurable URL, delay, async, and error handling. |
| inspectorBridgePlugin | dev | Inlines the inspector-bridge IIFE (~2KB) into index.html. Decouples inspector commands (mode.change, asset.import, etc.) from bridge.js. |
Babylon.js (@fps-games/vite-plugins/babylon)
| Plugin | Mode | Description |
|--------|------|-------------|
| stripBabylonPlugin | build | Tree-shakes unused Babylon.js modules: WGSL shaders, Audio, GUI, XR, Physics, OpenPBR, unused texture loaders, KHR_interactivity. |
| babylonInspectorPlugin | dev | Lazy-loads @babylonjs/inspector via globalThis.ensureInspectorReady(). Auto-preloads on ?edit, ?inspector, etc. |
Plugin Details
modelCachePlugin
modelCachePlugin({
extensions: ['.glb', '.png', '.jpg', '.webp'], // file types to version
roots: ['src/assets'], // directories to watch
cacheMaxAgeSeconds: 31536000, // 1 year (default)
})Intercepts new URL('model.glb', import.meta.url) patterns in TypeScript files and appends ?v=<mtime>. The dev server responds with Cache-Control: public, max-age=31536000, immutable when the version param is present, eliminating redundant network requests during development.
binaryCompressPlugin
binaryCompressPlugin({
extensions: ['.glb'], // file types to compress
mime: 'application/x-brotli', // output MIME type
minSize: 10 * 1024, // skip files < 10KB
enabled: true,
verbose: true,
})Uses Brotli quality 11 for maximum compression. Output is a base64 data URL that the runtime decompresses (e.g., via brotli-dec-wasm). Prints a summary table at build end.
localeAssetsPlugin
localeAssetsPlugin({
locale: 'ar', // BCP 47 code
htmlLang: 'ar', // <html lang="ar">
isRTL: true, // adds dir="rtl"
imgRoot: 'src/assets/ui', // base directory
alias: '@locale-img', // virtual import prefix (default)
})Usage in code:
import logo from '@locale-img/logo.png?url';
// Resolves to src/assets/ui/ar/logo.png if it exists, otherwise src/assets/ui/logo.pngstripBabylonPlugin
stripBabylonPlugin({
exclude: ['/Audio/', '/GUI/', '/XR/', '/Physics/', ...], // defaults provided
stripWGSL: true, // remove WebGPU shaders
stripOpenPBR: true, // remove OpenPBR glTF extension
stripTextureLoaders: true, // remove DDS/Basis/KTX/TGA/EXR/HDR/IES loaders
stripInteractivity: true, // remove KHR_interactivity extension
})Typical bundle size reduction: 30-50% of Babylon.js code removed.
depWhitelistPlugin
depWhitelistPlugin({
allowedPackages: [
'@babylonjs/core',
'@babylonjs/loaders',
'@babylonjs/inspector',
],
projectRoot: process.cwd(), // optional, defaults to Vite config root
})Any import of a package not in the list will throw a descriptive build error showing the source, importer, and current allowlist.
imageOptimizePlugin
imageOptimizePlugin({
optipngLevel: 2, // 0-7, higher = slower but smaller
webpQuality: 80, // 0-100
skipTransparentToWebp: true, // keep PNG for images with alpha
scanDirs: ['src'], // for content-hash → filename mapping in logs
})Requires optipng and/or cwebp CLI tools installed. On macOS: brew install optipng webp.
scriptInjectPlugin
scriptInjectPlugin({
src: 'http://localhost:8080/script/bridge.js',
delay: 2000, // ms before injection
enabled: true,
async: true,
errorMessage: '[Bridge] Not available',
// Optional URL rewrite for cloud environments:
urlRewrite: `
var e2b = location.href.match(/\\d+-([a-z0-9]+)\\.e2b\\.app/);
return e2b ? 'https://8080-' + e2b[1] + '.e2b.app/script/bridge.js' : defaultSrc;
`,
})babylonInspectorPlugin
babylonInspectorPlugin({
preloadParams: ['edit', 'inspector', 'debugGame', 'mcp'], // URL params that trigger preload
initScript: '/path/to/custom-init.ts', // optional custom init script
})Exposes globalThis.ensureInspectorReady() which returns a Promise that resolves to the inspector module. Also preloads EffectLayer and DepthRenderer scene components for selection outline support.
inspectorBridgePlugin
inspectorBridgePlugin({
enabled: true, // default
apply: 'serve', // 'serve' | 'build' — default 'serve'
delay: 0, // ms before injection — default 0 (inject immediately)
})Purpose: Decouple inspector commands (mode.change, asset.import, history.undo, etc.) from bridge.js.
How it works:
- Injects the inspector-bridge IIFE (~2KB) directly into
index.html - The script sets
window.__inspectorBridge = { active: true } bridge.jsdetects this flag and skips inspector commands- Inspector commands are handled independently without duplication
Recommended setup:
plugins: [
inspectorBridgePlugin(), // inject first (no delay)
scriptInjectPlugin({
src: 'http://localhost:8080/script/bridge.js',
delay: 2000, // bridge.js loads after
}),
]Why separate?
bridge.js= MCP tools (screenshot, debug, CDP)inspector-bridge= Editor commands (edit mode, asset import, undo/redo)- Both scripts run independently; no coupling required
Requirements
- Node.js >= 18
- Vite >= 5.0
- For
imageOptimizePlugin:optipngand/orcwebpin PATH - For Babylon.js plugins:
@babylonjs/core(peer dependency, optional)
License
MIT
