npm.io
0.1.2 • Published 2h ago

@fps-games/vite-plugins

Licence
MIT
Version
0.1.2
Deps
0
Size
290 kB
Vulns
0
Weekly
0

@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 -D

Quick 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.png
stripBabylonPlugin
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:

  1. Injects the inspector-bridge IIFE (~2KB) directly into index.html
  2. The script sets window.__inspectorBridge = { active: true }
  3. bridge.js detects this flag and skips inspector commands
  4. 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: optipng and/or cwebp in PATH
  • For Babylon.js plugins: @babylonjs/core (peer dependency, optional)

License

MIT

Keywords