Back to skills
SkillHub ClubShip Full StackFull StackFrontendBackend

vite

Vite is a next-generation frontend build tool that provides a fast dev server with HMR and optimized production builds

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
597
Hot score
99
Updated
March 20, 2026
Overall rating
C4.2
Composite score
4.2
Best-practice grade
C67.6

Install command

npx @skill-hub/cli install onmax-nuxt-skills-vite

Repository

onmax/nuxt-skills

Skill path: skills/vite

Vite is a next-generation frontend build tool that provides a fast dev server with HMR and optimized production builds

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack, Frontend, Backend.

Target audience: everyone.

License: MIT.

Original source

Catalog source: SkillHub Club.

Repository owner: onmax.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install vite into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/onmax/nuxt-skills before adding vite to shared team environments
  • Use vite for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: vite
description: Vite is a next-generation frontend build tool that provides a fast dev server with HMR and optimized production builds
license: MIT
---

# Vite

Next-gen frontend build tool with native ESM dev server and Rolldown-powered builds.

## When to Use

- Setting up frontend project build tooling
- Configuring dev server, HMR, proxies
- Building for production (SPA, MPA, library, SSR)
- Writing Vite plugins
- Integrating with backend frameworks

## Quick Start

```bash
npm create vite@latest my-app
```

```ts
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  server: {
    port: 3000,
  },
})
```

```bash
vite          # Dev server
vite build    # Production build
vite preview  # Preview build
```

## Reference Files

| Task                                    | File                                  |
| --------------------------------------- | ------------------------------------- |
| Config file, options, CLI, plugins      | [config.md](references/config.md)     |
| ESM, CSS, assets, env vars, glob import | [features.md](references/features.md) |
| Dev server, HMR, workers, performance   | [dev.md](references/dev.md)           |
| Production, library mode, SSR, chunking | [build.md](references/build.md)       |
| JS API, plugin authoring, module graph  | [advanced.md](references/advanced.md) |

## Loading Files

**Consider loading these reference files based on your task:**

- [ ] [references/config.md](references/config.md) - if setting up vite.config.ts, configuring plugins, or using CLI
- [ ] [references/features.md](references/features.md) - if using ESM, CSS, assets, env vars, or glob imports
- [ ] [references/dev.md](references/dev.md) - if configuring dev server, HMR, workers, or optimizing performance
- [ ] [references/build.md](references/build.md) - if building for production, library mode, SSR, or chunking
- [ ] [references/advanced.md](references/advanced.md) - if writing plugins, using JS API, or working with module graph

**DO NOT load all files at once.** Load only what's relevant to your current task.

## Cross-Skill References

- **Testing** → Use `vitest` skill (Vite-native testing)
- **Vue projects** → Use `vue` skill for component patterns
- **Library bundling** → Use `tsdown` skill for TypeScript libs


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### references/config.md

```markdown
# Configuration & CLI

## Config File

```ts
// vite.config.ts
import { defineConfig } from 'vite'

export default defineConfig({
  // options
})
```

Conditional config:

```ts
export default defineConfig(({ command, mode }) => {
  if (command === 'serve') {
    return { /* dev config */ }
  }
  return { /* build config */ }
})
```

Async config:

```ts
export default defineConfig(async ({ command }) => {
  const data = await fetchConfig()
  return { define: { CONFIG: data } }
})
```

## Common Options

```ts
defineConfig({
  root: './',                    // Project root
  base: '/',                     // Public base path
  publicDir: 'public',           // Static assets dir
  cacheDir: 'node_modules/.vite',

  // Dependency pre-bundling
  optimizeDeps: {
    include: ['lodash-es'],
    exclude: ['@my/package'],
    force: true,  // Re-bundle on restart
  },

  // Path resolution
  resolve: {
    alias: {
      '@': '/src',
      '~': '/src',
    },
    extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx'],
    mainFields: ['module', 'jsnext:main', 'jsnext', 'main'],
  },

  // Module handling
  json: {
    stringify: true,  // Smaller bundles
  },

  // Log level
  logLevel: 'info',  // 'info', 'warn', 'error', 'silent'
})
```

## Server Options

```ts
defineConfig({
  server: {
    host: '0.0.0.0',
    port: 3000,
    strictPort: true,
    open: true,
    https: true,
    cors: true,

    // Proxy API requests
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ''),
      },
      '/socket.io': {
        target: 'ws://localhost:8080',
        ws: true,
      },
    },

    // File system access
    fs: {
      strict: true,
      allow: ['..'],
      deny: ['.env', '.env.*'],
    },

    // Warmup frequently used files
    warmup: {
      clientFiles: ['./src/main.ts', './src/App.vue'],
      ssrFiles: ['./src/server/entry.ts'],
    },

    // File watching
    watch: {
      ignored: ['**/large-folder/**'],
    },
  },
})
```

## Build Options

```ts
defineConfig({
  build: {
    outDir: 'dist',
    assetsDir: 'assets',
    target: 'es2020',            // 'esnext', 'es2015', etc.
    minify: 'esbuild',           // 'esbuild', 'terser', false
    cssMinify: true,
    sourcemap: true,             // true, 'inline', 'hidden'
    emptyOutDir: true,

    // Chunking
    rolldownOptions: {
      input: {
        main: 'src/main.ts',
        admin: 'src/admin.ts',
      },
      output: {
        manualChunks: {
          vendor: ['vue', 'vue-router'],
        },
      },
    },

    // Inline threshold
    assetsInlineLimit: 4096,

    // Module format
    modulePreload: { polyfill: true },
  },
})
```

## Preview Options

```ts
defineConfig({
  preview: {
    port: 4173,
    host: true,
    strictPort: true,
    open: true,
    proxy: { /* same as server.proxy */ },
  },
})
```

## Plugins

```ts
import vue from '@vitejs/plugin-vue'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    vue(),
    react(),
    // Conditional plugin
    process.env.ANALYZE && analyzePlugin(),
  ],
})
```

Official plugins:

- `@vitejs/plugin-vue` - Vue 3 SFC
- `@vitejs/plugin-vue-jsx` - Vue 3 JSX
- `@vitejs/plugin-react` - React Fast Refresh
- `@vitejs/plugin-legacy` - Legacy browser support

Community plugins: [awesome-vite](https://github.com/vitejs/awesome-vite)

## CLI Commands

```bash
vite                    # Dev server
vite build              # Production build
vite preview            # Preview production build
vite optimize           # Pre-bundle dependencies

# Options
--config <file>         # Config file path
--base <path>           # Public base path
--mode <mode>           # Set mode (development, production)
--port <port>           # Server port
--host                  # Expose to network
--open [path]           # Open browser
--force                 # Force dependency re-bundling
--cors                  # Enable CORS
--strictPort            # Exit if port taken
--debug [feat]          # Debug logs
--profile               # CPU profiling
```

## Package.json Scripts

```json
{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "build:analyze": "vite build --mode analyze"
  }
}
```

```

### references/features.md

```markdown
# Features

## Native ESM Imports

Vite serves source files directly via native ES modules in dev.

```ts
// Bare imports resolved from node_modules
import _ from 'lodash'

// Relative imports
import { util } from './utils'

// Absolute paths from project root
import App from '/src/App.vue'
```

## TypeScript Support

Native TypeScript support - no config needed.

```ts
// vite.config.ts
import { defineConfig } from 'vite'

export default defineConfig({
  esbuild: {
    target: 'esnext',
    jsxFactory: 'h',
    jsxFragment: 'Fragment',
  },
})
```

Type checking is transpile-only. Run `vue-tsc` or `tsc --noEmit` separately.

## CSS

### CSS Modules

```css
/* styles.module.css */
.red { color: red; }
```

```ts
import classes from './styles.module.css'
console.log(classes.red)
```

### Preprocessors

Install the preprocessor:

```bash
npm i -D sass less stylus
```

```vue
<style lang="scss">
$color: red;
.btn { color: $color; }
</style>
```

### PostCSS

Create `postcss.config.js`:

```js
export default {
  plugins: {
    autoprefixer: {},
    'tailwindcss/nesting': {},
    tailwindcss: {},
  },
}
```

### Lightning CSS

```ts
defineConfig({
  css: {
    transformer: 'lightningcss',
    lightningcss: {
      targets: { chrome: 80 },
    },
  },
})
```

## Static Assets

```ts
// URL (dev: raw path, build: hashed)
import imgUrl from './img.png'
img.src = imgUrl

// Raw content
import text from './file.txt?raw'

// As URL (for workers)
import workerUrl from './worker.js?url'

// Inline as base64
import dataUrl from './small.png?inline'
```

### public Directory

Files in `public/` served at root, not processed.

```html
<img src="/logo.png" />
```

## JSON

```ts
// Full import
import pkg from './package.json'

// Named imports (tree-shaken)
import { version } from './package.json'
```

## Glob Import

```ts
// Eager load all
const modules = import.meta.glob('./modules/*.ts', { eager: true })

// Lazy load (dynamic import)
const modules = import.meta.glob('./modules/*.ts')
for (const path in modules) {
  const mod = await modules[path]()
}

// With options
const modules = import.meta.glob('./modules/*.ts', {
  import: 'default',           // Named export
  query: { raw: true },        // Query params
  exhaustive: true,            // Include node_modules
})

// Negative patterns
const modules = import.meta.glob(['./dir/*.ts', '!**/ignored.ts'])

// As strings
const modules = import.meta.glob('./modules/*.ts', {
  query: '?raw',
  import: 'default',
})
```

## Environment Variables

### .env Files

```ini
# .env
VITE_APP_TITLE=My App
VITE_API_URL=http://api.example.com
```

Load order: `.env` < `.env.local` < `.env.[mode]` < `.env.[mode].local`

### Usage

```ts
console.log(import.meta.env.VITE_APP_TITLE)
console.log(import.meta.env.MODE)       // 'development' | 'production'
console.log(import.meta.env.DEV)        // boolean
console.log(import.meta.env.PROD)       // boolean
console.log(import.meta.env.SSR)        // boolean
console.log(import.meta.env.BASE_URL)   // base path
```

### TypeScript Types

```ts
// env.d.ts
/// <reference types="vite/client" />

interface ImportMetaEnv {
  readonly VITE_APP_TITLE: string
  readonly VITE_API_URL: string
}

interface ImportMeta {
  readonly env: ImportMetaEnv
}
```

### Define at Build Time

```ts
defineConfig({
  define: {
    __APP_VERSION__: JSON.stringify('1.0.0'),
    'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
  },
})
```

## HMR API

```ts
if (import.meta.hot) {
  // Accept self
  import.meta.hot.accept()

  // Accept deps
  import.meta.hot.accept('./module.js', (newModule) => {
    console.log('module updated', newModule)
  })

  // Dispose handler
  import.meta.hot.dispose((data) => {
    cleanup()
  })

  // Preserve data across updates
  import.meta.hot.data.count = count

  // Custom events
  import.meta.hot.on('my-event', (data) => {})
  import.meta.hot.send('my-event', data)

  // Invalidate and trigger re-import
  import.meta.hot.invalidate()

  // Decline HMR (full reload)
  import.meta.hot.decline()
}
```

## Web Workers

```ts
// Constructor pattern
const worker = new Worker(new URL('./worker.ts', import.meta.url))

// Import suffix
import MyWorker from './worker?worker'
const worker = new MyWorker()

// Inline (no file)
import InlineWorker from './worker?worker&inline'

// Shared worker
import SharedWorker from './worker?sharedworker'
```

```ts
// worker.ts
self.onmessage = (e) => {
  self.postMessage(e.data * 2)
}
```

```

### references/dev.md

```markdown
# Development

## Dev Server Features

- Native ESM serving (no bundling in dev)
- Instant HMR (Hot Module Replacement)
- TypeScript transpilation
- CSS HMR
- Vue/React/Svelte SFC support

## Dependency Pre-Bundling

Vite pre-bundles dependencies for:

- CommonJS to ESM conversion
- Many-module dependencies (lodash-es)
- Faster cold starts

```ts
defineConfig({
  optimizeDeps: {
    include: [
      'lodash-es',              // Pre-bundle specific deps
      'vue > @vue/runtime-dom', // Nested deps
    ],
    exclude: ['@my/local-pkg'], // Skip pre-bundling
    force: true,                // Re-bundle on restart
    esbuildOptions: {
      target: 'esnext',
    },
  },
})
```

Auto-discovery runs on server start. Use `--force` to re-bundle.

## File System Access

```ts
defineConfig({
  server: {
    fs: {
      strict: true,            // Only allow listed dirs
      allow: [
        '../shared',           // Allow parent directory
        searchForWorkspaceRoot(process.cwd()),  // Monorepo root
      ],
      deny: ['.env', '.env.*', '*.pem'],
    },
  },
})
```

## HMR Configuration

```ts
defineConfig({
  server: {
    hmr: {
      overlay: true,           // Error overlay
      port: 24678,             // WebSocket port
      protocol: 'ws',          // 'ws' or 'wss'
      host: 'localhost',
      clientPort: 443,         // For reverse proxy
    },
  },
})
```

Disable HMR:

```ts
defineConfig({
  server: {
    hmr: false,
  },
})
```

## HMR API Usage

```ts
// Basic accept
if (import.meta.hot) {
  import.meta.hot.accept()
}

// Accept with handler
if (import.meta.hot) {
  import.meta.hot.accept((newModule) => {
    // Handle update
  })
}

// Preserve state
let count = import.meta.hot?.data?.count ?? 0

if (import.meta.hot) {
  import.meta.hot.dispose((data) => {
    data.count = count
  })
  import.meta.hot.accept()
}

// Custom events (plugin communication)
if (import.meta.hot) {
  import.meta.hot.on('custom:event', (payload) => {
    console.log(payload)
  })
}
```

## Web Workers

```ts
// Constructor (recommended)
const worker = new Worker(
  new URL('./worker.ts', import.meta.url),
  { type: 'module' }
)

// Query suffix
import MyWorker from './worker?worker'
const worker = new MyWorker()

// Inline (bundled, no separate file)
import InlineWorker from './worker?worker&inline'
```

Worker file:

```ts
// worker.ts
self.onmessage = (e: MessageEvent) => {
  const result = heavyComputation(e.data)
  self.postMessage(result)
}
```

SharedWorker:

```ts
import SharedWorker from './shared?sharedworker'
const worker = new SharedWorker()
```

## Watch Mode

```ts
defineConfig({
  server: {
    watch: {
      usePolling: true,        // For network drives, containers
      interval: 100,
      ignored: ['**/node_modules/**', '**/.git/**'],
    },
  },
})
```

## HTTPS

```ts
import basicSsl from '@vitejs/plugin-basic-ssl'

defineConfig({
  plugins: [basicSsl()],
  server: {
    https: true,
  },
})
```

Custom certificates:

```ts
import fs from 'node:fs'

defineConfig({
  server: {
    https: {
      key: fs.readFileSync('localhost-key.pem'),
      cert: fs.readFileSync('localhost.pem'),
    },
  },
})
```

## Network Access

```bash
vite --host                    # Expose to network
vite --host 0.0.0.0            # Specific interface
```

```ts
defineConfig({
  server: {
    host: true,                // Same as --host
    port: 3000,
    strictPort: true,          // Fail if port taken
  },
})
```

## Performance Tips

1. **Explicit file extensions** - Reduce resolve operations

   ```ts
   import './Component.tsx'  // Instead of './Component'
   ```

2. **Warm up frequently used files**

   ```ts
   server: {
     warmup: {
       clientFiles: ['./src/main.ts', './src/App.vue'],
     },
   }
   ```

3. **Avoid barrel files** - Direct imports are faster

   ```ts
   import { util } from './utils/util.ts'  // Not './utils'
   ```

4. **Limit watched files**

   ```ts
   server: {
     watch: {
       ignored: ['**/large-folder/**'],
     },
   }
   ```

5. **Disable unused features**
   ```ts
   css: { devSourcemap: false }
   ```

## Profiling

```bash
vite --debug                   # Debug logs
vite --debug plugin-transform  # Transform times
vite --profile                 # CPU profile
```

Use [vite-plugin-inspect](https://github.com/antfu/vite-plugin-inspect) for plugin debugging.

```

### references/build.md

```markdown
# Build

## Production Build

```bash
vite build
```

```ts
defineConfig({
  build: {
    outDir: 'dist',
    assetsDir: 'assets',
    target: 'modules',              // ESM browsers
    minify: 'esbuild',              // or 'terser'
    cssMinify: true,
    sourcemap: false,               // true, 'inline', 'hidden'
    emptyOutDir: true,
    reportCompressedSize: true,     // false for faster builds

    // Chunk size warnings
    chunkSizeWarningLimit: 500,
  },
})
```

## Multi-Page App

```ts
defineConfig({
  build: {
    rolldownOptions: {
      input: {
        main: 'index.html',
        nested: 'nested/index.html',
      },
    },
  },
})
```

## Library Mode

```ts
import { resolve } from 'node:path'
import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    lib: {
      entry: resolve(__dirname, 'src/index.ts'),
      name: 'MyLib',
      fileName: 'my-lib',          // → my-lib.es.js, my-lib.umd.js
      formats: ['es', 'umd'],
    },
    rolldownOptions: {
      external: ['vue', 'react'],  // Don't bundle
      output: {
        globals: {
          vue: 'Vue',
          react: 'React',
        },
      },
    },
  },
})
```

### package.json for Library

```json
{
  "name": "my-lib",
  "type": "module",
  "main": "./dist/my-lib.umd.cjs",
  "module": "./dist/my-lib.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/my-lib.js",
      "require": "./dist/my-lib.umd.cjs"
    }
  },
  "files": ["dist"]
}
```

## SSR Build

```ts
defineConfig({
  build: {
    ssr: true,                      // or 'src/entry-server.ts'
    ssrEmitAssets: true,
  },
})
```

### SSR Entry

```ts
// src/entry-server.ts
import { renderToString } from 'vue/server-renderer'
import { createApp } from './main'

export async function render(url: string) {
  const { app, router } = createApp()
  await router.push(url)
  await router.isReady()
  const html = await renderToString(app)
  return { html }
}
```

### Server Integration

```ts
import express from 'express'
import { createServer as createViteServer } from 'vite'

const app = express()

const vite = await createViteServer({
  server: { middlewareMode: true },
  appType: 'custom',
})

app.use(vite.middlewares)

app.use('*', async (req, res) => {
  // 1. Read index.html
  let template = fs.readFileSync('index.html', 'utf-8')

  // 2. Apply Vite transforms
  template = await vite.transformIndexHtml(req.url, template)

  // 3. Load SSR entry
  const { render } = await vite.ssrLoadModule('/src/entry-server.ts')

  // 4. Render
  const { html } = await render(req.url)

  // 5. Inject
  const page = template.replace('<!--ssr-outlet-->', html)
  res.send(page)
})
```

### SSR Externals

```ts
defineConfig({
  ssr: {
    external: ['lodash'],           // Don't bundle
    noExternal: ['@my/ui-lib'],     // Bundle despite node_modules
  },
})
```

## Backend Integration

For traditional backends (Rails, Laravel, etc.):

```ts
defineConfig({
  server: {
    cors: { origin: 'http://my-backend.example.com' },
  },
  build: {
    manifest: true,                 // Generate manifest.json
    rolldownOptions: {
      input: '/path/to/main.js',
    },
  },
})
```

Manifest output (`.vite/manifest.json`):

```json
{
  "views/foo.js": {
    "file": "assets/foo-abc123.js",
    "src": "views/foo.js",
    "isEntry": true,
    "imports": ["_shared-def456.js"],
    "css": ["assets/foo-789abc.css"]
  }
}
```

Use manifest to generate correct `<script>` and `<link>` tags in backend templates.

## Chunking Strategies

```ts
defineConfig({
  build: {
    rolldownOptions: {
      output: {
        manualChunks: {
          vendor: ['vue', 'vue-router', 'pinia'],
          ui: ['@ui/components'],
        },

        // Or function for dynamic chunking
        manualChunks(id) {
          if (id.includes('node_modules')) {
            return 'vendor'
          }
        },
      },
    },
  },
})
```

## Asset Handling

```ts
defineConfig({
  build: {
    assetsInlineLimit: 4096,        // Inline < 4KB
    rolldownOptions: {
      output: {
        assetFileNames: 'assets/[name]-[hash][extname]',
        chunkFileNames: 'chunks/[name]-[hash].js',
        entryFileNames: 'entries/[name]-[hash].js',
      },
    },
  },
})
```

## Legacy Browser Support

```bash
npm i -D @vitejs/plugin-legacy
```

```ts
import legacy from '@vitejs/plugin-legacy'

defineConfig({
  plugins: [
    legacy({
      targets: ['defaults', 'not IE 11'],
    }),
  ],
})
```

```

### references/advanced.md

```markdown
# Advanced

## JavaScript API

```ts
import { createServer, build, preview, resolveConfig } from 'vite'

// Dev server
const server = await createServer({
  configFile: false,
  root: __dirname,
  server: { port: 3000 },
})
await server.listen()
server.printUrls()

// Build
await build({
  root: './project',
  build: { outDir: 'dist' },
})

// Preview
const previewServer = await preview({
  preview: { port: 4173, open: true },
})

// Resolve config
const config = await resolveConfig({}, 'serve', 'development')
```

### ViteDevServer API

```ts
interface ViteDevServer {
  config: ResolvedConfig
  middlewares: Connect.Server      // Express-compatible
  httpServer: http.Server | null
  watcher: FSWatcher               // Chokidar
  ws: WebSocketServer              // HMR WebSocket

  transformRequest(url: string): Promise<TransformResult | null>
  transformIndexHtml(url: string, html: string): Promise<string>
  ssrLoadModule(url: string): Promise<Record<string, any>>
  ssrFixStacktrace(e: Error): void

  listen(port?: number): Promise<ViteDevServer>
  restart(): Promise<void>
  close(): Promise<void>
}
```

### Utility Functions

```ts
import { mergeConfig, loadEnv, normalizePath, searchForWorkspaceRoot } from 'vite'

// Merge configs
const merged = mergeConfig(baseConfig, overrideConfig)

// Load .env files
const env = loadEnv('development', process.cwd(), 'VITE_')

// Normalize Windows paths
normalizePath('foo\\bar')  // 'foo/bar'

// Find workspace root
const root = searchForWorkspaceRoot(process.cwd())
```

## Plugin API

```ts
export default function myPlugin(): Plugin {
  return {
    name: 'my-plugin',

    // Vite hooks
    config(config, { command, mode }) {
      return { /* merge into config */ }
    },
    configResolved(config) {
      // Store resolved config
    },
    configureServer(server) {
      // Add middleware
      server.middlewares.use((req, res, next) => {
        next()
      })
    },
    transformIndexHtml(html) {
      return html.replace('<title>', '<title>Modified ')
    },
    handleHotUpdate({ file, server, modules }) {
      // Custom HMR
    },

    // Rolldown hooks
    buildStart() {},
    resolveId(id, importer) {},
    load(id) {},
    transform(code, id) {},
    buildEnd() {},
    closeBundle() {},
  }
}
```

### Virtual Modules

```ts
export default function virtualPlugin(): Plugin {
  const virtualId = 'virtual:my-module'
  const resolvedId = '\0' + virtualId

  return {
    name: 'virtual-module',
    resolveId(id) {
      if (id === virtualId) return resolvedId
    },
    load(id) {
      if (id === resolvedId) {
        return `export const data = ${JSON.stringify(buildTimeData)}`
      }
    },
  }
}
```

Usage:

```ts
import { data } from 'virtual:my-module'
```

### HTML Injection

```ts
export default function injectPlugin(): Plugin {
  return {
    name: 'inject-tags',
    transformIndexHtml() {
      return {
        tags: [
          {
            tag: 'script',
            attrs: { src: '/inject.js' },
            injectTo: 'body',  // 'head' | 'body' | 'head-prepend' | 'body-prepend'
          },
        ],
      }
    },
  }
}
```

### Client-Server Communication

```ts
// Plugin (server)
configureServer(server) {
  server.ws.on('my:event', (data, client) => {
    client.send('my:reply', { received: true })
  })
}

// Client
if (import.meta.hot) {
  import.meta.hot.send('my:event', { message: 'hello' })
  import.meta.hot.on('my:reply', (data) => {
    console.log(data)
  })
}
```

### Transform Filtering

```ts
{
  transform: {
    filter: { id: /\.vue$/ },
    handler(code, id) {
      return transformVue(code)
    },
  },
}
```

## Plugin Ordering

```ts
export default function myPlugin(): Plugin {
  return {
    name: 'my-plugin',
    enforce: 'pre',   // Run before core plugins
    // enforce: 'post' - Run after core plugins
    // No enforce - Run with user plugins
  }
}
```

## Conditional Plugins

```ts
export default function myPlugin(): Plugin {
  return {
    name: 'my-plugin',
    apply: 'serve',   // Only in dev
    // apply: 'build' - Only in production
    // apply: (config, { command }) => command === 'serve'
  }
}
```

## Performance Optimization

### Browser Setup

- Use dev-only browser profile
- Disable DevTools "Disable Cache"
- Disable browser extensions

### Plugin Performance

1. Lazy load heavy dependencies
2. Avoid long operations in `buildStart`, `config`, `configResolved`
3. Check file extension before processing in `transform`

### Build Performance

```ts
defineConfig({
  build: {
    reportCompressedSize: false,  // Skip gzip calculation
    sourcemap: false,
  },
})
```

### Debug

```bash
vite --debug plugin-transform    # Transform timing
vite --profile                   # CPU profile → speedscope
```

## Named Plugin Conventions

- `vite-plugin-*` - Vite-specific
- `rollup-plugin-*` - Rollup-compatible
- `vite-plugin-vue-*` - Vue-specific
- `vite-plugin-react-*` - React-specific

## Module Graph

Access module dependency graph:

```ts
configureServer(server) {
  server.moduleGraph.getModuleById(id)
  server.moduleGraph.getModulesByFile(file)
  server.moduleGraph.invalidateModule(mod)
}
```

```