Featured image of post Vite Plugin Development: From Zero to ProductionFeatured image of post Vite Plugin Development: From Zero to Production

Vite Plugin Development: From Zero to Production

Complete guide to developing Vite plugins covering the plugin API, Rollup compatibility, virtual modules, HMR integration, config resolution, and publishing to npm.

Vite’s plugin system is one of its most powerful 기능, allowing developers to extend and 커스터마이즈 the build pipeline. Whether you need to transform file types, inject build-time constants, or integrate with other tools, the Vite plugin API gives you full control.

Understanding Vite’s Plugin System

Vite plugins are objects with hook functions that execute at specific points in the build lifecycle. Plugins operate in three phases: serve (dev 서버), build (production), and SSR. A basic plugin looks like this:

import type { Plugin } from 'vite';

export function myPlugin(): Plugin {
  return {
    name: 'my-plugin',
    config(config) {
      return config;
    },
    transform(code, id) {
      if (id.endsWith('.custom')) {
        return { code: `export default ${JSON.stringify(code)}`, map: null };
      }
    },
  };
}

The name property is required for debugging. enforce: 'pre' or 'post' controls execution order relative to other plugins.


Rollup Compatibility

Vite’s plugin 인터페이스 extends Rollup’s, meaning most Rollup plugins work with Vite out of the box. Shared hooks include resolveId, load, transform, and buildEnd. Vite-specific hooks add dev-서버 capabilities:

HookPurposePhase
configModify Vite config before 해상도Serve + Build
configResolvedRead the final resolved configServe + Build
configureServerExtend the dev 서버Serve only
handleHotUpdate커스터마이즈 HMR behaviorServe only

When porting a Rollup plugin to Vite, ensure it does not use Rollup-specific APIs like this.emitFile incorrectly in the dev 서버 context.


Virtual Modules

Virtual modules exist only at build time, generated by plugin code rather than file system files:

const VIRTUAL_MODULE = 'virtual:config';

export function configPlugin(): Plugin {
  return {
    name: 'config-plugin',
    resolveId(id) {
      if (id === VIRTUAL_MODULE) return '\0' + VIRTUAL_MODULE;
    },
    load(id) {
      if (id === '\0' + VIRTUAL_MODULE) {
        const config = { apiUrl: process.env.API_URL };
        return `export default ${JSON.stringify(config)}`;
      }
    },
  };
}

The \0 prefix is a Rollup convention to mark resolved virtual modules. Use cases include injecting polyfills, providing build-time constants, and generating code from configuration files.


HMR Integration

Hot Module Replacement keeps the development experience fast. The handleHotUpdate hook lets you 커스터마이즈 which modules are invalidated:

handleHotUpdate({ server, modules, timestamp }) {
  if (modules.every(m => m.url.includes('node_modules'))) {
    return [];
  }
  server.ws.send({
    type: 'custom',
    event: 'custom-update',
    data: { timestamp },
  });
  return modules;
}

Return an empty array to suppress HMR, or return a filtered list for granular updates instead of full reloads.


Testing and Publishing

Integration test your plugin with Vite’s programmatic API:

import { build } from 'vite';

const result = await build({
  plugins: [testPlugin({ option: 'value' })],
  logLevel: 'silent',
});

When publishing, follow the vite-plugin-* naming convention, list vite as a peer dependency, and include TypeScript declarations. Test across multiple Vite 버전 in CI to catch breaking changes early.


Vite’s plugin API is well-designed, straightforward, and powerful. Start with simple transformations, explore virtual modules for build-time code generation, and leverage HMR hooks for a polished development experience.