.claude/skills/vite/vite-agents/vite-agents-review/SKILL.md
Use when reviewing Vite configuration, validating a Vite project, checking for common mistakes, or auditing Vite code before deployment. Prevents shipping insecure envPrefix settings, wrong bundler options for the target version, and known anti-patterns. Covers config correctness, plugin hook signatures, HMR patterns, version-specific API usage, build optimization, environment variable security, SSR configuration, and anti-pattern detection. Keywords: review, validation, checklist, audit, anti-pattern, envPrefix, plugin hooks, security, SSR, build optimization.
npx skillsauth add OpenAEC-Foundation/OpenAEC-Workspace-Composer vite-agents-reviewInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Run every check in order. Mark each item PASS or FAIL. A single FAIL in a critical area (Security, Configuration) blocks deployment. Fix all FAILs before approving code.
| # | Check | Expected State | Common Failure |
|---|-------|---------------|----------------|
| C-01 | defineConfig() wraps config export | ALWAYS use defineConfig() for type safety and IDE support | Raw object export loses autocomplete and type checking |
| C-02 | Bundler options match target version | v8: build.rolldownOptions / v6-v7: build.rollupOptions | Using rollupOptions on v8 triggers deprecation; using rolldownOptions on v6 is invalid |
| C-03 | Transform config matches target version | v8: oxc option / v6-v7: esbuild option | Using esbuild on v8 is deprecated; using oxc on v6 is invalid |
| C-04 | envPrefix is NOT empty string | MUST be 'VITE_' or a non-empty custom prefix | Setting envPrefix: '' exposes ALL environment variables to client code |
| C-05 | appType matches usage pattern | 'spa' for single-page, 'mpa' for multi-page, 'custom' for SSR/middleware | Wrong appType causes missing HTML middleware or unwanted SPA fallback |
| C-06 | build.target is appropriate | Use 'baseline-widely-available' (v7+/v8) or explicit browser list | Setting 'esnext' for production breaks older browsers |
| C-07 | loadEnv() used in config, NOT import.meta.env | ALWAYS use loadEnv(mode, root, prefix) inside config files | import.meta.env is undefined during config resolution |
| C-08 | Conditional config uses function form | defineConfig(({ command, mode }) => ({...})) | Forgetting command/mode parameters leads to static config for all modes |
| # | Check | Expected State | Common Failure |
|---|-------|---------------|----------------|
| P-01 | Plugin is a factory function returning object | ALWAYS export a function, NEVER a plain object | Plain objects prevent configuration via options parameter |
| P-02 | Plugin object has name property | ALWAYS include name string on plugin object | Missing name makes errors/warnings untraceable |
| P-03 | config hook returns partial config or mutates | Return object is deep-merged; mutations are applied directly | Returning a full config object overwrites user settings |
| P-04 | configResolved stores config for later hooks | Store resolved config in closure variable | Accessing config parameter outside the hook lifecycle |
| P-05 | configureServer adds middleware correctly | Use server.middlewares.use() for pre-middleware; return function for post-middleware | Adding post-middleware directly instead of returning a function |
| P-06 | transformIndexHtml uses correct order property | order: 'pre' for before HTML processing, order: 'post' (default) for after | Using deprecated enforce property (removed in v7) |
| P-07 | handleHotUpdate returns correct type | Return ModuleNode[] to narrow updates, [] to skip, or void for default | Returning wrong type causes HMR failures |
| P-08 | enforce property used correctly | 'pre' runs before core plugins, 'post' runs after build plugins | Omitting enforce when ordering matters causes transform conflicts |
| P-09 | Virtual modules use correct prefix convention | User-facing: virtual: prefix; internal: \0 prefix on resolved ID | Missing \0 prefix lets other plugins process the virtual module |
| P-10 | moduleParsed hook NOT relied upon in dev | NEVER depend on moduleParsed during development | This hook is skipped in dev for performance — code silently fails |
| P-11 | v8: moduleType: 'js' set in load/transform for non-JS | ALWAYS set moduleType: 'js' when returning non-JS content | Rolldown cannot process content without explicit module type |
| # | Check | Expected State | Common Failure |
|---|-------|---------------|----------------|
| H-01 | import.meta.hot guard wraps ALL HMR code | ALWAYS wrap in if (import.meta.hot) { ... } | HMR code ships to production, increasing bundle size |
| H-02 | accept( has no space before parenthesis | Write import.meta.hot.accept( exactly | Vite static analysis fails with accept ( — HMR silently breaks |
| H-03 | hot.data mutated via properties, NEVER reassigned | import.meta.hot.data.key = value | import.meta.hot.data = { key: value } silently fails |
| H-04 | accept() called BEFORE invalidate() | ALWAYS call accept() first to establish HMR boundary | Calling invalidate() without accept() has no effect |
| H-05 | dispose() used for cleanup of side effects | Register cleanup in import.meta.hot.dispose(cb) | Timers, event listeners, WebSocket connections leak across updates |
| H-06 | prune() used for module removal cleanup | Register in import.meta.hot.prune(cb) when module may be removed | Resources persist after module is no longer imported |
| # | Check | Expected State | Common Failure |
|---|-------|---------------|----------------|
| E-01 | Only VITE_* vars accessed in client code | ALWAYS prefix client-exposed vars with VITE_ | Non-prefixed vars are undefined in import.meta.env |
| E-02 | No secrets in VITE_* variables | NEVER put API keys, tokens, or passwords in VITE_* vars | Client code exposes all VITE_* values in browser source |
| E-03 | TypeScript: ImportMetaEnv interface defined | Declare in src/vite-env.d.ts with /// <reference types="vite/client" /> | Missing types cause import.meta.env.VITE_* to be any |
| E-04 | loadEnv() used in config, NOT import.meta.env | const env = loadEnv(mode, process.cwd(), '') | import.meta.env is not available during config resolution |
| E-05 | .env.local files added to .gitignore | ALWAYS gitignore .env*.local files | Local secrets committed to version control |
| # | Check | Expected State | Common Failure |
|---|-------|---------------|----------------|
| B-01 | build.target matches deployment environment | Set explicit target or use 'baseline-widely-available' | Default 'esnext' (if set) breaks older browsers |
| B-02 | Sourcemaps not accidentally exposed in production | Use 'hidden' or false for production deployments | build.sourcemap: true exposes source code via .map files |
| B-03 | CSS code splitting enabled | Keep build.cssCodeSplit: true (default) | Disabling creates single CSS file, hurting load performance |
| B-04 | Library mode: name set for UMD/IIFE builds | ALWAYS provide build.lib.name when formats include UMD or IIFE | Missing name causes build error for global variable |
| B-05 | Library mode: peer deps externalized | Add framework deps to rolldownOptions.external (v8) or rollupOptions.external (v6-v7) | Bundling React/Vue into library doubles framework in consumer app |
| B-06 | Library mode: package.json exports configured | Set main, module, and exports fields | Consumers cannot resolve the library entry point |
| B-07 | build.minify appropriate for target | v8 default: 'oxc' for client; v6-v7: 'esbuild'; SSR: false | Using Terser without installing it, or minifying SSR output |
| B-08 | Chunk size warnings addressed | Investigate chunks exceeding build.chunkSizeWarningLimit (500 KiB) | Ignoring warnings leads to slow page loads |
| # | Check | Expected State | Common Failure |
|---|-------|---------------|----------------|
| S-01 | Middleware mode sets appType: 'custom' | ALWAYS pair server: { middlewareMode: true } with appType: 'custom' | Default SPA fallback interferes with custom server routing |
| S-02 | ssr.noExternal configured for deps needing transforms | Add deps with non-ESM code or Vite-processed imports to ssr.noExternal | Untransformed CSS imports or JSX crash Node.js at runtime |
| S-03 | ssrFixStacktrace in error handlers | ALWAYS call vite.ssrFixStacktrace(e) in SSR catch blocks | Stack traces point to transformed code instead of source files |
| S-04 | import.meta.env.SSR used for conditional logic | Use import.meta.env.SSR to branch server/client code | Using typeof window checks that fail in edge runtimes |
| S-05 | SSR build uses --ssr flag | Build with vite build --ssr src/entry-server.js | Building SSR entry without --ssr flag bundles as client code |
| S-06 | SSR manifest generated for preload hints | Add --ssrManifest to client build command | Missing preload directives degrade perceived load performance |
| # | Check | Expected State | Common Failure |
|---|-------|---------------|----------------|
| X-01 | envPrefix not set to empty string | NEVER use envPrefix: '' | Exposes ALL env vars (including DB_PASSWORD, SECRET_KEY) to client |
| X-02 | server.fs.strict not disabled without reason | Keep server.fs.strict: true (default) | Disabling allows dev server to serve any file on the machine |
| X-03 | server.allowedHosts configured for non-localhost | Set explicit host list when exposing dev server to network | DNS rebinding attacks can access dev server resources |
| X-04 | No secrets in client-exposed code | NEVER put credentials in VITE_* env vars, define, or client source | All client code is readable in browser DevTools |
| X-05 | Production sourcemaps restricted | Use build.sourcemap: 'hidden' or false | Public .map files expose full source code |
| X-06 | server.fs.deny includes sensitive patterns | Keep defaults: ['.env', '.env.*', '*.{crt,pem}', '**/.git/**'] | Removing deny rules exposes secrets and certificates |
## Review Results — [Project Name] — [Date]
### Version: Vite [6.x / 7.x / 8.x]
| Area | Checks | Pass | Fail | Blocked |
|-----------------|--------|------|------|---------|
| Configuration | 8 | | | |
| Plugins | 11 | | | |
| HMR | 6 | | | |
| Env Variables | 5 | | | |
| Build | 8 | | | |
| SSR | 6 | | | |
| Security | 6 | | | |
| **TOTAL** | **50** | | | |
### Critical Failures (blocks deployment):
- [list any Security or Configuration FAILs]
### Non-Critical Failures (fix before merge):
- [list other FAILs]
### Notes:
- [version-specific observations]
development
Use when integrating Vite with a backend framework, rendering Vite assets from server-side templates, or setting up dev/production HTML serving. Prevents incorrect manifest.json traversal and missing CSS chunk resolution in production. Covers build.manifest configuration, .vite/manifest.json structure, ManifestChunk properties, dev mode HTML setup, production rendering, CSS/JS chunk resolution, and modulepreload polyfill. Keywords: backend integration, manifest.json, ManifestChunk, Django, Laravel, Rails, modulepreload.
development
Use when encountering dev server startup failures, HMR issues, proxy errors, CORS blocks, or module not found errors during development. Prevents misconfiguring server.hmr behind reverse proxies and forgetting appType: 'custom' in middleware mode. Covers HMR full-reload debugging, proxy configuration, CORS setup, HTTPS certificates, server.fs.strict violations, port conflicts, WebSocket failures, file watcher issues, and middleware mode. Keywords: dev server, HMR, proxy, CORS, HTTPS, WebSocket, port conflict, server.fs.strict, middleware mode, file watcher.
development
Use when encountering pre-bundling errors, dependency resolution failures, stale cache issues, or slow development server startup. Prevents excluding CJS dependencies from pre-bundling (which breaks runtime module resolution) and misconfiguring optimizeDeps. Covers CJS/ESM conversion failures, missing dependency auto-discovery, optimizeDeps configuration, monorepo linked dependencies, cache invalidation, browser cache staleness, and large dependency tree performance. Keywords: pre-bundling, optimizeDeps, CJS, ESM, cache, dependency resolution, monorepo, node_modules/.vite.
development
Use when encountering Vite build failures, chunk size warnings, or version-specific build errors. Prevents the common mistake of using deprecated rollupOptions in v8 or misconfiguring build targets and minifiers. Covers Rolldown/Rollup bundling failures, CSS minification errors, sourcemap problems, library mode build failures, BundleError handling, and asset processing errors. Keywords: build error, Rolldown, chunk size, sourcemap, library mode, minify, BundleError, rollupOptions, build.target.